| // Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| import 'util.dart'; |
| |
| typedef TestFunction<T> = List<T> Function(List<T>); |
| |
| class InputOutputData<T> { |
| final List<T> input; |
| final List<T> output; |
| |
| const InputOutputData(this.input, this.output); |
| } |
| |
| const Strategy simpleAddStrategy = Strategy('simple-add', ''' |
| Add entries to a list one at a time.'''); |
| |
| const Strategy spreadStrategy = Strategy('spread', ''' |
| Create the list via spreads.'''); |
| |
| const Strategy listGenerateGrowableStrategy = |
| Strategy('list-generate-growable', ''' |
| Create list via List.generate with growable = true.'''); |
| |
| const Strategy listGenerateNotGrowableStrategy = |
| Strategy('list-generate-not-growable', ''' |
| Create list via List.generate with growable = false.'''); |
| |
| const Strategy listFilledGrowableStrategy = Strategy('list-filled-growable', ''' |
| Create list via List.generate with growable = true.'''); |
| |
| const Strategy listFilledNotGrowableStrategy = |
| Strategy('list-filled-not-growable', ''' |
| Create list via List.generate with growable = false.'''); |
| |
| const Strategy listFilledAlternativeGrowableStrategy = |
| Strategy('list-filled-alt-growable', ''' |
| Create list via List.generate with growable = true in an alternative way.'''); |
| |
| const Strategy listFilledAlternativeNotGrowableStrategy = |
| Strategy('list-filled-alt-not-growable', ''' |
| Create list via List.generate with growable = false in an alternative way.'''); |
| |
| const Scenario emptyScenario = Scenario('empty', ''' |
| The input and output is empty.'''); |
| const Scenario oneEntryScenario = Scenario('one', ''' |
| The input is one entry.'''); |
| const Scenario severalEntriesScenario = Scenario('several', ''' |
| The input has several entries.'''); |
| |
| Map<Scenario, InputOutputData<String>> scenarios = { |
| emptyScenario: const InputOutputData([], []), |
| oneEntryScenario: const InputOutputData(["a"], ["<", "a", ">"]), |
| severalEntriesScenario: const InputOutputData( |
| [ |
| "a", |
| "b", |
| "c", |
| "d", |
| "e", |
| "f", |
| "g", |
| ], |
| [ |
| "<", |
| "a", |
| ", ", |
| "b", |
| ", ", |
| "c", |
| ", ", |
| "d", |
| ", ", |
| "e", |
| ", ", |
| "f", |
| ", ", |
| "g", |
| ">", |
| ], |
| ), |
| }; |
| |
| class Test<T> { |
| final int size; |
| final Map<Strategy, TestFunction<T>> strategies; |
| |
| const Test(this.size, this.strategies); |
| |
| void _test(Registry registry, SeriesKey key, int runs, int iterations, |
| List<T> input, List<T> expectedResult, TestFunction<T> testFunction) { |
| for (int run = 0; run < runs; run++) { |
| Stopwatch sw = new Stopwatch(); |
| for (int i = 0; i < iterations; i++) { |
| sw.start(); |
| List<T> actualOutput = testFunction(input); |
| sw.stop(); |
| checkEquals(actualOutput, expectedResult); |
| } |
| registry.registerData(key, size, sw.elapsedMicroseconds); |
| } |
| } |
| |
| void checkEquals(List<T> a, List<T> b) { |
| if (a.length != b.length) throw "length for $a vs $b"; |
| for (int i = 0; i < a.length; i++) { |
| if (a[i] != b[i]) throw "index $i for $a vs $b"; |
| } |
| } |
| |
| void performTest( |
| {required Registry registry, |
| required int runs, |
| required int iterations, |
| required Map<Scenario, InputOutputData<T>> scenarios}) { |
| for (MapEntry<Scenario, InputOutputData<T>> scenario in scenarios.entries) { |
| List<T> scenarioInput = scenario.value.input; |
| List<T> scenarioExpectedOutput = scenario.value.output; |
| for (MapEntry<Strategy, TestFunction<T>> entry in strategies.entries) { |
| _test(registry, new SeriesKey(entry.key, scenario.key), runs, |
| iterations, scenarioInput, scenarioExpectedOutput, entry.value); |
| } |
| } |
| } |
| } |
| |
| List<Test<String>> tests = [ |
| // "size" isn't used here... |
| Test<String>(-1, { |
| simpleAddStrategy: simplyAdd, |
| listGenerateGrowableStrategy: listGenerateGrowable, |
| listGenerateNotGrowableStrategy: listGenerateNotGrowable, |
| listFilledGrowableStrategy: listFilledGrowable, |
| listFilledNotGrowableStrategy: listFilledNotGrowable, |
| listFilledAlternativeGrowableStrategy: listFilledAlternativeGrowable, |
| listFilledAlternativeNotGrowableStrategy: listFilledAlternativeNotGrowable, |
| spreadStrategy: spread, |
| }), |
| ]; |
| |
| List<String> simplyAdd(List<String> input) { |
| if (input.isEmpty) return []; |
| List<String> result = []; |
| result.add("<"); |
| result.add(input[0]); |
| for (int i = 1; i < input.length; i++) { |
| result.add(", "); |
| result.add(input[i]); |
| } |
| result.add(">"); |
| return result; |
| } |
| |
| List<String> listGenerateGrowable(List<String> input) { |
| if (input.isEmpty) return []; |
| int size = input.length * 2 + 1; |
| return List<String>.generate(size, (index) { |
| if (index == 0) return "<"; |
| if (index == size - 1) return ">"; |
| if (index.isEven) return ", "; |
| return input[index >> 1]; |
| }, growable: true); |
| } |
| |
| List<String> listGenerateNotGrowable(List<String> input) { |
| if (input.isEmpty) return []; |
| int size = input.length * 2 + 1; |
| return List<String>.generate(size, (index) { |
| if (index == 0) return "<"; |
| if (index == size - 1) return ">"; |
| if (index.isEven) return ", "; |
| return input[index >> 1]; |
| }, growable: false); |
| } |
| |
| List<String> listFilledAlternativeGrowable(List<String> input) { |
| if (input.isEmpty) return []; |
| int size = input.length * 2 + 1; |
| List<String> result = List<String>.filled(size, ", ", growable: true); |
| for (int i = 0; i < input.length; i++) { |
| result[(i << 1) + 1] = input[i]; |
| } |
| result[0] = "<"; |
| result[result.length - 1] = ">"; |
| |
| return result; |
| } |
| |
| List<String> listFilledAlternativeNotGrowable(List<String> input) { |
| if (input.isEmpty) return []; |
| int size = input.length * 2 + 1; |
| List<String> result = List<String>.filled(size, ", ", growable: false); |
| for (int i = 0; i < input.length; i++) { |
| result[(i << 1) + 1] = input[i]; |
| } |
| result[0] = "<"; |
| result[result.length - 1] = ">"; |
| |
| return result; |
| } |
| |
| List<String> listFilledGrowable(List<String> input) { |
| if (input.isEmpty) return []; |
| int size = input.length * 2 + 1; |
| List<String> result = List<String>.filled(size, "", growable: true); |
| int j = 0; |
| result[j++] = "<"; |
| result[j++] = input[0]; |
| for (int i = 1; i < input.length; i++) { |
| result[j++] = ", "; |
| result[j++] = input[i]; |
| } |
| result[j++] = ">"; |
| |
| return result; |
| } |
| |
| List<String> listFilledNotGrowable(List<String> input) { |
| if (input.isEmpty) return []; |
| int size = input.length * 2 + 1; |
| List<String> result = List<String>.filled(size, "", growable: false); |
| int j = 0; |
| result[j++] = "<"; |
| result[j++] = input[0]; |
| for (int i = 1; i < input.length; i++) { |
| result[j++] = ", "; |
| result[j++] = input[i]; |
| } |
| result[j++] = ">"; |
| |
| return result; |
| } |
| |
| List<String> spread(List<String> input) { |
| return [ |
| if (input.isNotEmpty) ...[ |
| '<', |
| input.first, |
| for (String s in input.skip(1)) ...[', ', s], |
| '>', |
| ] |
| ]; |
| } |
| |
| void main() { |
| // Dry run |
| for (Test test in tests) { |
| test.performTest( |
| registry: new Registry(), |
| runs: 5, |
| iterations: 10, |
| scenarios: scenarios); |
| } |
| // Actual test |
| Registry registry = new Registry(); |
| for (Test test in tests) { |
| test.performTest( |
| registry: registry, runs: 10, iterations: 100000, scenarios: scenarios); |
| } |
| SeriesSet seriesSet = registry.generateSeriesSet(); |
| print('== Raw data =='); |
| for (Scenario scenario in scenarios.keys) { |
| print(seriesSet.getFullSpreadByScenario(scenario)); |
| } |
| print('== Reduced averages =='); |
| SeriesSet reducedSeriesSet = seriesSet.filter((list) => removeMax(list, 3)); |
| for (Scenario scenario in scenarios.keys) { |
| print(reducedSeriesSet.getAveragedSpreadByScenario(scenario)); |
| } |
| } |