From 24464d48fc150a4ffd167424d0e7d0efe092e093 Mon Sep 17 00:00:00 2001 From: Ryan Barlow <7389646+ryanbarlow97@users.noreply.github.com> Date: Tue, 13 Oct 2026 14:57:33 +0000 Subject: [PATCH] fix: load Research config files in name order YamlFolder returned files in filesystem order, so when two files in a folder defined the same id, which one was kept depended on the machine. Sort by file name (ignoring case) so the first file by name always wins. This also makes ResearchLoadersTest.templatesValidateNestedPathsCyclesDuplicatesAndWeights pass on every filesystem. Co-Authored-By: Claude Opus 5.5 --- .../tfminecraft/research/util/YamlFolder.java | 6 +++++ .../research/util/ResearchUtilitiesTest.java | 22 ++++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/tfminecraft/research/util/YamlFolder.java b/src/main/java/net/tfminecraft/research/util/YamlFolder.java index 87bfb62..283229d 100644 --- a/src/main/java/net/tfminecraft/research/util/YamlFolder.java +++ b/src/main/java/net/tfminecraft/research/util/YamlFolder.java @@ -3,10 +3,15 @@ import java.io.File; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.List; public final class YamlFolder { + // Sorted so that duplicate ids resolve the same way on every filesystem: the first file by name wins. + private static final Comparator BY_NAME = Comparator.comparing(File::getName, String.CASE_INSENSITIVE_ORDER) + .thenComparing(File::getName); + private YamlFolder() {} public static List listYamlFiles(File folder) { @@ -23,6 +28,7 @@ public static List listYamlFiles(File folder) { yamlFiles.add(file); } } + yamlFiles.sort(BY_NAME); return yamlFiles; } } diff --git a/src/test/java/net/tfminecraft/research/util/ResearchUtilitiesTest.java b/src/test/java/net/tfminecraft/research/util/ResearchUtilitiesTest.java index 63ce3f1..597d65c 100644 --- a/src/test/java/net/tfminecraft/research/util/ResearchUtilitiesTest.java +++ b/src/test/java/net/tfminecraft/research/util/ResearchUtilitiesTest.java @@ -422,7 +422,7 @@ void yamlFilesAreSelectedOnlyFromReadableDirectories() throws Exception { Files.writeString(directory.resolve("other.txt"), "text"); Files.createDirectory(directory.resolve("folder.yml")); assertEquals(List.of(), YamlFolder.listYamlFiles(lower.toFile())); - assertEquals(Set.of(lower.toFile(), upper.toFile()), new HashSet<>(YamlFolder.listYamlFiles(directory.toFile()))); + assertEquals(List.of(lower.toFile(), upper.toFile()), YamlFolder.listYamlFiles(directory.toFile())); File unreadable = mock(File.class); when(unreadable.exists()).thenReturn(true); when(unreadable.isDirectory()).thenReturn(true); @@ -430,6 +430,26 @@ void yamlFilesAreSelectedOnlyFromReadableDirectories() throws Exception { assertEquals(List.of(), YamlFolder.listYamlFiles(unreadable)); } + @Test + void yamlFilesAreSortedByNameWhateverOrderTheFilesystemLists() { + File folder = mock(File.class); + when(folder.exists()).thenReturn(true); + when(folder.isDirectory()).thenReturn(true); + File b = yamlFile("b.yml"); + File upperA = yamlFile("A.yml"); + File lowerA = yamlFile("a.yml"); + File c = yamlFile("C.yml"); + when(folder.listFiles()).thenReturn(new File[] {c, lowerA, b, upperA}); + assertEquals(List.of(upperA, lowerA, b, c), YamlFolder.listYamlFiles(folder)); + } + + private static File yamlFile(String name) { + File file = mock(File.class); + when(file.isFile()).thenReturn(true); + when(file.getName()).thenReturn(name); + return file; + } + @Test void utilityClassesExposeNoPublicConstructors() throws Exception { for (Class type : List.of(GridLayout.class, AspectProgressLayout.class, AspectSlotOrder.class,