Add mod list troubleshooter skeleton
This commit is contained in:
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -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));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user