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
16 changes: 10 additions & 6 deletions docs/e2e.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,16 @@ JDK, so `java` can only come from what vfox installs:
without the build number selects the JDK through `ParseLegacyFile`:
`java=21.0.8-tem`, `java=21.0.5-jbr` (SDKMAN's name for JetBrains) or
`java=25.0.4.fx-librca` (its spelling of JavaFX builds). So does one
holding the installed version (`java=21.0.8+9-tem`). Both run next to an
empty install of another release whose version starts with the pinned
one (`21.0.80+1-tem`): vfox sorts installed versions as strings,
descending, so `vfox list java` must show it first, and
`ParseLegacyFile` used to take it as the first one starting with the
release. On Windows the file is written with CRLF line endings.
padding the release with a zero, as `sdk list java` shows many:
`java=21.0.8.0+9-tem`, `java=25.0.4.0-fx+9-librca` (Foojay, and so the
installed name, has no such zero). So does one holding the installed
version (`java=21.0.8+9-tem`). All run next to an empty install of
another release whose version starts with the pinned one
(`21.0.80+1-tem`): vfox sorts installed versions as strings, descending,
so `vfox list java` must show it first. `ParseLegacyFile` used to take it
as the first one starting with the release, and, for the padded release,
as the newest release of the major. On Windows the file is written with
CRLF line endings.
5. After `vfox use --global`, the JDK on `PATH` reports the requested
`java.version`, the runner's `os.arch` (`amd64` / `aarch64`, which also
catches an x64 JDK emulated on Windows ARM64) and the distribution's
Expand Down
21 changes: 14 additions & 7 deletions hooks/parse_legacy_file.lua
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,12 @@ function PLUGIN:ParseLegacyFile(ctx)
return {}
end

local version = strings.split(fullVersion, "+")[1]
--- 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 majorVersion = strings.split(version, ".")[1]

--- Installed versions end in -<vendor> (OpenJDK's in nothing), then -fx for
Expand All @@ -81,14 +86,16 @@ function PLUGIN:ParseLegacyFile(ctx)
installed[v] = true
end

--- The version itself (21.0.5+63130-jb), else the newest build of the
--- release, as SDKMAN's build numbers count its own rebuilds
--- The version itself (21.0.5+63130-jb), else the release itself or its
--- newest build, as SDKMAN's build numbers count its own rebuilds
--- (21.0.12+1.1-tem), else the newest installed release of the major
--- (21.0.12+8-tem for 21.0.3-tem).
local name = fullVersion .. suffix
--- (21.0.12+8-tem for 21.0.3-tem). A bare major (21-tem) skips the
--- release, taking the newest one as vfox use java@21-tem does, while
--- SDKMAN's 21.0.0 still names the release 21 (21+35-tem).
local name = version .. build .. suffix
return {
version = installed[name] and name
or installed_version.find(installed, version, suffix)
or installed_version.newest(installed, majorVersion .. ".", "^[%d.+]*$", suffix),
or not fullVersion:match("^%d+$") and installed_version.release(installed, version, suffix)
or installed_version.major(installed, majorVersion, suffix),
}
end
29 changes: 24 additions & 5 deletions lib/installed_version.lua
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,26 @@ function installed_version.newest(installed, prefix, rest, suffix)
return best
end

--- Returns the installed version of a release given without a build number,
--- or nil: the release itself (17.0.12-graal), else its newest build
--- (21.0.8+9-tem for 21.0.8, 27+36-librca for 27), but never another release
--- (not 21.0.12.1+1 for 21.0.12, nor 21.0.12+8 for 21.0.1).
function installed_version.release(installed, release, suffix)
local name = release .. suffix
if installed[name] then
return name
end
return installed_version.newest(installed, release, "^%+%d+$", suffix)
end

--- Returns the newest installed release of a major, or nil: any release of it
--- (21.0.12.1+1-tem for 21), the first one too (27+36-librca, 27-oracle for
--- 27), but no EA build (26-ea+13 for 26).
function installed_version.major(installed, major, suffix)
local name = major .. suffix
return installed_version.newest(installed, major, "^[.+][%d.+]*$", suffix) or installed[name] and name
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
Expand All @@ -72,11 +92,10 @@ function installed_version.find(installed, version, suffix)
if installed[name] then
return name
end
-- A bare major takes any release of it (21.0.12.1+1 for 21) but no EA build
-- (26-ea+13 for 26); other versions take only a build number (not
-- 21.0.12.1+1 for 21.0.12, nor 21.0.12+8 for 21.0.1).
local rest = version:match("^%d+$") and "^[.+][%d.+]*$" or "^%+%d+$"
return installed_version.newest(installed, version, rest, suffix)
if version:match("^%d+$") then
return installed_version.major(installed, version, suffix)
end
return installed_version.release(installed, version, suffix)
end

return installed_version
30 changes: 25 additions & 5 deletions tests/e2e/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -273,19 +273,39 @@ func Major(version string) string {
return strings.SplitN(version, ".", 2)[0]
}

// sdkmanName returns SDKMAN's name for the distribution, which calls
// JetBrains "jbr".
func sdkmanName(distribution string) string {
if name := Base(distribution); name != "jb" {
return name
}
return "jbr"
}

// LegacyIdentifier renders the SDKMAN identifier a .sdkmanrc holds for a
// release: "<version>-<distribution>", without the build number, in SDKMAN's
// spelling: JetBrains is "jbr" and JavaFX builds are
// "<version>.fx-<distribution>" (21.0.5-jbr, 25.0.4.fx-librca).
func LegacyIdentifier(distribution, version string) string {
name := Base(distribution)
if name == "jb" {
name = "jbr"
}
if JavaFX(distribution) {
version += ".fx"
}
return version + "-" + name
return version + "-" + sdkmanName(distribution)
}

// PaddedIdentifier renders the SDKMAN identifier of an installed version
// with a zero padding the release, as `sdk list java` shows many
// (25.0.1.0+8-open, 21.0.9.0+116386-jbr):
// "<version>.0[-fx][+<build>]-<distribution>" with the installed build
// (21.0.8.0+9-tem, 25.0.4.0-fx+9-librca). Foojay, and so the installed
// name, leaves the zero out; ParseLegacyFile did not, so it found no such
// release and fell back to the newest one of the major (vfox-java#80).
func PaddedIdentifier(distribution, version, installed string) string {
version += ".0"
if JavaFX(distribution) {
version += "-fx"
}
return version + buildNumber.FindString(installed) + "-" + sdkmanName(distribution)
}

// Alias renders the asdf-style spelling "<long-name>-<version>[-fx]" of an
Expand Down
14 changes: 14 additions & 0 deletions tests/e2e/common/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,20 @@ func TestSpecs(t *testing.T) {
}
}

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"},
{"tem", "21.0.8", "21.0.8+9-tem", "21.0.8.0+9-tem"},
{"librca-fx", "25.0.4", "25.0.4+9-librca-fx", "25.0.4.0-fx+9-librca"},
{"jb", "21.0.5", "21.0.5+79248-jb", "21.0.5.0+79248-jbr"},
{"graalce", "25.0.2", "25.0.2-graalce", "25.0.2.0-graalce"},
} {
if got := PaddedIdentifier(c.dist, c.version, c.installed); got != c.want {
t.Errorf("PaddedIdentifier(%s, %s, %s) = %q, want %q", c.dist, c.version, c.installed, got, c.want)
}
}
}

