Split Getdown into multiple (Maven) modules.

- core: the main Getdown logic code
- tools: code to create digest.txt & patch files
- launcher: the standalone launcher/updater app
- ant: the Ant task for creating digest.txt files

This paves the way for a proper Jigsaw-ification of the Getdown code.

I may further factor code out of getdown-launcher and into getdown-core to
enable the use-case where an app embeds Getdown completely and does not use the
launcher app/UI. That will also make it easier to create a JavaFX UI and retire
the old Swing UI.

This also moves the obsolete applet code into a separate applet module, which
is merely a temporary holding area. It will be deleted in the next commit.
This commit is contained in:
Michael Bayne
2018-09-04 09:11:29 -07:00
parent 8ff9bf4d68
commit 4c383f99c7
63 changed files with 386 additions and 264 deletions
@@ -0,0 +1,49 @@
package com.threerings.getdown.classpath;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.LinkedHashSet;
import org.junit.*;
import org.junit.rules.TemporaryFolder;
import static org.junit.Assert.assertEquals;
/**
* Tests for {@link ClassPath}.
*/
public class ClassPathTest
{
@Before public void createJarsAndSetupClassPath () throws IOException
{
_firstJar = _folder.newFile("a.jar");
_secondJar = _folder.newFile("b.jar");
LinkedHashSet<File> classPathEntries = new LinkedHashSet<File>();
classPathEntries.add(_firstJar);
classPathEntries.add(_secondJar);
_classPath = new ClassPath(classPathEntries);
}
@Test public void shouldCreateValidArgumentString ()
{
assertEquals(
_firstJar.getAbsolutePath() + File.pathSeparator + _secondJar.getAbsolutePath(),
_classPath.asArgumentString());
}
@Test public void shouldProvideJarUrls () throws MalformedURLException, URISyntaxException
{
URL[] actualUrls = _classPath.asUrls();
assertEquals(_firstJar, new File(actualUrls[0].toURI()));
assertEquals(_secondJar, new File(actualUrls[1].toURI()));
}
@Rule public TemporaryFolder _folder = new TemporaryFolder();
private File _firstJar, _secondJar;
private ClassPath _classPath;
}
@@ -0,0 +1,68 @@
package com.threerings.getdown.classpath;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import org.junit.*;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import static org.junit.Assert.assertEquals;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import static org.mockito.Mockito.when;
import com.samskivert.util.FileUtil;
import com.threerings.getdown.data.Application;
import com.threerings.getdown.data.Resource;
@RunWith(MockitoJUnitRunner.class)
public class ClassPathsTest
{
@Before public void setupFilesAndResources () throws IOException
{
_firstJarFile = _appdir.newFile("a.jar");
_secondJarFile = _appdir.newFile("b.jar");
when(_firstJar.getFinalTarget()).thenReturn(_firstJarFile);
when(_secondJar.getFinalTarget()).thenReturn(_secondJarFile);
when(_application.getActiveCodeResources()).thenReturn(Arrays.asList(_firstJar, _secondJar));
when(_application.getAppdir()).thenReturn(_appdir.getRoot());
}
@Test public void shouldBuildDefaultClassPath () throws IOException
{
ClassPath classPath = ClassPaths.buildDefaultClassPath(_application);
String expectedClassPath = _firstJarFile.getAbsolutePath() + File.pathSeparator +
_secondJarFile.getAbsolutePath();
assertEquals(expectedClassPath, classPath.asArgumentString());
}
@Test public void shouldBuildCachedClassPath () throws IOException
{
when(_application.getDigest(_firstJar)).thenReturn("first");
when(_application.getDigest(_secondJar)).thenReturn("second");
when(_application.getCodeCacheRetentionDays()).thenReturn(1);
File firstCachedJarFile = FileUtil.newFile(
_appdir.getRoot(), ClassPaths.CACHE_DIR, "fi", "first.jar");
File secondCachedJarFile = FileUtil.newFile(
_appdir.getRoot(), ClassPaths.CACHE_DIR, "se", "second.jar");
String expectedClassPath = firstCachedJarFile.getAbsolutePath() + File.pathSeparator +
secondCachedJarFile.getAbsolutePath();
ClassPath classPath = ClassPaths.buildCachedClassPath(_application);
assertEquals(expectedClassPath, classPath.asArgumentString());
}
@Mock protected Application _application;
@Mock protected Resource _firstJar;
@Mock protected Resource _secondJar;
protected File _firstJarFile, _secondJarFile;
@Rule public TemporaryFolder _appdir = new TemporaryFolder();
}
@@ -0,0 +1,66 @@
package com.threerings.getdown.classpath.cache;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import org.junit.*;
import org.junit.rules.TemporaryFolder;
import static org.junit.Assert.*;
import static org.junit.Assume.assumeTrue;
/**
* Validates that cache garbage is collected and deleted correctly.
*/
public class GarbageCollectorTest
{
@Before public void setupFiles () throws IOException
{
_cachedFile = _folder.newFile("abc123.jar");
_lastAccessedFile = _folder.newFile("abc123.jar" + ResourceCache.LAST_ACCESSED_FILE_SUFFIX);
}
@Test public void shouldDeleteCacheEntryIfRetentionPeriodIsReached ()
{
gcNow();
assertFalse(_cachedFile.exists());
assertFalse(_lastAccessedFile.exists());
}
@Test public void shouldDeleteCacheFolderIfFolderIsEmpty ()
{
gcNow();
assertFalse(_folder.getRoot().exists());
}
private void gcNow() {
GarbageCollector.collect(_folder.getRoot(), -1);
}
@Test public void shouldKeepFilesInCacheIfRententionPeriodIsNotReached ()
{
GarbageCollector.collect(_folder.getRoot(), TimeUnit.DAYS.toMillis(1));
assertTrue(_cachedFile.exists());
assertTrue(_lastAccessedFile.exists());
}
@Test public void shouldDeleteCachedFileIfLastAccessedFileIsMissing ()
{
assumeTrue(_lastAccessedFile.delete());
gcNow();
assertFalse(_cachedFile.exists());
}
@Test public void shouldDeleteLastAccessedFileIfCachedFileIsMissing ()
{
assumeTrue(_cachedFile.delete());
gcNow();
assertFalse(_lastAccessedFile.exists());
}
@Rule public TemporaryFolder _folder = new TemporaryFolder();
private File _cachedFile;
private File _lastAccessedFile;
}
@@ -0,0 +1,67 @@
package com.threerings.getdown.classpath.cache;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import org.junit.*;
import org.junit.rules.TemporaryFolder;
import static org.junit.Assert.*;
/**
* Asserts the correct functionality of the {@link ResourceCache}.
*/
public class ResourceCacheTest
{
@Before public void setupCache () throws IOException {
_fileToCache = _folder.newFile("filetocache.jar");
_cache = new ResourceCache(_folder.newFolder(".cache"));
}
@Test public void shouldCacheFile () throws IOException
{
assertEquals("abc123.jar", cacheFile().getName());
}
private File cacheFile() throws IOException
{
return _cache.cacheFile(_fileToCache, "abc123");
}
@Test public void shouldTrackFileUsage () throws IOException
{
String name = "abc123.jar" + ResourceCache.LAST_ACCESSED_FILE_SUFFIX;
File lastAccessedFile = new File(cacheFile().getParentFile(), name);
assertTrue(lastAccessedFile.exists());
}
@Test public void shouldNotCacheTheSameFile () throws Exception
{
File cachedFile = cacheFile();
cachedFile.setLastModified(YESTERDAY);
long expectedLastModified = cachedFile.lastModified();
// caching it another time
File sameCachedFile = cacheFile();
assertEquals(expectedLastModified, sameCachedFile.lastModified());
}
@Test public void shouldRememberWhenFileWasRequested () throws Exception
{
File cachedFile = cacheFile();
String name = cachedFile.getName() + ResourceCache.LAST_ACCESSED_FILE_SUFFIX;
File lastAccessedFile = new File(cachedFile.getParentFile(), name);
lastAccessedFile.setLastModified(YESTERDAY);
long lastAccessed = lastAccessedFile.lastModified();
// caching it another time
cacheFile();
assertTrue(lastAccessedFile.lastModified() > lastAccessed);
}
@Rule public TemporaryFolder _folder = new TemporaryFolder();
private File _fileToCache;
private ResourceCache _cache;
private static final long YESTERDAY = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1);
}
@@ -0,0 +1,42 @@
//
// Getdown - application installer, patcher and launcher
// Copyright (C) 2004-2016 Getdown authors
// https://github.com/threerings/getdown/blob/master/LICENSE
package com.threerings.getdown.data;
import org.junit.*;
import static org.junit.Assert.*;
public class SysPropsTest {
@After public void clearProps () {
System.clearProperty("appbase_domain");
System.clearProperty("appbase_override");
}
public static String[] APPBASES = {
"http://foobar.com/myapp",
"https://foobar.com/myapp",
"http://foobar.com:8080/myapp",
"https://foobar.com:8080/myapp"
};
@Test public void testAppbaseDomain () {
System.setProperty("appbase_domain", "https://barbaz.com");
for (String appbase : APPBASES) {
assertEquals("https://barbaz.com/myapp", SysProps.overrideAppbase(appbase));
}
System.setProperty("appbase_domain", "http://barbaz.com");
for (String appbase : APPBASES) {
assertEquals("http://barbaz.com/myapp", SysProps.overrideAppbase(appbase));
}
}
@Test public void testAppbaseOverride () {
System.setProperty("appbase_override", "https://barbaz.com/newapp");
for (String appbase : APPBASES) {
assertEquals("https://barbaz.com/newapp", SysProps.overrideAppbase(appbase));
}
}
}
@@ -0,0 +1,170 @@
//
// Getdown - application installer, patcher and launcher
// Copyright (C) 2004-2016 Getdown authors
// https://github.com/threerings/getdown/blob/master/LICENSE
package com.threerings.getdown.util;
import java.io.IOException;
import java.io.StringReader;
import java.util.List;
import com.samskivert.util.RandomUtil;
import org.junit.*;
import static org.junit.Assert.*;
/**
* Tests {@link Config}.
*/
public class ConfigTest
{
public static class Pair {
public final String key;
public final String value;
public Pair (String key, String value) {
this.key = key;
this.value = value;
}
}
public static final Pair[] SIMPLE_PAIRS = {
new Pair("one", "two"),
new Pair("three", "four"),
new Pair("five", "six"),
new Pair("seven", "eight"),
new Pair("nine", "ten"),
};
@Test public void testSimplePairs () throws IOException
{
List<String[]> pairs = Config.parsePairs(
toReader(SIMPLE_PAIRS), Config.createOpts(true));
for (int ii = 0; ii < SIMPLE_PAIRS.length; ii++) {
assertEquals(SIMPLE_PAIRS[ii].key, pairs.get(ii)[0]);
assertEquals(SIMPLE_PAIRS[ii].value, pairs.get(ii)[1]);
}
}
@Test public void testQualifiedPairs () throws IOException
{
Pair linux = new Pair("one", "[linux] two");
Pair mac = new Pair("three", "[mac os x] four");
Pair linuxAndMac = new Pair("five", "[linux, mac os x] six");
Pair linux64 = new Pair("seven", "[linux-x86_64] eight");
Pair linux64s = new Pair("nine", "[linux-x86_64, linux-amd64] ten");
Pair mac64 = new Pair("eleven", "[mac os x-x86_64] twelve");
Pair win64 = new Pair("thirteen", "[windows-x86_64] fourteen");
Pair notWin = new Pair("fifteen", "[!windows] sixteen");
Pair[] pairs = { linux, mac, linuxAndMac, linux64, linux64s, mac64, win64, notWin };
Config.ParseOpts opts = Config.createOpts(false);
opts.osname = "linux";
opts.osarch = "i386";
List<String[]> parsed = Config.parsePairs(toReader(pairs), opts);
assertTrue(exists(parsed, linux.key));
assertTrue(!exists(parsed, mac.key));
assertTrue(exists(parsed, linuxAndMac.key));
assertTrue(!exists(parsed, linux64.key));
assertTrue(!exists(parsed, linux64s.key));
assertTrue(!exists(parsed, mac64.key));
assertTrue(!exists(parsed, win64.key));
assertTrue(exists(parsed, notWin.key));
opts.osarch = "x86_64";
parsed = Config.parsePairs(toReader(pairs), opts);
assertTrue(exists(parsed, linux.key));
assertTrue(!exists(parsed, mac.key));
assertTrue(exists(parsed, linuxAndMac.key));
assertTrue(exists(parsed, linux64.key));
assertTrue(exists(parsed, linux64s.key));
assertTrue(!exists(parsed, mac64.key));
assertTrue(!exists(parsed, win64.key));
assertTrue(exists(parsed, notWin.key));
opts.osarch = "amd64";
parsed = Config.parsePairs(toReader(pairs), opts);
assertTrue(exists(parsed, linux.key));
assertTrue(!exists(parsed, mac.key));
assertTrue(exists(parsed, linuxAndMac.key));
assertTrue(!exists(parsed, linux64.key));
assertTrue(exists(parsed, linux64s.key));
assertTrue(!exists(parsed, mac64.key));
assertTrue(!exists(parsed, win64.key));
assertTrue(exists(parsed, notWin.key));
opts.osname = "mac os x";
opts.osarch = "x86_64";
parsed = Config.parsePairs(toReader(pairs), opts);
assertTrue(!exists(parsed, linux.key));
assertTrue(exists(parsed, mac.key));
assertTrue(exists(parsed, linuxAndMac.key));
assertTrue(!exists(parsed, linux64.key));
assertTrue(!exists(parsed, linux64s.key));
assertTrue(exists(parsed, mac64.key));
assertTrue(!exists(parsed, win64.key));
assertTrue(exists(parsed, notWin.key));
opts.osname = "windows";
opts.osarch = "i386";
parsed = Config.parsePairs(toReader(pairs), opts);
assertTrue(!exists(parsed, linux.key));
assertTrue(!exists(parsed, mac.key));
assertTrue(!exists(parsed, linuxAndMac.key));
assertTrue(!exists(parsed, linux64.key));
assertTrue(!exists(parsed, linux64s.key));
assertTrue(!exists(parsed, mac64.key));
assertTrue(!exists(parsed, win64.key));
assertTrue(!exists(parsed, notWin.key));
opts.osarch = "x86_64";
parsed = Config.parsePairs(toReader(pairs), opts);
assertTrue(!exists(parsed, linux.key));
assertTrue(!exists(parsed, mac.key));
assertTrue(!exists(parsed, linuxAndMac.key));
assertTrue(!exists(parsed, linux64.key));
assertTrue(!exists(parsed, linux64s.key));
assertTrue(!exists(parsed, mac64.key));
assertTrue(exists(parsed, win64.key));
assertTrue(!exists(parsed, notWin.key));
opts.osarch = "amd64";
parsed = Config.parsePairs(toReader(pairs), opts);
assertTrue(!exists(parsed, linux.key));
assertTrue(!exists(parsed, mac.key));
assertTrue(!exists(parsed, linuxAndMac.key));
assertTrue(!exists(parsed, linux64.key));
assertTrue(!exists(parsed, linux64s.key));
assertTrue(!exists(parsed, mac64.key));
assertTrue(!exists(parsed, win64.key));
assertTrue(!exists(parsed, notWin.key));
}
protected static boolean exists (List<String[]> pairs, String key)
{
for (String[] pair : pairs) {
if (pair[0].equals(key)) {
return true;
}
}
return false;
}
protected static StringReader toReader (Pair[] pairs)
{
StringBuilder builder = new StringBuilder();
for (Pair pair : pairs) {
// throw some whitespace in to ensure it's trimmed
builder.append(whitespace()).append(pair.key).
append(whitespace()).append("=").
append(whitespace()).append(pair.value).
append(whitespace()).append("\n");
}
return new StringReader(builder.toString());
}
protected static String whitespace ()
{
return RandomUtil.getBoolean() ? " " : "";
}
}
@@ -0,0 +1,60 @@
//
// Getdown - application installer, patcher and launcher
// Copyright (C) 2004-2016 Getdown authors
// https://github.com/threerings/getdown/blob/master/LICENSE
package com.threerings.getdown.util;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.util.List;
import org.junit.*;
import org.junit.rules.TemporaryFolder;
import static org.junit.Assert.*;
/**
* Tests {@link FileUtil}.
*/
public class FileUtilTest
{
@Test public void testReadLines () throws IOException
{
String data = "This is a test\nof a file with\na few lines\n";
List<String> lines = FileUtil.readLines(new StringReader(data));
String[] linesBySplit = data.split("\n");
assertEquals(linesBySplit.length, lines.size());
for (int ii = 0; ii < lines.size(); ii++) {
assertEquals(linesBySplit[ii], lines.get(ii));
}
}
@Test public void shouldCopyFile () throws IOException
{
File source = _folder.newFile("source.txt");
File target = new File(_folder.getRoot(), "target.txt");
assertFalse(target.exists());
FileUtil.copy(source, target);
assertTrue(target.exists());
}
@Test public void shouldRecursivelyWalkOverFilesAndFolders () throws IOException
{
_folder.newFile("a.txt");
new File(_folder.newFolder("b"), "b.txt").createNewFile();
class CountingVisitor implements FileUtil.Visitor {
int fileCount = 0;
@Override public void visit(File file) {
fileCount++;
}
}
CountingVisitor visitor = new CountingVisitor();
FileUtil.walkTree(_folder.getRoot(), visitor);
assertEquals(3, visitor.fileCount);
}
@Rule public TemporaryFolder _folder = new TemporaryFolder();
}
@@ -0,0 +1,53 @@
//
// Getdown - application installer, patcher and launcher
// Copyright (C) 2004-2016 Getdown authors
// https://github.com/threerings/getdown/blob/master/LICENSE
package com.threerings.getdown.util;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class VersionUtilTest {
@Test
public void shouldParseJavaVersion ()
{
long version = VersionUtil.parseJavaVersion(
"(\\d+)(?:\\.(\\d+)(?:\\.(\\d+)(_\\d+)?)?)?", "1.8.0_152");
assertEquals(1_080_152, version);
}
@Test
public void shouldParseJavaVersion8 ()
{
long version = VersionUtil.parseJavaVersion(
"(\\d+)(?:\\.(\\d+)(?:\\.(\\d+)(_\\d+)?)?)?", "1.8");
assertEquals(1_080_000, version);
}
@Test
public void shouldParseJavaVersion9 ()
{
long version = VersionUtil.parseJavaVersion(
"(\\d+)(?:\\.(\\d+)(?:\\.(\\d+)(_\\d+)?)?)?", "9");
assertEquals(9_000_000, version);
}
@Test
public void shouldParseJavaVersion10 ()
{
long version = VersionUtil.parseJavaVersion(
"(\\d+)(?:\\.(\\d+)(?:\\.(\\d+)(_\\d+)?)?)?", "10");
assertEquals(10_000_000, version);
}
@Test
public void shouldParseJavaRuntimeVersion ()
{
long version = VersionUtil.parseJavaVersion(
"(\\d+)\\.(\\d+)\\.(\\d+)(_\\d+)?(-b\\d+)?", "1.8.0_131-b11");
assertEquals(108_013_111, version);
}
}