Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion docs/e2e.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,12 @@ JDK, so `java` can only come from what vfox installs:
registered from this checkout. On Windows, the runner builds `main` once,
while the `latest` combinations run, and the `main` containers start from
the e2e image plus that `vfox.exe`.
2. A release no distribution has published (`<major>.0.99`) fails to install,
2. A distribution without a version, alone or after an empty one
(`java@tem`, `java@-tem`), fails both `vfox install` and `vfox use` with
an error asking for one. The hooks stopped with a Lua error on the first,
while `PreInstall` sent the empty version to Foojay, which lists every
release, and installed the newest one.
A release no distribution has published (`<major>.0.99`) fails to install,
with `PreInstall` naming the platform it searched (`linux/amd64`,
`windows/arm64`, ...), so the Foojay lookup is checked to target the
runner. So does a build of the pinned release that was never published
Expand Down
4 changes: 4 additions & 0 deletions hooks/parse_legacy_file.lua
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,11 @@ function PLUGIN:ParseLegacyFile(ctx)
if not distributionVersion then
return {}
end
--- A distribution alone (java=tem) selects nothing, as vfox use rejects it.
local fullVersion = distributionVersion.version
if not fullVersion then
return {}
end
local vendor = distributionVersion.distribution.short_name
if not vendor then
return {}
Expand Down
3 changes: 3 additions & 0 deletions hooks/pre_install.lua
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ function PLUGIN:PreInstall(ctx)
if not distribution_version then
error("Could not extract a valid distribution: " .. ctx.version)
end
if not distribution_version.version then
error(distribution_version_parser.missing_version_error(ctx.version, distribution_version))
end
if loongnix.supported() or loongnix.is_distribution(distribution_version.distribution.name) then
return loongnix.pre_install(distribution_version)
end
Expand Down
3 changes: 3 additions & 0 deletions hooks/pre_use.lua
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ function PLUGIN:PreUse(ctx)
end

if distribution_version then
if not distribution_version.version then
error(distribution_version_parser.missing_version_error(input_version, distribution_version))
end
-- OpenJDK versions are installed without a distribution suffix, so
-- 25.0.2+10-open and openjdk-25.0.2+10 both name 25.0.2+10.
local suffix = distribution_version.javafx_bundled and "-fx" or ""
Expand Down
28 changes: 26 additions & 2 deletions lib/distribution_version.lua
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,15 @@ function distribution_version.parse_version(arg)
end
end

-- Convert version format for Foojay API
version = distribution_version.convert_version_for_api(version)
-- A distribution alone (tem, zulu-fx), or after an empty version (-tem,
-- temurin-), names no version: leave it nil for the hooks to reject.
if version == "" then
version = nil
end
if version then
-- Convert version format for Foojay API
version = distribution_version.convert_version_for_api(version)
end

return {
version = version,
Expand All @@ -140,4 +147,21 @@ function distribution_version.parse_version(arg)
}
end

--- Returns the error for a user-input version that parse_version read
--- without a version, such as tem: it names no JDK to install or use.
--- @param arg string User-input version
--- @param parsed table What parse_version returned for arg
--- @return string Error message
function distribution_version.missing_version_error(arg, parsed)
local short_name = parsed.distribution.short_name
return "No version in "
.. arg
.. "; add one, such as 21-"
.. short_name
.. (parsed.javafx_bundled and "-fx" or "")
.. ". Please check available versions with 'vfox search java "
.. short_name
.. "'"
end

return distribution_version
10 changes: 10 additions & 0 deletions tests/e2e/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,16 @@ func MissingBuildSpec(distribution, version string) string {
return Spec(distribution, version+"+99")
}

// VersionlessSpecs renders the distribution without a version, alone and
// after an empty one ("tem" and "-tem", "librca-fx" and "-librca-fx", "open"
// and "-open" for OpenJDK). PreInstall and PreUse must reject both, asking
// for a version: they failed with a Lua error on the first (vfox-java#98),
// while PreInstall sent the empty version to Foojay, which lists every
// release, and installed the newest one.
func VersionlessSpecs(distribution string) []string {
return []string{distribution, "-" + distribution}
}

// Decoy renders the installed name of another release whose version starts
// with the pinned one (<version>0+1, e.g. 21.0.80+1-tem). vfox lists the
// installed versions sorted as strings, descending, so it comes before the
Expand Down
15 changes: 15 additions & 0 deletions tests/e2e/common/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,21 @@ func TestSpecs(t *testing.T) {
}
}