func TestAlias(t *testing.T) {
for _, c := range []struct{ dist, installed, want string }{
{"open", "25.0.2+10", "openjdk-25.0.2+10"},
Expand Down
15 changes: 10 additions & 5 deletions tests/e2e/linux/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,11 @@ func verifySearch(ctx context.Context, t *testing.T, ctr tc.Container, distribut
// verifyLegacyFile proves ParseLegacyFile selects the installed JDK from a
// .sdkmanrc (PLUGIN.legacyFilenames) holding the SDKMAN identifier without
// the build number (e.g. java=21.0.8-tem, java=21.0.5-jbr,
// java=25.0.4.fx-librca) or the installed version itself (e.g.
// java=21.0.8+9-tem), while vfox lists an empty install of another release
// first (see common.Decoy). It runs before any `vfox use`, so java can only
// come from the legacy file.
// java=25.0.4.fx-librca), the one with a zero padding the release (e.g.
// java=21.0.8.0+9-tem, see common.PaddedIdentifier) or the installed version
// itself (e.g. java=21.0.8+9-tem), while vfox lists an empty install of
// another release first (see common.Decoy). It runs before any `vfox use`,
// so java can only come from the legacy file.
func verifyLegacyFile(ctx context.Context, t *testing.T, ctr tc.Container, distribution, version, installed, arch string) {
t.Helper()
const dir = "/tmp/e2e-legacy-sdkmanrc"
Expand All @@ -69,7 +70,11 @@ func verifyLegacyFile(ctx context.Context, t *testing.T, ctr tc.Container, distr
listed := common.ListedVersions(execOK(ctx, t, ctr, "vfox list java"))
require.Equalf(t, []string{decoy, installed}, listed, "vfox list java must list the decoy %s first", decoy)

for _, identifier := range []string{common.LegacyIdentifier(distribution, version), installed} {
for _, identifier := range []string{
common.LegacyIdentifier(distribution, version),
common.PaddedIdentifier(distribution, version, installed),
installed,
} {
execOK(ctx, t, ctr, fmt.Sprintf("mkdir --parents %s && printf 'java=%%s\\n' %s > %s/.sdkmanrc",
dir, shQuote(identifier), dir))
// The probe activates inside its own exec, a fresh shell session, so no
Expand Down
13 changes: 9 additions & 4 deletions tests/e2e/windows/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,10 @@ func verifySearch(ctx context.Context, t *testing.T, ctr tc.Container, box slotB
// verifyLegacyFile proves ParseLegacyFile selects the installed JDK from a
// .sdkmanrc (PLUGIN.legacyFilenames) holding the SDKMAN identifier without
// the build number (e.g. java=21.0.8-tem, java=21.0.5-jbr,
// java=25.0.4.fx-librca) or the installed version itself (e.g.
// java=21.0.8+9-tem), written by Set-Content with Windows line endings,
// while vfox lists an empty install of another release first (see
// java=25.0.4.fx-librca), the one with a zero padding the release (e.g.
// java=21.0.8.0+9-tem, see common.PaddedIdentifier) or the installed version
// itself (e.g. java=21.0.8+9-tem), written by Set-Content with Windows line
// endings, while vfox lists an empty install of another release first (see
// common.Decoy). It runs before any `vfox use`, so java can only come from
// the legacy file.
func verifyLegacyFile(ctx context.Context, t *testing.T, ctr tc.Container, box slotBox, distribution, version, installed, arch string) {
Expand All @@ -73,7 +74,11 @@ func verifyLegacyFile(ctx context.Context, t *testing.T, ctr tc.Container, box s
listed := common.ListedVersions(execOK(ctx, t, ctr, "vfox list java", box.vars...))
require.Equalf(t, []string{decoy, installed}, listed, "vfox list java must list the decoy %s first", decoy)

for _, identifier := range []string{common.LegacyIdentifier(distribution, version), installed} {
for _, identifier := range []string{
common.LegacyIdentifier(distribution, version),
common.PaddedIdentifier(distribution, version, installed),
installed,
} {
execOK(ctx, t, ctr, fmt.Sprintf(
"New-Item -ItemType Directory -Force -Path %s | Out-Null; Set-Content -Path %s -Value %s",
pwshQuote(dir), pwshQuote(dir+`\.sdkmanrc`), pwshQuote("java="+identifier)), box.vars...)
Expand Down
20 changes: 20 additions & 0 deletions tests/parse_legacy_file_test.lua
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,26 @@ local cases = {
{ "java=21.0.12-zulu\n", jb_and_zulu_fx, nil },
{ "java=21.0.12.fx-zulu\n", { "21.0.12+7-zulu" }, nil },
{ "java=21.0.8.fx-zulu\n", { "21.0.12+7-zulu", "21.0.12+7-zulu-fx" }, "21.0.12+7-zulu-fx" },
-- SDKMAN pads releases with zeros that Foojay, and so the installed name,
-- leaves out (vfox-java#80).
{ "java=27.0.0+36-librca\n", { "27+36-librca" }, "27+36-librca" },
{ "java=27.0.0-fx+36-librca\n", { "27+36-librca", "27+36-librca-fx" }, "27+36-librca-fx" },
{ "java=27.0.0+35-zulu\n", { "27+35-zulu" }, "27+35-zulu" },
{ "java=27.0.0.0+35-tem\n", { "27+35-tem" }, "27+35-tem" },
{ "java=27.0.0+35-open\n", { "27+35" }, "27+35" },
{ "java=26.0.0.0+35-open\n", { "26+35" }, "26+35" },
{ "java=25.0.1.0+8-open\n", { "25.0.1+8", "25.0.2+10" }, "25.0.1+8" },
{ "java=21.0.0.0-graal\n", { "21-graal", "21.0.12-graal" }, "21-graal" },
{ "java=21.0.12.0-sapmchn\n", { "21.0.12-sapmchn", "21.0.12.1-sapmchn" }, "21.0.12-sapmchn" },
{ "java=27.0.0.0+33-amzn\n", { "27-amzn" }, "27-amzn" },
{ "java=21.0.10-tem\n", { "21.0.1+12-tem", "21.0.10+7-tem" }, "21.0.10+7-tem" },
-- A padded major names its first release, a bare major the newest one.
{ "java=26.0.0.0+35-open\n", { "26+34", "26.0.2+10" }, "26+34" },
{ "java=26-open\n", { "26+34", "26.0.2+10" }, "26.0.2+10" },
-- Without the release, the first release of the major is the newest one too.
{ "java=25.0.1.0+8-open\n", { "25+36" }, "25+36" },
{ "java=26.0.1-oracle\n", { "26-oracle" }, "26-oracle" },
{ "java=26.0.2-oracle\n", { "26-oracle", "26.0.1-oracle" }, "26.0.1-oracle" },
-- No java line at all selects nothing.
{ "maven=3.9.9\n", { "21.0.8+9-tem" }, nil },
}
Expand Down
Loading