From 4d2b676c8b34306886ed9c9b7eb7a5b7726ab0c3 Mon Sep 17 00:00:00 2001 From: PhatPhuckDave Date: Sun, 16 Mar 2025 22:14:58 +0100 Subject: [PATCH] Add mod list troubleshooter skeleton --- lib/mod_list_troubleshooter.dart | 42 ++++++++++++++++ test/mod_list_troubleshooter_test.dart | 70 +++++++++++++++++++++++++- 2 files changed, 110 insertions(+), 2 deletions(-) diff --git a/lib/mod_list_troubleshooter.dart b/lib/mod_list_troubleshooter.dart index e69de29..ab8d2d2 100644 --- a/lib/mod_list_troubleshooter.dart +++ b/lib/mod_list_troubleshooter.dart @@ -0,0 +1,42 @@ +import 'dart:async'; + +import 'package:rimworld_modman/logger.dart'; +import 'package:rimworld_modman/mod.dart'; +import 'package:rimworld_modman/mod_list.dart'; + +/// A class that helps find the minimum set of mods that exhibit a bug. +/// +/// Provides two main algorithms: +/// - Binary search / bisect: Divides mods into smaller subsets to find problematic ones quickly. +/// - Linear search / batching: Tests mods in small groups to systematically identify issues. +/// +/// These approaches help RimWorld mod users identify which mods are causing problems +/// when many mods are installed. +class ModListTroubleshooter { + final ModList originalModList; + ModList currentModList; + + ModListTroubleshooter(ModList modList) + : originalModList = modList, + currentModList = modList.copyWith(); + + ModList binaryForward() { + return currentModList; + } + + ModList binaryBackward() { + return currentModList; + } + + ModList linearForward() { + return currentModList; + } + + ModList linearBackward() { + return currentModList; + } + + void reset() { + currentModList = originalModList.copyWith(); + } +} diff --git a/test/mod_list_troubleshooter_test.dart b/test/mod_list_troubleshooter_test.dart index 6be6453..f3401c8 100644 --- a/test/mod_list_troubleshooter_test.dart +++ b/test/mod_list_troubleshooter_test.dart @@ -4,9 +4,9 @@ // A) a binary search / bisect algorithm to find the minimum set of mods // that exhibit a bug // B) a linear search / batching algorithm for the same purpose -// Why both? I think B will be most useful most often but A theoretically +// Why both? I think B will be most useful most often but A theoretically // should be faster -// Why I think A might not always be faster is because it takes us a very long +// Why I think A might not always be faster is because it takes us a very long // time to load a lot of mods // So say it takes us 30 minutes to load 300 mods // Via bisect we would be loading 30 + 15 + 7.5 + ... = some 50 minutes @@ -24,3 +24,69 @@ // And we should keep a registry of tested (say Good) mods and ones we haven't gotten to yet // Maybe even make sure each batch contains N untested mods // And that we don't test the same mod twice (unless it's a library) + +import 'package:flutter_test/flutter_test.dart'; +import 'package:rimworld_modman/mod.dart'; +import 'package:rimworld_modman/mod_list.dart'; +import 'package:rimworld_modman/mod_list_troubleshooter.dart'; + +Mod makeDummy() { + return Mod( + name: 'Dummy Mod', + id: 'dummy', + path: '', + versions: ["1.5"], + description: '', + dependencies: [], + loadAfter: [], + loadBefore: [], + incompatibilities: [], + size: 0, + isBaseGame: false, + isExpansion: false, + enabled: false, + ); +} + +void main() { + group('ModListTroubleshooter Bisect Tests', () { + late ModList modList; + setUp(() { + modList = ModList(); + + // Add some base mods + for (int i = 0; i < 20; i++) { + final modId = 'test.mod$i'; + final mod = makeDummy().copyWith(name: 'Test Mod $i', id: modId); + modList.mods[modId] = mod; + } + + // Add some mods with dependencies + for (int i = 20; i < 30; i++) { + final modId = 'test.mod$i'; + final mod = makeDummy().copyWith( + name: 'Test Mod $i', + id: modId, + dependencies: ['test.mod${i - 20}'], // Depend on earlier mods + ); + modList.mods[modId] = mod; + } + }); + modList.enableAll(); + + test( + 'Bisect search should end up with half the mods every iteration until 1', + () { + final troubleshooter = ModListTroubleshooter(modList); + + final result = troubleshooter.binaryForward(); + // Half of our initial 30 + expect(result.activeMods.length, equals(15)); + + final result2 = troubleshooter.binaryBackward(); + // Half of our previous result + expect(result2.activeMods.length, equals(7)); + }, + ); + }); +}