Skip to content

Commit

Permalink
[FEATURE] Introduce SpecVersionUtil
Browse files Browse the repository at this point in the history
Introduce a utility module which handles SpecVersion
comparators for an easier SpecVersion maintainance
within the UI5 Tooling source code.
This utility is a kind of semver compatibility layer which
allows using semver functions with the SpecVersion
typical format "Major.Minor".

JIRA: CPOUI5FOUNDATION-224
  • Loading branch information
larskissel authored and RandomByte committed Jun 15, 2022
1 parent 159a4c3 commit 5d3e8d7
Show file tree
Hide file tree
Showing 2 changed files with 206 additions and 0 deletions.
71 changes: 71 additions & 0 deletions lib/SpecVersionUtil.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
const semver = require("semver");

const SPEC_VERSION_PATTERN = /^\d+\.\d+$/;
const SUPPORTED_VERSIONS = [
"0.1", "1.0", "1.1",
"2.0", "2.1", "2.2", "2.3", "2.4", "2.5", "2.6"
];

function isSupportedSpecVersion(specVersion) {
return SUPPORTED_VERSIONS.includes(specVersion);
}

function getSemverCompatibleVersion(specVersion) {
if (isSupportedSpecVersion(specVersion)) {
return specVersion + ".0";
}
// TODO 3.0: sync error text with error of projectPreprocessor.js
throw new Error(
`Unsupported specification version ${specVersion} defined. Your UI5 CLI installation might be outdated. ` +
`For details see https://sap.github.io/ui5-tooling/pages/Configuration/#specification-versions`);
}

function major(specVersion) {
const version = getSemverCompatibleVersion(specVersion);
return semver.major(version);
}

function minor(specVersion) {
const version = getSemverCompatibleVersion(specVersion);
return semver.minor(version);
}

function satisfies(specVersion, range) {
const version = getSemverCompatibleVersion(specVersion);
return semver.satisfies(version, range);
}

function handleSemverComparator(comparator, specVersion, expectedVersion) {
if (SPEC_VERSION_PATTERN.test(expectedVersion)) {
const a = getSemverCompatibleVersion(specVersion);
const b = expectedVersion + ".0";
return comparator(a, b);
}
throw new Error("Invalid spec version expectation given in comparator: " + expectedVersion);
}
const gt = (specVersion, expectedVersion) => handleSemverComparator(semver.gt, specVersion, expectedVersion);
const gte = (specVersion, expectedVersion) => handleSemverComparator(semver.gte, specVersion, expectedVersion);
const lt = (specVersion, expectedVersion) => handleSemverComparator(semver.lt, specVersion, expectedVersion);
const lte = (specVersion, expectedVersion) => handleSemverComparator(semver.lte, specVersion, expectedVersion);
const eq = (specVersion, expectedVersion) => handleSemverComparator(semver.eq, specVersion, expectedVersion);
const neq = (specVersion, expectedVersion) => handleSemverComparator(semver.neq, specVersion, expectedVersion);

module.exports = {
isSupportedSpecVersion,
// semver functions
major,
minor,
satisfies,
gt,
gte,
lt,
lte,
eq,
neq
};

// Export local function for testing only
if (process.env.NODE_ENV === "test") {
module.exports._getSemverCompatibleVersion = getSemverCompatibleVersion;
module.exports._handleSemverComparator = handleSemverComparator;
}
135 changes: 135 additions & 0 deletions test/lib/SpecVersionUtil.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
const test = require("ava");
const sinon = require("sinon");
const SpecVersionUtil = require("../../lib/SpecVersionUtil");

const unsupportedSpecVersionText = (specVersion) =>
`Unsupported specification version ${specVersion} defined. Your UI5 CLI installation might be outdated. ` +
`For details see https://sap.github.io/ui5-tooling/pages/Configuration/#specification-versions`;

test("isSupportedSpecVersion", (t) => {
t.is(SpecVersionUtil.isSupportedSpecVersion("0.1"), true);
t.is(SpecVersionUtil.isSupportedSpecVersion("1.0"), true);
t.is(SpecVersionUtil.isSupportedSpecVersion("1.1"), true);
t.is(SpecVersionUtil.isSupportedSpecVersion("2.0"), true);
t.is(SpecVersionUtil.isSupportedSpecVersion("2.4"), true);
t.is(SpecVersionUtil.isSupportedSpecVersion("0.2"), false);
t.is(SpecVersionUtil.isSupportedSpecVersion("1.2"), false);
t.is(SpecVersionUtil.isSupportedSpecVersion(1.1), false);
t.is(SpecVersionUtil.isSupportedSpecVersion("foo"), false);
t.is(SpecVersionUtil.isSupportedSpecVersion(""), false);
t.is(SpecVersionUtil.isSupportedSpecVersion(), false);
});

test("major", (t) => {
t.is(SpecVersionUtil.major("0.1"), 0);
t.is(SpecVersionUtil.major("1.1"), 1);
t.is(SpecVersionUtil.major("2.1"), 2);

t.is(t.throws(() => {
SpecVersionUtil.major("0.2");
}).message, unsupportedSpecVersionText("0.2"));
});

test("minor", (t) => {
t.is(SpecVersionUtil.minor("2.1"), 1);
t.is(SpecVersionUtil.minor("2.2"), 2);
t.is(SpecVersionUtil.minor("2.3"), 3);

t.is(t.throws(() => {
SpecVersionUtil.minor("1.2");
}).message, unsupportedSpecVersionText("1.2"));
});

