refactor(model): add ReleaseMetadata container (#627)

master
Tobias Nett 2021-02-16 20:24:00 +01:00 committed by GitHub
parent 4b27bfa527
commit 47df617db5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 208 additions and 23 deletions

View File

@ -8,23 +8,24 @@ import java.util.Date;
import java.util.List;
/**
* A game release describes a game artefact (asset) that can be downloaded and installed by the launcher.
* A game release describes a (remote) game artifact (asset) that can be downloaded and installed by the launcher.
* <p>
* Each game release is uniquely identified by the {@link GameIdentifier} {@code id} and provides a URL from which the
* artefact can be retrieved.
* artifact can be retrieved.
* </p>
* <ul>
* <li>TODO: define what the <b>artifact</b> is, and what requirements/restrictions there are</li>
* </ul>
*/
public class GameRelease {
final GameIdentifier id;
final ReleaseMetadata releaseMetadata;
final URL url;
final List<String> changelog;
final Date timestamp;
public GameRelease(GameIdentifier id, URL url, List<String> changelog, Date timestamp) {
public GameRelease(GameIdentifier id, URL url, ReleaseMetadata releaseMetadata) {
this.id = id;
this.url = url;
this.changelog = changelog;
this.timestamp = timestamp;
this.releaseMetadata = releaseMetadata;
}
public GameIdentifier getId() {
@ -39,10 +40,14 @@ public class GameRelease {
* The changelog associated with the game release
*/
public List<String> getChangelog() {
return changelog;
return releaseMetadata.changelog;
}
public Date getTimestamp() {
return timestamp;
return releaseMetadata.timestamp;
}
public boolean isLwjgl3() {
return releaseMetadata.isLwjgl3;
}
}

View File

@ -0,0 +1,30 @@
// Copyright 2021 The Terasology Foundation
// SPDX-License-Identifier: Apache-2.0
package org.terasology.launcher.model;
import java.util.Date;
import java.util.List;
/**
*
*/
public class ReleaseMetadata {
final List<String> changelog;
final Date timestamp;
final boolean isLwjgl3;
public ReleaseMetadata(List<String> changelog, Date timestamp, boolean isLwjgl3) {
this.changelog = changelog;
this.timestamp = timestamp;
this.isLwjgl3 = isLwjgl3;
}
public List<String> getChangelog() {
return changelog;
}
public Date getTimestamp() {
return timestamp;
}
}

View File

@ -9,6 +9,7 @@ import org.terasology.launcher.model.Build;
import org.terasology.launcher.model.GameIdentifier;
import org.terasology.launcher.model.GameRelease;
import org.terasology.launcher.model.Profile;
import org.terasology.launcher.model.ReleaseMetadata;
import java.net.MalformedURLException;
import java.net.URL;
@ -78,12 +79,12 @@ class JenkinsRepositoryAdapter implements ReleaseRepository {
private Optional<GameRelease> computeReleaseFrom(Jenkins.Build jenkinsBuildInfo) {
if (hasAcceptableResult(jenkinsBuildInfo)) {
final URL url = client.getArtifactUrl(jenkinsBuildInfo, TERASOLOGY_ZIP_PATTERN);
final Date timestamp = new Date(jenkinsBuildInfo.timestamp);
final List<String> changelog = computeChangelogFrom(jenkinsBuildInfo);
final ReleaseMetadata metadata = computeReleaseMetadataFrom(jenkinsBuildInfo);
final Optional<GameIdentifier> id = computeIdentifierFrom(jenkinsBuildInfo);
if (url != null && id.isPresent()) {
return Optional.of(new GameRelease(id.get(), url, changelog, timestamp));
return Optional.of(new GameRelease(id.get(), url, metadata));
} else {
logger.debug("Skipping build without game artifact or version identifier: '{}'", jenkinsBuildInfo.url);
}
@ -100,10 +101,17 @@ class JenkinsRepositoryAdapter implements ReleaseRepository {
.map(displayVersion -> new GameIdentifier(displayVersion, buildProfile, profile));
}
private List<String> computeChangelogFrom(Jenkins.Build jenkinsBuildInfo) {
return Optional.ofNullable(jenkinsBuildInfo.changeSet)
.map(changeSet ->
Arrays.stream(changeSet.items)
private ReleaseMetadata computeReleaseMetadataFrom(Jenkins.Build jenkinsBuildInfo) {
List<String> changelog = computeChangelogFrom(jenkinsBuildInfo.changeSet);
final Date timestamp = new Date(jenkinsBuildInfo.timestamp);
// all builds from this Jenkins are using LWJGL v3
return new ReleaseMetadata(changelog, timestamp, true);
}
private List<String> computeChangelogFrom(Jenkins.ChangeSet changeSet) {
return Optional.ofNullable(changeSet)
.map(changes ->
Arrays.stream(changes.items)
.map(change -> change.msg)
.collect(Collectors.toList())
).orElse(new ArrayList<>());

View File

@ -9,6 +9,7 @@ import org.terasology.launcher.model.Build;
import org.terasology.launcher.model.GameIdentifier;
import org.terasology.launcher.model.GameRelease;
import org.terasology.launcher.model.Profile;
import org.terasology.launcher.model.ReleaseMetadata;
import java.net.MalformedURLException;
import java.net.URL;
@ -65,6 +66,41 @@ class LegacyJenkinsRepositoryAdapter implements ReleaseRepository {
.orElse(new ArrayList<>());
}
/**
* The PR upgrading the engine to LWJGL v3 (https://github.com/MovingBlocks/Terasology/pull/3969) was merged on
* Oct 24, 2020, 20:12 UTC as commit 'c83655fb94c02d68fb8ba31fdb1954e81dde12d6'.
* <p>
* This method does some reverse engineering of which build on which Jenkins job contains this change. This should
* probably be baked into the {@link GameIdentifier} while fetching the info from the remote source. With upcoming
* refactoring I'd like to keep this separate here for now...
*
* @param buildNumber the Jenkins build numbers (from jenkins.terasology.org)
* @return
*/
private boolean isLwjgl3(int buildNumber) {
if (profile.equals(Profile.OMEGA) && buildProfile.equals(Build.STABLE)) {
return buildNumber > 37;
}
if (profile.equals(Profile.OMEGA) && buildProfile.equals(Build.NIGHTLY)) {
return buildNumber > 1103;
}
if (profile.equals(Profile.ENGINE) && buildProfile.equals(Build.STABLE)) {
return buildNumber > 82;
}
if (profile.equals(Profile.ENGINE) && buildProfile.equals(Build.NIGHTLY)) {
return buildNumber > 2317;
}
return false;
}
private ReleaseMetadata computeReleaseMetadataFrom(Jenkins.Build jenkinsBuildInfo) {
final List<String> changelog = computeChangelogFrom(jenkinsBuildInfo.changeSet);
final Date timestamp = new Date(jenkinsBuildInfo.timestamp);
final boolean isLwjgl3 = isLwjgl3(Integer.parseInt(jenkinsBuildInfo.number));
// all builds from this Jenkins are using LWJGL v3
return new ReleaseMetadata(changelog, timestamp, isLwjgl3);
}
public List<GameRelease> fetchReleases() {
final List<GameRelease> pkgList = new LinkedList<>();
final String apiUrl = baseUrl + "job/" + jobName + "/" + API_FILTER;
@ -76,12 +112,11 @@ class LegacyJenkinsRepositoryAdapter implements ReleaseRepository {
if (result != null && result.builds != null) {
for (Jenkins.Build build : result.builds) {
if (hasAcceptableResult(build)) {
final List<String> changelog = computeChangelogFrom(build.changeSet);
final URL url = client.getArtifactUrl(build, TERASOLOGY_ZIP_PATTERN);
if (url != null) {
final GameIdentifier id = new GameIdentifier(build.number, buildProfile, profile);
final Date timestamp = new Date(build.timestamp);
final GameRelease release = new GameRelease(id, url, changelog, timestamp);
final ReleaseMetadata releaseMetadata = computeReleaseMetadataFrom(build);
final GameRelease release = new GameRelease(id, url, releaseMetadata);
pkgList.add(release);
} else {
logger.debug("Skipping build without game artifact: '{}'", build.url);

View File

@ -5,7 +5,7 @@ package org.terasology.launcher.repositories;
import java.util.List;
public class JenkinsPayload {
class JenkinsPayload {
private JenkinsPayload() {
}
@ -14,6 +14,21 @@ public class JenkinsPayload {
* Example payloads from the "old" Jenkins at http://jenkins.terasology.org
*/
static class V1 {
static String minimalValidBuildPayload() {
return "{\n" +
" \"artifacts\": [\n" +
" {\n" +
" \"fileName\": \"TerasologyOmega.zip\",\n" +
" \"relativePath\": \"distros/omega/build/distributions/TerasologyOmega.zip\"\n" +
" }\n" +
" ],\n" +
" \"number\": 1123,\n" +
" \"result\": \"SUCCESS\",\n" +
" \"timestamp\": 1609713454443,\n" +
" \"url\": \"http://jenkins.terasology.org/job/DistroOmega/1123/\"\n" +
"}\n";
}
static String validPayload() {
return "{\n" +
" \"builds\": [\n" +

View File

@ -14,6 +14,7 @@ import org.terasology.launcher.model.Build;
import org.terasology.launcher.model.GameIdentifier;
import org.terasology.launcher.model.GameRelease;
import org.terasology.launcher.model.Profile;
import org.terasology.launcher.model.ReleaseMetadata;
import java.net.MalformedURLException;
import java.net.URL;
@ -82,7 +83,8 @@ class JenkinsRepositoryAdapterTest {
final JenkinsClient stubClient = new StubJenkinsClient(url -> validResult, url -> versionInfo);
final GameIdentifier id = new GameIdentifier(expectedVersion, Build.STABLE, Profile.OMEGA);
final GameRelease expected = new GameRelease(id, expectedArtifactUrl, new ArrayList<>(), new Date(1604285977306L));
final ReleaseMetadata releaseMetadata = new ReleaseMetadata(new ArrayList<>(), new Date(1604285977306L), true);
final GameRelease expected = new GameRelease(id, expectedArtifactUrl, releaseMetadata);
final JenkinsRepositoryAdapter adapter = new JenkinsRepositoryAdapter(Profile.OMEGA, Build.STABLE, stubClient);
@ -90,7 +92,9 @@ class JenkinsRepositoryAdapterTest {
assertAll(
() -> assertEquals(expected.getId(), adapter.fetchReleases().get(0).getId()),
() -> assertEquals(expected.getUrl(), adapter.fetchReleases().get(0).getUrl()),
() -> assertEquals(expected.getTimestamp(), adapter.fetchReleases().get(0).getTimestamp())
() -> assertEquals(expected.getTimestamp(), adapter.fetchReleases().get(0).getTimestamp()),
() -> assertEquals(expected.isLwjgl3(), adapter.fetchReleases().get(0).isLwjgl3(),
"Jenkins adapter should assume only builds for LWJGL v3 releases")
);
}

View File

@ -0,0 +1,86 @@
// Copyright 2021 The Terasology Foundation
// SPDX-License-Identifier: Apache-2.0
package org.terasology.launcher.repositories;
import com.google.gson.Gson;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.terasology.launcher.model.Build;
import org.terasology.launcher.model.Profile;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
@DisplayName("LegacyJenkinsRepositoryAdapter should be backwards compatible.")
class LegacyJenkinsRepositoryAdapterCompatibilityTest {
static final String BASE_URL = "http://jenkins.terasology.org";
static final String JOB = "DistroOmega";
static Gson gson = new Gson();
/**
* The PR upgrading the engine to LWJGL v3 (https://github.com/MovingBlocks/Terasology/pull/3969) was merged on
* Oct 24, 2020, 20:12 UTC as commit 'c83655fb94c02d68fb8ba31fdb1954e81dde12d6'.
* <p>
* These are the corresponding build numbers on 'jenkins.terasology.org' for the first build with LWJGL v3.
*/
static Stream<Arguments> firstBuildsForLwjgl3() {
return Stream.of(
Arguments.of(Profile.OMEGA, Build.STABLE, 38),
Arguments.of(Profile.OMEGA, Build.NIGHTLY, 1104),
Arguments.of(Profile.ENGINE, Build.STABLE, 83),
Arguments.of(Profile.ENGINE, Build.NIGHTLY, 2318)
);
}
@ParameterizedTest(name = "release before LWJGL v3 is correctly marked - {arguments}")
@MethodSource("firstBuildsForLwjgl3")
void releaseBeforeLwjgl3IsCorrectlyMarked(Profile profile, Build build, int buildNumber) {
Jenkins.Build before = gson.fromJson(JenkinsPayload.V1.minimalValidBuildPayload(), Jenkins.Build.class);
before.number = Integer.toString(buildNumber - 1);
Jenkins.ApiResult result = gson.fromJson(JenkinsPayload.V1.minimalValidPayload(), Jenkins.ApiResult.class);
result.builds = new Jenkins.Build[]{before};
final JenkinsClient stubClient = new StubJenkinsClient(url -> result, url -> fail());
final LegacyJenkinsRepositoryAdapter adapter = new LegacyJenkinsRepositoryAdapter(BASE_URL, JOB, build, profile, stubClient);
assertFalse(adapter.fetchReleases().get(0).isLwjgl3());
}
@ParameterizedTest(name = "release after LWJGL v3 is correctly marked - {arguments}")
@MethodSource("firstBuildsForLwjgl3")
void releaseAfterLwjgl3IsCorrectlyMarked(Profile profile, Build build, int buildNumber) {
Jenkins.Build after = gson.fromJson(JenkinsPayload.V1.minimalValidBuildPayload(), Jenkins.Build.class);
after.number = Integer.toString(buildNumber + 1);
Jenkins.ApiResult result = gson.fromJson(JenkinsPayload.V1.minimalValidPayload(), Jenkins.ApiResult.class);
result.builds = new Jenkins.Build[]{after};
final JenkinsClient stubClient = new StubJenkinsClient(url -> result, url -> fail());
final LegacyJenkinsRepositoryAdapter adapter = new LegacyJenkinsRepositoryAdapter(BASE_URL, JOB, build, profile, stubClient);
assertTrue(adapter.fetchReleases().get(0).isLwjgl3());
}
@ParameterizedTest(name = "first release with LWJGL v3 is correctly marked - {arguments}")
@MethodSource("firstBuildsForLwjgl3")
void firstReleaseWithLwjgl3IsCorrectlyMarked(Profile profile, Build build, int buildNumber) {
Jenkins.Build first = gson.fromJson(JenkinsPayload.V1.minimalValidBuildPayload(), Jenkins.Build.class);
first.number = Integer.toString(buildNumber);
Jenkins.ApiResult result = gson.fromJson(JenkinsPayload.V1.minimalValidPayload(), Jenkins.ApiResult.class);
result.builds = new Jenkins.Build[]{first};
final JenkinsClient stubClient = new StubJenkinsClient(url -> result, url -> fail());
final LegacyJenkinsRepositoryAdapter adapter = new LegacyJenkinsRepositoryAdapter(BASE_URL, JOB, build, profile, stubClient);
assertTrue(adapter.fetchReleases().get(0).isLwjgl3());
}
}

View File

@ -14,6 +14,7 @@ import org.terasology.launcher.model.Build;
import org.terasology.launcher.model.GameIdentifier;
import org.terasology.launcher.model.GameRelease;
import org.terasology.launcher.model.Profile;
import org.terasology.launcher.model.ReleaseMetadata;
import java.net.MalformedURLException;
import java.net.URL;
@ -46,7 +47,8 @@ class LegacyJenkinsRepositoryAdapterTest {
try {
final URL expectedArtifactUrl = new URL(validResult.builds[0].url + "artifact/" + validResult.builds[0].artifacts[0].relativePath);
final GameIdentifier id = new GameIdentifier(validResult.builds[0].number, Build.STABLE, Profile.OMEGA);
return new GameRelease(id, expectedArtifactUrl, new ArrayList<>(), new Date(validResult.builds[0].timestamp));
final ReleaseMetadata releaseMetadata = new ReleaseMetadata(new ArrayList<>(), new Date(validResult.builds[0].timestamp), true);
return new GameRelease(id, expectedArtifactUrl, releaseMetadata);
} catch (MalformedURLException e) {
throw new RuntimeException("Error in test setup!");
}