Add mod list troubleshooter skeleton

This commit is contained in:
2025-03-16 22:14:58 +01:00
parent 2a7b3f8345
commit 4d2b676c8b
2 changed files with 110 additions and 2 deletions

View File

@@ -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();
}
}

View File

@@ -4,9 +4,9 @@
// A) a binary search / bisect algorithm to find the minimum set of mods // A) a binary search / bisect algorithm to find the minimum set of mods
// that exhibit a bug // that exhibit a bug
// B) a linear search / batching algorithm for the same purpose // 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 // 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 // time to load a lot of mods
// So say it takes us 30 minutes to load 300 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 // 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 // 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 // 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) // 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));
},
);
});
}