test("satisfies", (t) => {
// range: 1.x
t.is(SpecVersionUtil.satisfies("1.0", "1.x"), true);
t.is(SpecVersionUtil.satisfies("1.1", "1.x"), true);
t.is(SpecVersionUtil.satisfies("2.0", "1.x"), false);

// range: ^2.2
t.is(SpecVersionUtil.satisfies("2.1", "^2.2"), false);
t.is(SpecVersionUtil.satisfies("2.2", "^2.2"), true);
t.is(SpecVersionUtil.satisfies("2.3", "^2.2"), true);

// range: > 1.0
t.is(SpecVersionUtil.satisfies("1.0", "> 1.0"), false);
t.is(SpecVersionUtil.satisfies("1.1", "> 1.0"), true);
t.is(SpecVersionUtil.satisfies("2.2", "> 1.0"), true);

// range: 2.2 - 2.4
t.is(SpecVersionUtil.satisfies("2.1", "2.2 - 2.4"), false);
t.is(SpecVersionUtil.satisfies("2.2", "2.2 - 2.4"), true);
t.is(SpecVersionUtil.satisfies("2.3", "2.2 - 2.4"), true);
t.is(SpecVersionUtil.satisfies("2.4", "2.2 - 2.4"), true);
t.is(SpecVersionUtil.satisfies("2.5", "2.2 - 2.4"), false);

// range: 0.1 || 1.0 - 1.1 || ^2.5
t.is(SpecVersionUtil.satisfies("0.1", "0.1 || 1.0 - 1.1 || ^2.5"), true);
t.is(SpecVersionUtil.satisfies("1.0", "0.1 || 1.0 - 1.1 || ^2.5"), true);
t.is(SpecVersionUtil.satisfies("1.1", "0.1 || 1.0 - 1.1 || ^2.5"), true);
t.is(SpecVersionUtil.satisfies("2.4", "0.1 || 1.0 - 1.1 || ^2.5"), false);
t.is(SpecVersionUtil.satisfies("2.5", "0.1 || 1.0 - 1.1 || ^2.5"), true);
t.is(SpecVersionUtil.satisfies("2.6", "0.1 || 1.0 - 1.1 || ^2.5"), true);

// unsupported spec version
t.is(t.throws(() => {
SpecVersionUtil.satisfies("0.2", "1.x");
}).message, unsupportedSpecVersionText("0.2"));
});

test("low level comparator", (t) => {
t.is(SpecVersionUtil.gt("2.1", "2.2"), false);
t.is(SpecVersionUtil.gt("2.2", "2.2"), false);
t.is(SpecVersionUtil.gt("2.3", "2.2"), true);

t.is(SpecVersionUtil.gte("2.1", "2.2"), false);
t.is(SpecVersionUtil.gte("2.2", "2.2"), true);
t.is(SpecVersionUtil.gte("2.3", "2.2"), true);

t.is(SpecVersionUtil.lt("2.1", "2.2"), true);
t.is(SpecVersionUtil.lt("2.2", "2.2"), false);
t.is(SpecVersionUtil.lt("2.3", "2.2"), false);

t.is(SpecVersionUtil.lte("2.1", "2.2"), true);
t.is(SpecVersionUtil.lte("2.2", "2.2"), true);
t.is(SpecVersionUtil.lte("2.3", "2.2"), false);

t.is(SpecVersionUtil.eq("2.0", "2.2"), false);
t.is(SpecVersionUtil.eq("2.2", "2.2"), true);

t.is(SpecVersionUtil.neq("2.0", "2.2"), true);
t.is(SpecVersionUtil.neq("2.2", "2.2"), false);
});

test("getSemverCompatibleVersion", (t) => {
t.is(SpecVersionUtil._getSemverCompatibleVersion("0.1"), "0.1.0");
t.is(SpecVersionUtil._getSemverCompatibleVersion("1.1"), "1.1.0");
t.is(SpecVersionUtil._getSemverCompatibleVersion("2.0"), "2.0.0");

t.is(t.throws(() => {
SpecVersionUtil._getSemverCompatibleVersion("1.2.3");
}).message, unsupportedSpecVersionText("1.2.3"));
t.is(t.throws(() => {
SpecVersionUtil._getSemverCompatibleVersion("0.99");
}).message, unsupportedSpecVersionText("0.99"));
t.is(t.throws(() => {
SpecVersionUtil._getSemverCompatibleVersion("foo");
}).message, unsupportedSpecVersionText("foo"));
t.is(t.throws(() => {
SpecVersionUtil._getSemverCompatibleVersion();
}).message, unsupportedSpecVersionText("undefined"));
});

test("handleSemverComparator", (t) => {
const comparatorStub = sinon.stub().returns("foobar");
t.is(SpecVersionUtil._handleSemverComparator(comparatorStub, "1.1", "2.2"), "foobar");
t.deepEqual(comparatorStub.getCall(0).args, ["1.1.0", "2.2.0"]);

t.is(t.throws(() => {
SpecVersionUtil._handleSemverComparator(undefined, "a.b", "2.2");
}).message, unsupportedSpecVersionText("a.b"));

t.is(t.throws(() => {
SpecVersionUtil._handleSemverComparator(undefined, undefined, "a.b");
}).message, "Invalid spec version expectation given in comparator: a.b");
});

0 comments on commit 5d3e8d7

Please sign in to comment.