| // Copyright (c) 2018, 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 'dart:collection'; |
| import 'dart:math' show Random; |
| |
| import 'package:graphs/graphs.dart'; |
| import 'package:test/test.dart'; |
| |
| import 'utils/utils.dart'; |
| |
| void main() { |
| const graph = <String, List<String>>{ |
| '1': ['2', '5'], |
| '2': ['3'], |
| '3': ['4', '5'], |
| '4': ['1'], |
| '5': ['8'], |
| '6': ['7'], |
| }; |
| |
| List<String> readEdges(String key) => graph[key] ?? []; |
| |
| List<X> getXValues(X key) => graph[key.value]?.map(X.new).toList() ?? []; |
| |
| void singlePathTest(String from, String to, List<String>? expected) { |
| test('$from -> $to should be $expected (mapped)', () { |
| expect( |
| shortestPath<X>( |
| X(from), |
| X(to), |
| getXValues, |
| equals: xEquals, |
| hashCode: xHashCode, |
| )?.map((x) => x.value), |
| expected, |
| ); |
| }); |
| |
| test('$from -> $to should be $expected', () { |
| expect(shortestPath(from, to, readEdges), expected); |
| }); |
| } |
| |
| void pathsTest( |
| String from, |
| Map<String, List<String>> expected, |
| List<String> nullPaths, |
| ) { |
| test('paths from $from (mapped)', () { |
| final result = shortestPaths<X>( |
| X(from), |
| getXValues, |
| equals: xEquals, |
| hashCode: xHashCode, |
| ).map((k, v) => MapEntry(k.value, v.map((x) => x.value).toList())); |
| expect(result, expected); |
| }); |
| |
| test('paths from $from', () { |
| final result = shortestPaths(from, readEdges); |
| expect(result, expected); |
| }); |
| |
| for (var entry in expected.entries) { |
| singlePathTest(from, entry.key, entry.value); |
| } |
| |
| for (var entry in nullPaths) { |
| singlePathTest(from, entry, null); |
| } |
| } |
| |
| pathsTest('1', { |
| '5': ['5'], |
| '3': ['2', '3'], |
| '8': ['5', '8'], |
| '1': [], |
| '2': ['2'], |
| '4': ['2', '3', '4'], |
| }, [ |
| '6', |
| '7', |
| ]); |
| |
| pathsTest('6', { |
| '7': ['7'], |
| '6': [], |
| }, [ |
| '1', |
| ]); |
| pathsTest('7', {'7': []}, ['1', '6']); |
| |
| pathsTest('42', {'42': []}, ['1', '6']); |
| |
| test('integration test', () { |
| // Be deterministic in the generated graph. This test may have to be updated |
| // if the behavior of `Random` changes for the provided seed. |
| final rnd = Random(1); |
| const size = 1000; |
| final graph = HashMap<int, List<int>>(); |
| |
| Iterable<int>? resultForGraph() => |
| shortestPath(0, size - 1, (e) => graph[e] ?? const []); |
| |
| void addRandomEdge() { |
| final toList = graph.putIfAbsent(rnd.nextInt(size), () => <int>[]); |
| |
| final toValue = rnd.nextInt(size); |
| if (!toList.contains(toValue)) { |
| toList.add(toValue); |
| } |
| } |
| |
| Iterable<int>? result; |
| |
| // Add edges until there is a shortest path between `0` and `size - 1` |
| do { |
| addRandomEdge(); |
| result = resultForGraph(); |
| } while (result == null); |
| |
| expect(result, [313, 547, 91, 481, 74, 64, 439, 388, 660, 275, 999]); |
| |
| var count = 0; |
| // Add edges until the shortest path between `0` and `size - 1` is 2 items |
| // Adding edges should never increase the length of the shortest path. |
| // Adding enough edges should reduce the length of the shortest path. |
| do { |
| expect(++count, lessThan(size * 5), reason: 'This loop should finish.'); |
| addRandomEdge(); |
| final previousResultLength = result!.length; |
| result = resultForGraph(); |
| expect(result, hasLength(lessThanOrEqualTo(previousResultLength))); |
| } while (result!.length > 2); |
| |
| expect(result, [275, 999]); |
| |
| count = 0; |
| // Remove edges until there is no shortest path. |
| // Removing edges should never reduce the length of the shortest path. |
| // Removing enough edges should increase the length of the shortest path and |
| // eventually eliminate any path. |
| do { |
| expect(++count, lessThan(size * 5), reason: 'This loop should finish.'); |
| final randomKey = graph.keys.elementAt(rnd.nextInt(graph.length)); |
| final list = graph[randomKey]!; |
| expect(list, isNotEmpty); |
| list.removeAt(rnd.nextInt(list.length)); |
| if (list.isEmpty) { |
| graph.remove(randomKey); |
| } |
| final previousResultLength = result!.length; |
| result = resultForGraph(); |
| if (result != null) { |
| expect(result, hasLength(greaterThanOrEqualTo(previousResultLength))); |
| } |
| } while (result != null); |
| }); |
| } |