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
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,20 @@ public enum KEY {
public GlobalProperties() {
String propsUrl = "/crashreporter.properties";
String defaultPropsUrl = "/crashreporter_defaults.properties";
try (InputStream stream = CrashReporter.class.getResourceAsStream(defaultPropsUrl)) {
properties.load(stream);
} catch (IOException e) {
// this should never go wrong
System.err.println("Unable to load default properties");
}
try (InputStream stream = CrashReporter.class.getResourceAsStream(propsUrl)) {
properties.load(stream);
loadIfPresent(defaultPropsUrl);
// Only cr-core's downstream consumers (cr-terasology, cr-destsol, ...) ship this file -
// it's absent when cr-core is used standalone, which getResourceAsStream signals with
// null rather than an IOException, so that has to be checked explicitly.
loadIfPresent(propsUrl);
}

private void loadIfPresent(String resourceUrl) {
try (InputStream stream = CrashReporter.class.getResourceAsStream(resourceUrl)) {
if (stream != null) {
properties.load(stream);
}
} catch (IOException e) {
System.err.println("Unable to load " + propsUrl);
System.err.println("Unable to load " + resourceUrl);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.List;
Expand Down Expand Up @@ -163,24 +162,24 @@ else if (evt.getPropertyName() == LogUpdateWorker.MODIFIED) {
logUpdateWorker.execute();
}

/**
* Alphabetical by filename, not creation time (see #53 item 1): creation-time order looked
* arbitrary to users - two log files from one session sort as "newest first", which is
* neither the order they were written in nor the order their names suggest. Alphabetical is
* deterministic and, for Terasology's own naming (e.g. {@code Terasology-init.log} before
* {@code Terasology-menu.log}), happens to match session order too.
*/
private static void sortLogFiles(List<Path> files) {
files.sort(new Comparator<Path>() {

@Override
public int compare(Path p0, Path p1) {
try {
BasicFileAttributes attr0 = Files.readAttributes(p0, BasicFileAttributes.class);
BasicFileAttributes attr1 = Files.readAttributes(p1, BasicFileAttributes.class);
FileTime time0 = attr0.creationTime();
FileTime time1 = attr1.creationTime();
return time0.compareTo(time1);
} catch (Exception e) {
// ignore silently
return 0;
}
String name0 = p0.getFileName().toString();
String name1 = p1.getFileName().toString();
return name0.compareToIgnoreCase(name1);
}

}.reversed()); // invert sort order
});
}

@Override
Expand Down Expand Up @@ -246,6 +245,18 @@ public Path getLogFile() {
return idx >= 0 ? logFiles.get(idx) : null;
}

/**
* @return the tab titles (filenames relative to the log folder) in the order they're
* displayed - see {@link #sortLogFiles}.
*/
public List<String> getTabTitles() {
List<String> titles = Lists.newArrayListWithCapacity(tabPane.getTabCount());
for (int i = 0; i < tabPane.getTabCount(); i++) {
titles.add(tabPane.getTitleAt(i));
}
return titles;
}

private static String readLogFileContent(Path logFile) {
StringBuilder builder = new StringBuilder();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright 2026 The Terasology Foundation
// SPDX-License-Identifier: Apache-2.0

package org.terasology.crashreporter.pages;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.terasology.crashreporter.CrashReporter;
import org.terasology.crashreporter.GlobalProperties;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;

/**
* Regression test for #53 item 1: log tabs sorted by file creation time (newest first) instead of
* alphabetically, which read as arbitrary/broken with more than one log file present.
*/
class ErrorMessagePanelTabOrderTest {

@Test
void tabsAreOrderedAlphabeticallyRegardlessOfCreationOrder(@TempDir Path logFolder) throws IOException, InterruptedException {
// Written out of alphabetical order, with a real gap between creation times so a
// creation-time-based sort (the old behavior) would disagree with alphabetical order.
writeLog(logFolder, "Terasology-menu.log", "MENU");
Thread.sleep(10);
writeLog(logFolder, "Terasology-init.log", "INIT");

ErrorMessagePanel panel = new ErrorMessagePanel(new GlobalProperties(), new RuntimeException("boom"),
logFolder, CrashReporter.MODE.CRASH_REPORTER);

List<String> titles = panel.getTabTitles();
assertEquals(Arrays.asList("Terasology-init.log", "Terasology-menu.log"), titles,
"Expected tabs in alphabetical order regardless of which file was created first, got: " + titles);
}

private static void writeLog(Path folder, String name, String content) throws IOException {
Files.write(folder.resolve(name), content.getBytes(StandardCharsets.UTF_8));
}
}