diff --git a/lib/mod_list_troubleshooter.dart b/lib/mod_list_troubleshooter.dart index fb343cb..3a0e676 100644 --- a/lib/mod_list_troubleshooter.dart +++ b/lib/mod_list_troubleshooter.dart @@ -49,27 +49,54 @@ class ModListTroubleshooter { } ModList linearForward({int stepSize = 20}) { - _endIndex = _startIndex + stepSize; + final totalMods = originalModList.activeMods.length; + + if (_startIndex >= totalMods || _startIndex == _endIndex) { + int newStart = totalMods - stepSize; + if (newStart < 0) { + newStart = 0; + } + _startIndex = newStart; + } + + _startIndex = _startIndex.clamp(0, totalMods); + _endIndex = (_startIndex + stepSize).clamp(0, totalMods); + final subset = originalModList.activeMods.keys.toList().sublist( _startIndex, _endIndex, ); + currentModList.disableAll(); currentModList.enableMods(subset); _startIndex = _endIndex; + return currentModList; } ModList linearBackward({int stepSize = 20}) { - _startIndex = _endIndex - stepSize; - _endIndex = _startIndex; + final totalMods = originalModList.activeMods.length; + + if (_endIndex <= 0 || _startIndex == _endIndex) { + int newEnd = stepSize; + if (newEnd > totalMods) { + newEnd = totalMods; + } + _endIndex = newEnd; + } + + _endIndex = _endIndex.clamp(0, totalMods); + _startIndex = (_endIndex - stepSize).clamp(0, _endIndex); + final subset = originalModList.activeMods.keys.toList().sublist( _startIndex, _endIndex, ); + currentModList.disableAll(); currentModList.enableMods(subset); _endIndex = _startIndex; + return currentModList; } diff --git a/test/mod_list_troubleshooter_test.dart b/test/mod_list_troubleshooter_test.dart index fcf3392..1e4ac91 100644 --- a/test/mod_list_troubleshooter_test.dart +++ b/test/mod_list_troubleshooter_test.dart @@ -151,13 +151,52 @@ void main() { result = troubleshooter.binaryForward(); expect(result.activeMods.length, equals(2)); - expect(result.activeMods.keys.first, equals('test.mod9')); + expect(result.activeMods.keys.first, equals('test.mod9')); result = troubleshooter.binaryBackward(); expect(result.activeMods.length, equals(1)); expect(result.activeMods.keys.last, equals('test.mod9')); }, ); + test('Bisect should handle abuse gracefully', () { + final troubleshooter = ModListTroubleshooter(modList); + + var result = troubleshooter.binaryBackward(); + expect(result.activeMods.length, equals(15)); + + result = troubleshooter.binaryForward(); + expect(result.activeMods.length, equals(8)); + + result = troubleshooter.binaryBackward(); + expect(result.activeMods.length, equals(4)); + + result = troubleshooter.binaryForward(); + expect(result.activeMods.length, equals(2)); + + result = troubleshooter.binaryBackward(); + expect(result.activeMods.length, equals(1)); + expect(result.activeMods.keys.first, equals('test.mod9')); + + result = troubleshooter.binaryForward(); + expect(result.activeMods.length, equals(1)); + expect(result.activeMods.keys.first, equals('test.mod9')); + + result = troubleshooter.binaryBackward(); + expect(result.activeMods.length, equals(1)); + expect(result.activeMods.keys.first, equals('test.mod9')); + + result = troubleshooter.binaryForward(); + expect(result.activeMods.length, equals(1)); + expect(result.activeMods.keys.first, equals('test.mod9')); + + result = troubleshooter.binaryBackward(); + expect(result.activeMods.length, equals(1)); + expect(result.activeMods.keys.first, equals('test.mod9')); + + result = troubleshooter.binaryForward(); + expect(result.activeMods.length, equals(1)); + expect(result.activeMods.keys.first, equals('test.mod9')); + }); }); group('Linear Tests', () { @@ -193,61 +232,159 @@ void main() { var result = troubleshooter.linearForward(stepSize: 10); expect(result.activeMods.length, equals(10)); expect(result.activeMods.keys.first, equals('test.mod0')); + expect(result.activeMods.keys.last, equals('test.mod9')); result = troubleshooter.linearForward(stepSize: 10); expect(result.activeMods.length, equals(10)); expect(result.activeMods.keys.first, equals('test.mod10')); + expect(result.activeMods.keys.last, equals('test.mod19')); result = troubleshooter.linearForward(stepSize: 10); expect(result.activeMods.length, equals(10)); expect(result.activeMods.keys.first, equals('test.mod20')); + expect(result.activeMods.keys.last, equals('test.mod29')); }, ); test( - 'Bisect search should end up with half the mods every backward iteration until 1', + 'Linear search should end up with 10 mods every backward iteration', () { final troubleshooter = ModListTroubleshooter(modList); - var result = troubleshooter.binaryBackward(); - // Half of our initial 30 - expect(result.activeMods.length, equals(15)); + var result = troubleshooter.linearBackward(stepSize: 10); + expect(result.activeMods.length, equals(10)); + expect(result.activeMods.keys.first, equals('test.mod20')); + expect(result.activeMods.keys.last, equals('test.mod29')); - result = troubleshooter.binaryBackward(); - // Half of our previous result - expect(result.activeMods.length, equals(8)); + result = troubleshooter.linearBackward(stepSize: 10); + expect(result.activeMods.length, equals(10)); + expect(result.activeMods.keys.first, equals('test.mod10')); + expect(result.activeMods.keys.last, equals('test.mod19')); - result = troubleshooter.binaryBackward(); - expect(result.activeMods.length, equals(4)); - - result = troubleshooter.binaryBackward(); - expect(result.activeMods.length, equals(2)); - - result = troubleshooter.binaryBackward(); - expect(result.activeMods.length, equals(1)); + result = troubleshooter.linearBackward(stepSize: 10); + expect(result.activeMods.length, equals(10)); + expect(result.activeMods.keys.first, equals('test.mod0')); + expect(result.activeMods.keys.last, equals('test.mod9')); }, ); - test( - 'Bisect search should end up with half the mods every iteration until 1', - () { - final troubleshooter = ModListTroubleshooter(modList); + test('Linear search should end up with 10 mods every iteration', () { + final troubleshooter = ModListTroubleshooter(modList); - var result = troubleshooter.binaryBackward(); - // Half of our initial 30 - expect(result.activeMods.length, equals(15)); + var result = troubleshooter.linearBackward(stepSize: 10); + expect(result.activeMods.length, equals(10)); + expect(result.activeMods.keys.first, equals('test.mod20')); + expect(result.activeMods.keys.last, equals('test.mod29')); - result = troubleshooter.binaryForward(); - // Half of our previous result - expect(result.activeMods.length, equals(8)); + result = troubleshooter.linearForward(stepSize: 10); + expect(result.activeMods.length, equals(10)); + expect(result.activeMods.keys.first, equals('test.mod20')); + expect(result.activeMods.keys.last, equals('test.mod29')); - result = troubleshooter.binaryBackward(); - expect(result.activeMods.length, equals(4)); + result = troubleshooter.linearBackward(stepSize: 10); + expect(result.activeMods.length, equals(10)); + expect(result.activeMods.keys.first, equals('test.mod20')); + expect(result.activeMods.keys.last, equals('test.mod29')); + }); + test('Linear search should handle abuse gracefully', () { + final troubleshooter = ModListTroubleshooter(modList); - result = troubleshooter.binaryForward(); - expect(result.activeMods.length, equals(2)); + var result = troubleshooter.linearBackward(stepSize: 10); + expect(result.activeMods.length, equals(10)); - result = troubleshooter.binaryBackward(); - expect(result.activeMods.length, equals(1)); - }, - ); + result = troubleshooter.linearForward(stepSize: 10); + expect(result.activeMods.length, equals(10)); + + result = troubleshooter.linearBackward(stepSize: 10); + expect(result.activeMods.length, equals(10)); + + result = troubleshooter.linearForward(stepSize: 10); + expect(result.activeMods.length, equals(10)); + + result = troubleshooter.linearForward(stepSize: 10); + expect(result.activeMods.length, equals(10)); + }); + test('Linear search cannot return more items than there are', () { + final troubleshooter = ModListTroubleshooter(modList); + + var result = troubleshooter.linearBackward(stepSize: 10000); + expect(result.activeMods.length, equals(30)); + + result = troubleshooter.linearForward(stepSize: 10000); + expect(result.activeMods.length, equals(30)); + }); + }); + group('Loading dependencies', () { + late ModList modList = ModList(); + setUp(() { + modList = ModList(); + + for (int i = 0; i < 20; i++) { + final modId = 'test.mod$i'; + var mod = makeDummy().copyWith(name: 'Test Mod $i', id: modId); + if (i % 3 == 0) { + mod = mod.copyWith(dependencies: ['test.mod${i + 1}']); + } + modList.mods[modId] = mod; + } + // Dependencies are: + // 0 -> 1 + // 3 -> 4 + // 6 -> 7 + // 9 -> 10 + // 12 -> 13 + // 15 -> 16 + // 18 -> 19 + modList.enableAll(); + }); + // Not that it has any reason to since they're completely detached... + test('Loading dependencies should not fuck up troubleshooter', () { + final troubleshooter = ModListTroubleshooter(modList); + final expectedFirst = [ + 'test.mod1', + // 0 depends on 1 + 'test.mod0', + 'test.mod2', + // 3 depends on 4 + 'test.mod4', + 'test.mod3', + 'test.mod5', + // 6 depends on 7 + 'test.mod7', + 'test.mod6', + 'test.mod8', + // 9 depends on 10 + 'test.mod10', + 'test.mod9', + ]; + + var result = troubleshooter.linearForward(stepSize: 10); + var loadOrder = result.loadRequired(); + expect(loadOrder.length, equals(11)); + for (int i = 0; i < expectedFirst.length; i++) { + expect(loadOrder[i], equals(expectedFirst[i])); + } + + final expectedSecond = [ + 'test.mod10', + 'test.mod11', + // 12 depends on 13 + 'test.mod13', + 'test.mod12', + 'test.mod14', + // 15 depends on 16 + 'test.mod16', + 'test.mod15', + 'test.mod17', + // 18 depends on 19 + 'test.mod19', + 'test.mod18', + ]; + + result = troubleshooter.linearForward(stepSize: 10); + loadOrder = result.loadRequired(); + expect(loadOrder.length, equals(10)); + for (int i = 0; i < expectedSecond.length; i++) { + expect(loadOrder[i], equals(expectedSecond[i])); + } + }); }); }