func TestVersionlessSpecs(t *testing.T) {
for _, c := range []struct {
dist string
want []string
}{
{"open", []string{"open", "-open"}},
{"tem", []string{"tem", "-tem"}},
{"librca-fx", []string{"librca-fx", "-librca-fx"}},
} {
if got := VersionlessSpecs(c.dist); !slices.Equal(got, c.want) {
t.Errorf("VersionlessSpecs(%s) = %q, want %q", c.dist, got, c.want)
}
}
}

func TestPaddedIdentifier(t *testing.T) {
for _, c := range []struct{ dist, version, installed, want string }{
{"open", "25.0.2", "25.0.2+10", "25.0.2.0+10-open"},
Expand Down
1 change: 1 addition & 0 deletions tests/e2e/linux/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ func runOne(t *testing.T, ctx context.Context, image, platform, arch string, c c
t.Logf("=== vfox %s, java@%s, %s (%s) ===", c.Vfox, common.Spec(c.Distribution, version), platform, machine)

setupVfox(ctx, t, ctr, c.Vfox)
checkVersionlessRejected(ctx, t, ctr, c.Distribution)
checkMissingVersionRejected(ctx, t, ctr, c.Distribution, version, arch)

installed := installJava(ctx, t, ctr, common.Spec(c.Distribution, version))
Expand Down
20 changes: 20 additions & 0 deletions tests/e2e/linux/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,26 @@ func setupVfox(ctx context.Context, t *testing.T, ctr tc.Container, vfoxVersion
t.Logf("PASS %s with the local java plugin", strings.TrimSpace(execOK(ctx, t, ctr, "vfox --version")))
}

// checkVersionlessRejected proves a distribution without a version (e.g.
// java@tem, java@-tem, see common.VersionlessSpecs) fails both `vfox
// install` and `vfox use` with an error asking for one. The hooks raise it
// before any Foojay lookup, so nothing is retried.
func checkVersionlessRejected(ctx context.Context, t *testing.T, ctr tc.Container, distribution string) {
t.Helper()
for _, spec := range common.VersionlessSpecs(distribution) {
want := "No version in " + spec + ";"
for _, c := range []struct{ label, script string }{
{"vfox install java@" + spec, "vfox install java@" + spec},
{"vfox use --global java@" + spec, activated("vfox use --global java@" + spec)},
} {
code, out := execRun(ctx, t, ctr, c.script)
require.NotZerof(t, code, "%s must fail\n--- output ---\n%s", c.label, out)
require.Containsf(t, common.StripANSI(out), want, "%s must ask for a version", c.label)
}
t.Logf("PASS java@%s is rejected by vfox install and vfox use", spec)
}
}

// checkMissingVersionRejected proves an unpublished release, and an
// unpublished build of the pinned one, fail loudly instead of installing
// something else: PreInstall finds no Foojay package and names the platform
Expand Down
3 changes: 2 additions & 1 deletion tests/e2e/windows/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,9 +242,10 @@ func runOne(t *testing.T, ctx context.Context, image, platform, arch string, c c

box := setupSlotEnv(ctx, t, ctr, c.Slug())
setupVfox(ctx, t, ctr, box, c.Vfox)
activation := fetchActivation(ctx, t, ctr, box)
checkVersionlessRejected(ctx, t, ctr, box, activation, c.Distribution)
checkMissingVersionRejected(ctx, t, ctr, box, c.Distribution, version, arch)

activation := fetchActivation(ctx, t, ctr, box)
installed := installJava(ctx, t, ctr, box, common.Spec(c.Distribution, version))
verifySearch(ctx, t, ctr, box, c.Distribution, installed)
// Before any `vfox use`, so java can only come from the legacy file.
Expand Down
21 changes: 21 additions & 0 deletions tests/e2e/windows/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,27 @@ func activatedEnv(activation, script string) string {
"if ($__vfoxExport.Trim()) { Invoke-Expression -Command $__vfoxExport }\r\n" + script
}

// checkVersionlessRejected proves a distribution without a version (e.g.
// java@tem, java@-tem, see common.VersionlessSpecs) fails both `vfox
// install` and `vfox use` with an error asking for one. The hooks raise it
// before any Foojay lookup, so nothing is retried.
func checkVersionlessRejected(ctx context.Context, t *testing.T, ctr tc.Container, box slotBox, activation, distribution string) {
t.Helper()
for _, spec := range common.VersionlessSpecs(distribution) {
want := "No version in " + spec + ";"
arg := pwshQuote("java@" + spec)
for _, c := range []struct{ label, script string }{
{"vfox install java@" + spec, "vfox install " + arg},
{"vfox use --global java@" + spec, activated(activation, "vfox use --global "+arg)},
} {
code, out := execRun(ctx, t, ctr, c.script, box.vars...)
require.NotZerof(t, code, "%s must fail\n--- output ---\n%s", c.label, out)
require.Containsf(t, common.StripANSI(out), want, "%s must ask for a version", c.label)
}
t.Logf("PASS java@%s is rejected by vfox install and vfox use", spec)
}
}

// checkMissingVersionRejected proves an unpublished release, and an
// unpublished build of the pinned one, fail loudly instead of installing
// something else: PreInstall finds no Foojay package (zip or tar.gz) and
Expand Down
3 changes: 3 additions & 0 deletions tests/loongnix_test.lua
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ end, "No Loongnix JDK")
rejects(function()
PLUGIN:PreInstall({ version = "21" })
end, "ABI")
rejects(function()
PLUGIN:PreInstall({ version = "lnxabi2-fx" })
end, "No version in lnxabi2-fx; add one, such as 21-lnxabi2-fx.")
rejects(function()
PLUGIN:Available({ args = { "tem" } })
end, "Foojay")
Expand Down
7 changes: 7 additions & 0 deletions tests/parse_legacy_file_test.lua
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,13 @@ local cases = {
{ "java=28-ea+19-zulu-fx\n", { "28-ea+19-zulu", "28-ea+19-zulu-fx" }, "28-ea+19-zulu-fx" },
{ "java=26-ea+13-graal\n", { "26-ea+2-graal", "26-ea+13-graal" }, "26-ea+13-graal" },
{ "java=28-ea+20\n", { "28-ea+18", "28-ea+19" }, nil },
-- A distribution without a version selects nothing, as vfox use rejects it
-- (vfox-java#98), where parse_version failed with a Lua error.
{ "java=tem\n", { "21.0.8+9-tem", "21.0.12+8-tem" }, nil },
{ "java=temurin-\n", { "21.0.8+9-tem" }, nil },
{ "java=-tem\n", { "21.0.8+9-tem" }, nil },
{ "java=zulu-fx\n", { "21.0.12+7-zulu-fx" }, nil },
{ "java=open\n", { "25.0.2+10" }, nil },
-- No java line at all selects nothing.
{ "maven=3.9.9\n", { "21.0.8+9-tem" }, nil },
}
Expand Down
24 changes: 24 additions & 0 deletions tests/pre_install_test.lua
Original file line number Diff line number Diff line change
Expand Up @@ -204,4 +204,28 @@ assert(
)
RUNTIME.osType = "linux"

-- A distribution without a version, alone or after an empty one, asks for a
-- version before any lookup (vfox-java#98). Alone it failed with a Lua error;
-- the empty version reached Foojay, which listed every release, and the newest
-- one installed. Once pcall has caught an error, gopher-lua's http stub keeps
-- the requests table it saw, so it is emptied in place.
for _, case in ipairs({
{
"tem",
"No version in tem; add one, such as 21-tem. Please check available versions with 'vfox search java tem'",
},
{ "temurin", "No version in temurin; add one, such as 21-tem." },
{ "-tem", "No version in -tem; add one, such as 21-tem." },
{ "temurin-", "No version in temurin-; add one, such as 21-tem." },
{ "zulu-fx", "such as 21-zulu-fx. Please check available versions with 'vfox search java zulu'" },
{ "openjdk", "such as 21-open. Please check available versions with 'vfox search java open'" },
}) do
for i = #requests, 1, -1 do
requests[i] = nil
end
local ok, err = pcall(PLUGIN.PreInstall, PLUGIN, { version = case[1] })
assert(not ok and tostring(err):find(case[2], 1, true), case[1] .. ": unexpected " .. tostring(err))
assert(#requests == 0, case[1] .. ": unexpected lookup " .. tostring(requests[1]))
end

print("PASS: " .. cases .. " PreInstall Foojay lookup cases")
12 changes: 11 additions & 1 deletion tests/pre_use_test.lua
Original file line number Diff line number Diff line change
Expand Up @@ -121,4 +121,14 @@ for _, case in ipairs(cases) do
local version = result and result.version
assert(version == case[2], case[1] .. ": expected " .. tostring(case[2]) .. ", got " .. tostring(version))
end
print("PASS: " .. #cases .. " PreUse regression cases")

-- A distribution without a version names no installed version, so PreUse asks
-- for one, as PreInstall does (vfox-java#98). Alone it failed with a Lua error;
-- after an empty version, vfox reported -tem not installed.
local rejected = { "tem", "temurin", "-tem", "temurin-", "zulu-fx", "open", "lnxabi2-fx" }
for _, version in ipairs(rejected) do
local ok, err = pcall(PLUGIN.PreUse, PLUGIN, { version = version, installedSdks = installed })
local message = "No version in " .. version .. "; add one"
assert(not ok and tostring(err):find(message, 1, true), version .. ": unexpected " .. tostring(err))
end
print("PASS: " .. #cases + #rejected .. " PreUse regression cases")
Loading