| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127 |
- package au.pfip;
- import com.sun.net.httpserver.HttpServer;
- import java.net.InetSocketAddress;
- import java.nio.charset.StandardCharsets;
- import java.util.*;
- public class Harness {
- static int failures = 0;
- static void check(String name, boolean ok) {
- System.out.println((ok ? "PASS " : "FAIL ") + name);
- if (!ok) failures++;
- }
- public static void main(String[] a) throws Exception {
- String me = System.getProperty("user.name");
- // --- user parsing
- check("parseUsers trims/dedupes/filters",
- Arrays.equals(Main.parseUsers(" alice, bob ,alice,,bad name;rm,$(x)"), new String[]{"alice","bob"}));
- check("parseUsers empty", Main.parseUsers("").length == 0);
- // --- games json parsing
- List<String> g1 = communications.parseUniqueValues("[\"minecraft\",\"steam\",\"minecraft\",\"bad;name\"]");
- check("games array deduped + bad name dropped", g1.equals(List.of("minecraft","steam")));
- List<String> g2 = communications.parseUniqueValues("{\"a\":\"roblox\",\"b\":\"fortnite\"}");
- check("games object values", g2.equals(List.of("roblox","fortnite")));
- // --- Shell
- List<String> echo = Shell.run(5, "echo", "hi $(whoami) \"q\"");
- check("Shell.run passes text literally (no shell)", echo != null && echo.get(0).equals("hi $(whoami) \"q\""));
- long t0 = System.currentTimeMillis();
- check("Shell.run times out", Shell.run(1, "sleep", "30") == null && System.currentTimeMillis() - t0 < 5000);
- check("Shell.run non-zero exit -> null", Shell.run(5, "false") == null);
- // --- Processes
- int n = 40000 + new Random().nextInt(9000);
- Process p1 = new ProcessBuilder("sleep", String.valueOf(n)).start();
- Process p2 = new ProcessBuilder("sleep", String.valueOf(n + 1)).start();
- Thread.sleep(500);
- List<Processes.Proc> snap = Processes.snapshot(me);
- check("snapshot finds my process", Processes.matching(snap, "sleep " + n).size() == 1);
- check("snapshot excludes other text", Processes.matching(snap, "sleep " + (n + 5000)).isEmpty());
- check("terminate returns true", Processes.terminate(me, "sleep " + n));
- p1.waitFor();
- check("target process is dead", !p1.isAlive());
- check("other process untouched", p2.isAlive());
- p2.destroyForcibly();
- // a process that ignores SIGTERM must be force-killed
- Process stubborn = new ProcessBuilder("bash", "-c", "trap '' TERM; exec -a stubborn-game-" + (n + 7) + " sleep 600").start();
- Thread.sleep(700);
- String stubName = "stubborn-game-" + (n + 7);
- check("stubborn process found", Processes.matching(Processes.snapshot(me), stubName).size() == 1);
- check("stubborn (ignores SIGTERM) is force-killed", Processes.terminate(me, stubName) && !stubborn.isAlive());
- // --- whole-word matching
- ProcessHandle dummy = ProcessHandle.current();
- java.util.function.BiPredicate<String, String> hit = (cmd, game) ->
- !Processes.matching(List.of(new Processes.Proc(dummy, cmd)), game).isEmpty();
- check("technic matches ~/.technic path", hit.test("java -jar /home/x/.technic/launcher.jar", "technic"));
- check("technic does NOT match technical-report", !hit.test("libreoffice /home/x/technical-report.odt", "technic"));
- check("opera matches /usr/lib/opera/opera", hit.test("/usr/lib/opera/opera --type=renderer", "opera"));
- check("opera does NOT match operate/cooperative", !hit.test("tool --operate cooperative.txt", "opera"));
- check("minecraft matches minecraft-launcher", hit.test("/opt/minecraft-launcher/minecraft-launcher", "minecraft"));
- check("minecraft matches -Dminecraft.launcher", hit.test("java -Dminecraft.launcher.brand=x -jar y", "minecraft"));
- check("wine exe matches", hit.test("wine Z:\\games\\bf2.exe", "bf2.exe"));
- check("underscore names match", hit.test("/x/hl2_linux -game hl2", "hl2_linux"));
- check("firefox-esr matches firefox", hit.test("/usr/lib/firefox-esr/firefox-esr", "firefox"));
- settings.addSetting("MatchWholeWords", "false");
- check("MatchWholeWords=false is plain contains", hit.test("libreoffice /home/x/technical-report.odt", "technic"));
- settings.addSetting("MatchWholeWords", "true");
- // --- ignore list
- Process kiosk = new ProcessBuilder("bash", "-c", "exec -a 'kgame-" + (n + 11) + " --kiosk' sleep 600").start();
- Thread.sleep(500);
- String kName = "kgame-" + (n + 11);
- check("process found without ignore list", Processes.matching(Processes.snapshot(me), kName).size() == 1);
- settings.addSetting("IgnoreCommandLines", "--kiosk, -contentproc");
- check("process ignored with IgnoreCommandLines=--kiosk", Processes.matching(Processes.snapshot(me), kName).isEmpty());
- settings.addSetting("IgnoreCommandLines", "");
- kiosk.destroyForcibly();
- // --- communications against a local fake server
- HttpServer server = HttpServer.create(new InetSocketAddress("127.0.0.1", 0), 0);
- final String[] body = {"12"}; final int[] status = {200};
- server.createContext("/", ex -> {
- byte[] b = body[0].getBytes(StandardCharsets.UTF_8);
- ex.sendResponseHeaders(status[0], b.length);
- ex.getResponseBody().write(b); ex.close();
- });
- server.start();
- int port = server.getAddress().getPort();
- Main.PlaytimeURL = "http://127.0.0.1:" + port + "/timer.php";
- Main.GamesURL = "http://127.0.0.1:" + port + "/games.json";
- body[0] = "12"; check("numeric reply", communications.getPlaytimeFromServer(me, "g") == 12);
- body[0] = "0"; check("zero reply", communications.getPlaytimeFromServer(me, "g") == 0);
- body[0] = "1440"; check("1440 reply", communications.getPlaytimeFromServer(me, "g") == 1440);
- body[0] = ""; check("empty reply -> UNKNOWN", communications.getPlaytimeFromServer(me, "g") == communications.UNKNOWN);
- body[0] = "<b>Fatal error</b> in \"x\" `id`"; check("html reply -> UNKNOWN", communications.getPlaytimeFromServer(me, "g") == communications.UNKNOWN);
- status[0] = 500; body[0] = "5"; check("HTTP 500 -> UNKNOWN", communications.getPlaytimeFromServer(me, "g") == communications.UNKNOWN);
- status[0] = 200; body[0] = "Max play time exceeded."; check("text reply -> 0", communications.getPlaytimeFromServer(me, "g") == 0);
- body[0] = "x\"; touch /tmp/pwned; \""; check("hostile text reply -> 0, no command run", communications.getPlaytimeFromServer(me, "g") == 0 && !new java.io.File("/tmp/pwned").exists());
- // games list: good, then server error keeps old list, empty answer keeps old list
- status[0] = 200; body[0] = "[\"minecraft\",\"steam\"]";
- check("games fetched", communications.getGamesListFromServer() && Main.GamesList.length == 2);
- status[0] = 500;
- check("server error keeps list", !communications.getGamesListFromServer() && Main.GamesList.length == 2);
- status[0] = 200; body[0] = "[]";
- check("empty answer keeps list", !communications.getGamesListFromServer() && Main.GamesList.length == 2);
- Main.GamesList = new String[0];
- status[0] = 500;
- communications.getGamesListFromServer();
- check("offline start uses cache", Main.GamesList.length == 2);
- // unreachable server
- server.stop(0);
- long t1 = System.currentTimeMillis();
- check("unreachable -> UNKNOWN fast", communications.getPlaytimeFromServer(me, "g") == communications.UNKNOWN && System.currentTimeMillis() - t1 < 15000);
- stubborn.destroyForcibly();
- System.out.println(failures == 0 ? "ALL PASSED" : failures + " FAILED");
- System.exit(failures == 0 ? 0 : 1);
- }
- }
|