diff --git a/docs/e2e.md b/docs/e2e.md index 2aa5a97..108b1c7 100644 --- a/docs/e2e.md +++ b/docs/e2e.md @@ -109,6 +109,11 @@ JDK, so `java` can only come from what vfox installs: build number `PreUse` finds among the installed versions) and the asdf-style `-[-fx]` (`java@temurin-21.0.8+9`, rewritten to `21.0.8+9-tem`; `java@openjdk-25.0.2+10` to `25.0.2+10`). + So does the release padded with a zero, as SDKMAN writes releases + (`java@21.0.8.0-tem`, `java@21.0.8.0+9-tem`), which `vfox install` must + first find already installed: `PreInstall` passes the zero on to Foojay, + which leaves it out, while `PreUse` looked for an installed version + starting with the padded release and reported it not installed. The default matrix runs every distribution against both vfox channels (`latest`, `main`): diff --git a/hooks/parse_legacy_file.lua b/hooks/parse_legacy_file.lua index 90abd4f..1b0682e 100644 --- a/hooks/parse_legacy_file.lua +++ b/hooks/parse_legacy_file.lua @@ -75,10 +75,7 @@ function PLUGIN:ParseLegacyFile(ctx) --- SDKMAN pads the release with zeros that Foojay, and so the installed --- name, leaves out: 27.0.0+36 is 27+36, 25.0.1.0+8 is 25.0.1+8. - local version, build = fullVersion:match("^([^+]*)(.*)$") - while version:find("%.0$") do - version = version:sub(1, -3) - end + local version, build = installed_version.unpad(fullVersion):match("^([^+]*)(.*)$") local majorVersion = strings.split(version, ".")[1] --- Installed versions end in - (OpenJDK's in nothing), then -fx for diff --git a/lib/installed_version.lua b/lib/installed_version.lua index 74d8233..49b84d2 100644 --- a/lib/installed_version.lua +++ b/lib/installed_version.lua @@ -82,11 +82,24 @@ function installed_version.major(installed, major, suffix) return installed_version.newest(installed, major, "^[.+][%d.+]*$", suffix) or installed[name] and name end +--- Returns a Java version without the zeros SDKMAN pads releases with, which +--- Foojay, and so the installed name, leaves out even when asked for them: +--- 27.0.0+36 is 27+36, 25.0.1.0 is 25.0.1. +function installed_version.unpad(version) + local release, build = version:match("^([^+]*)(.*)$") + while release:find("%.0$") do + release = release:sub(1, -3) + end + return release .. build +end + --- Returns the installed version that version .. suffix names, or nil. The --- input may leave out the build number PreInstall found, so, like PreInstall, --- take the newest build of a release (21.0.8-tem selects 21.0.8+9-tem) and --- the newest release of a bare major (21-tem selects 21.0.12.1+1-tem), unless ---- the name itself is installed. +--- the name itself is installed. It may also pad the release with zeros, which +--- PreInstall passes on to Foojay: 27.0.0-librca installs, and so selects, the +--- release 27 (27+36-librca), not the newest one of the major. function installed_version.find(installed, version, suffix) local name = version .. suffix if installed[name] then @@ -95,7 +108,7 @@ function installed_version.find(installed, version, suffix) if version:match("^%d+$") then return installed_version.major(installed, version, suffix) end - return installed_version.release(installed, version, suffix) + return installed_version.release(installed, installed_version.unpad(version), suffix) end return installed_version diff --git a/tests/e2e/common/common.go b/tests/e2e/common/common.go index d8be46b..91f4bb6 100644 --- a/tests/e2e/common/common.go +++ b/tests/e2e/common/common.go @@ -352,6 +352,21 @@ func Alias(distribution, installed string) (string, bool) { return alias, true } +// PaddedSpecs renders the pinned release padded with a zero, as SDKMAN +// writes releases (27.0.0-librca, 25.0.1.0-open), in the plugin's spelling: +// ".0[-]" and, when the installed version has a +// build number, ".0+[-]" (21.0.8.0-tem, +// 21.0.8.0+9-tem). PreInstall passes the zero on to Foojay, which leaves it +// out, so both install the pinned JDK, but PreUse looked for an installed +// version starting with the padded release and found none (vfox-java#87). +func PaddedSpecs(distribution, version, installed string) []string { + specs := []string{Spec(distribution, version+".0")} + if build := buildNumber.FindString(installed); build != "" { + specs = append(specs, Spec(distribution, version+".0"+build)) + } + return specs +} + // Vendor returns the java.vendor property the distribution's builds report, // when the suite knows it. func Vendor(distribution string) (string, bool) { diff --git a/tests/e2e/common/common_test.go b/tests/e2e/common/common_test.go index f40f476..e42a1fb 100644 --- a/tests/e2e/common/common_test.go +++ b/tests/e2e/common/common_test.go @@ -15,6 +15,7 @@ package common import ( + "slices" "testing" ) @@ -261,6 +262,24 @@ func TestAlias(t *testing.T) { } } +func TestPaddedSpecs(t *testing.T) { + for _, c := range []struct { + dist, version, installed string + want []string + }{ + {"open", "25.0.2", "25.0.2+10", []string{"25.0.2.0", "25.0.2.0+10"}}, + {"tem", "21.0.8", "21.0.8+9-tem", []string{"21.0.8.0-tem", "21.0.8.0+9-tem"}}, + {"librca-fx", "25.0.4", "25.0.4+9-librca-fx", []string{"25.0.4.0-librca-fx", "25.0.4.0+9-librca-fx"}}, + {"jb", "21.0.5", "21.0.5+79248-jb", []string{"21.0.5.0-jb", "21.0.5.0+79248-jb"}}, + // GraalVM's versions carry no build number. + {"graalce", "25.0.2", "25.0.2-graalce", []string{"25.0.2.0-graalce"}}, + } { + if got := PaddedSpecs(c.dist, c.version, c.installed); !slices.Equal(got, c.want) { + t.Errorf("PaddedSpecs(%s, %s, %s) = %q, want %q", c.dist, c.version, c.installed, got, c.want) + } + } +} + func TestDistributionFacts(t *testing.T) { if v, ok := Vendor("librca-fx"); !ok || v != "BellSoft" { t.Errorf("Vendor(librca-fx) = %q, %v", v, ok) diff --git a/tests/e2e/linux/verify.go b/tests/e2e/linux/verify.go index 11c4826..79f6ed3 100644 --- a/tests/e2e/linux/verify.go +++ b/tests/e2e/linux/verify.go @@ -161,7 +161,10 @@ func verifyToolchain(ctx context.Context, t *testing.T, ctr tc.Container, distri // build number PreUse finds among the installed versions; // 3. the asdf-style -[-fx] spelling (e.g. // java@temurin-21.0.8+9), which PreUse rewrites to - -// ( for OpenJDK, installed without a suffix). +// ( for OpenJDK, installed without a suffix); +// 4. the release padded with a zero, as SDKMAN writes releases (e.g. +// java@21.0.8.0-tem, java@21.0.8.0+9-tem, see common.PaddedSpecs), +// which `vfox install` must first find already installed. // // After each `vfox use`, java on PATH must still be the installed JDK. func verifyPreUse(ctx context.Context, t *testing.T, ctr tc.Container, distribution, version, installed string) { @@ -170,6 +173,16 @@ func verifyPreUse(ctx context.Context, t *testing.T, ctr tc.Container, distribut if alias, ok := common.Alias(distribution, installed); ok { spellings = append(spellings, alias) } + // Foojay leaves the zero out, so PreInstall names the installed JDK and + // vfox downloads nothing. + padded := common.PaddedSpecs(distribution, version, installed) + for _, spec := range padded { + out := execRetry(ctx, t, ctr, "vfox install java@"+spec, "vfox install java@"+spec) + require.Containsf(t, common.StripANSI(out), "java@"+installed+" is already installed", + "vfox install java@%s must find the installed %s", spec, installed) + } + t.Logf("PASS vfox install java@%s finds %s installed", strings.Join(padded, ", java@"), installed) + spellings = append(spellings, padded...) for _, spelling := range spellings { out := execOK(ctx, t, ctr, activated("vfox use --global java@"+spelling)) require.Containsf(t, common.StripANSI(out), "Now using java@"+installed+".", diff --git a/tests/e2e/windows/verify.go b/tests/e2e/windows/verify.go index 7860287..759ce7a 100644 --- a/tests/e2e/windows/verify.go +++ b/tests/e2e/windows/verify.go @@ -174,7 +174,10 @@ func verifyToolchain(ctx context.Context, t *testing.T, ctr tc.Container, box sl // build number PreUse finds among the installed versions; // 3. the asdf-style -[-fx] spelling (e.g. // java@temurin-21.0.8+9), which PreUse rewrites to - -// ( for OpenJDK, installed without a suffix). +// ( for OpenJDK, installed without a suffix); +// 4. the release padded with a zero, as SDKMAN writes releases (e.g. +// java@21.0.8.0-tem, java@21.0.8.0+9-tem, see common.PaddedSpecs), +// which `vfox install` must first find already installed. // // After each `vfox use`, java on PATH must still be the installed JDK. func verifyPreUse(ctx context.Context, t *testing.T, ctr tc.Container, box slotBox, activation, distribution, version, installed string) { @@ -183,6 +186,16 @@ func verifyPreUse(ctx context.Context, t *testing.T, ctr tc.Container, box slotB if alias, ok := common.Alias(distribution, installed); ok { spellings = append(spellings, alias) } + // Foojay leaves the zero out, so PreInstall names the installed JDK and + // vfox downloads nothing. + padded := common.PaddedSpecs(distribution, version, installed) + for _, spec := range padded { + out := execRetry(ctx, t, ctr, "vfox install java@"+spec, "vfox install "+pwshQuote("java@"+spec), box.vars...) + require.Containsf(t, common.StripANSI(out), "java@"+installed+" is already installed", + "vfox install java@%s must find the installed %s", spec, installed) + } + t.Logf("PASS vfox install java@%s finds %s installed", strings.Join(padded, ", java@"), installed) + spellings = append(spellings, padded...) for _, spelling := range spellings { out := execOK(ctx, t, ctr, activated(activation, "vfox use --global "+pwshQuote("java@"+spelling)), box.vars...) require.Containsf(t, common.StripANSI(out), "Now using java@"+installed+".", diff --git a/tests/pre_use_test.lua b/tests/pre_use_test.lua index f21b04a..f79526d 100644 --- a/tests/pre_use_test.lua +++ b/tests/pre_use_test.lua @@ -25,6 +25,8 @@ for _, version in ipairs({ "21.0.12.1+1-tem", "25.0.4+9-librca", "25.0.4+9-librca-fx", + "27+36-librca", + "27+36-librca-fx", "21.0.5+63130-jb", "21.0.5+79248-jb", "17.0.12-graal", @@ -34,6 +36,7 @@ for _, version in ipairs({ "21.0.12+8-lnxabi2-fx", -- OpenJDK versions carry no distribution suffix. "25.0.2+10", + "25.0.1+8", "21+35", "21.0.2+13", "28-ea+18", @@ -89,6 +92,27 @@ local cases = { { "25.0.2-open-fx", "25.0.2-fx" }, { "17-open", "17" }, { "28-open", "28" }, + -- A release padded with zeros, as SDKMAN writes it, selects what PreInstall + -- installs for it, as Foojay leaves the zeros out (vfox-java#87): that + -- release, not the newest one of its major (21.0.0-open is 21+35). + { "27.0.0-librca", "27+36-librca" }, + { "27.0.0+36-librca", "27+36-librca" }, + { "27.0-librca", "27+36-librca" }, + { "27.0.0-librca-fx", "27+36-librca-fx" }, + { "liberica-27.0.0+36-fx", "27+36-librca-fx" }, + { "21.0.8.0-tem", "21.0.8+9-tem" }, + { "21.0.8.0+9-tem", "21.0.8+9-tem" }, + { "21.0.12.0-tem", "21.0.12+8-tem" }, + { "17.0.12.0-graal", "17.0.12-graal" }, + { "25.0.1.0-open", "25.0.1+8" }, + { "25.0.1.0", "25.0.1+8" }, + { "openjdk-25.0.1.0+8", "25.0.1+8" }, + { "21.0.0-open", "21+35" }, + { "21.0.0", "21+35" }, + -- Padded or not, never another build or an EA build. + { "21.0.8.0+7-tem", "21.0.8.0+7-tem" }, + { "27.0.0+35-librca", "27.0.0+35-librca" }, + { "26.0.0-graal", "26.0.0-graal" }, } for _, case in ipairs(cases) do local result = PLUGIN:PreUse({ version = case[1], installedSdks = installed })