Index: sdk/lib/_internal/pub/test/version_solver_test.dart |
diff --git a/sdk/lib/_internal/pub/test/version_solver_test.dart b/sdk/lib/_internal/pub/test/version_solver_test.dart |
index 6c2cb22784290651e90b911bf57aef1b9cb9575b..35af3ac94b923c677cfbd32023dad888d1cc1a58 100644 |
--- a/sdk/lib/_internal/pub/test/version_solver_test.dart |
+++ b/sdk/lib/_internal/pub/test/version_solver_test.dart |
@@ -10,6 +10,7 @@ import 'dart:io'; |
import 'package:unittest/unittest.dart'; |
import '../lib/src/lock_file.dart'; |
+import '../lib/src/log.dart' as log; |
import '../lib/src/package.dart'; |
import '../lib/src/pubspec.dart'; |
import '../lib/src/sdk.dart' as sdk; |
@@ -27,6 +28,9 @@ MockSource source2; |
main() { |
initConfig(); |
+ // Uncomment this to debug failing tests. |
+ // log.showSolver(); |
+ |
// Since this test isn't run from the SDK, it can't find the "version" file |
// to load. Instead, just manually inject a version. |
sdk.version = new Version(1, 2, 3); |
@@ -484,6 +488,111 @@ backtracking() { |
'c': '1.0.0' |
}, maxTries: 2); |
+ // Tests that the backjumper will jump past unrelated selections when a |
+ // source conflict occurs. This test selects, in order: |
+ // - myapp -> a |
+ // - myapp -> b |
+ // - myapp -> c (1 of 5) |
+ // - b -> a |
+ // It selects a and b first because they have fewer versions than c. It |
+ // traverses b's dependency on a after selecting a version of c because |
+ // dependencies are traversed breadth-first (all of myapps's immediate deps |
+ // before any other their deps). |
+ // |
+ // This means it doesn't discover the source conflict until after selecting |
+ // c. When that happens, it should backjump past c instead of trying older |
+ // versions of it since they aren't related to the conflict. |
+ testResolve('backjump to conflicting source', { |
+ 'myapp 0.0.0': { |
+ 'a': 'any', |
+ 'b': 'any', |
+ 'c': 'any' |
+ }, |
+ 'a 1.0.0': {}, |
+ 'a 1.0.0 from mock2': {}, |
+ 'b 1.0.0': { |
+ 'a': 'any' |
+ }, |
+ 'b 2.0.0': { |
+ 'a from mock2': 'any' |
+ }, |
+ 'c 1.0.0': {}, |
+ 'c 2.0.0': {}, |
+ 'c 3.0.0': {}, |
+ 'c 4.0.0': {}, |
+ 'c 5.0.0': {}, |
+ }, result: { |
+ 'myapp from root': '0.0.0', |
+ 'a': '1.0.0', |
+ 'b': '1.0.0', |
+ 'c': '5.0.0' |
+ }, maxTries: 2); |
+ |
+ // Like the above test, but for a conflicting description. |
+ testResolve('backjump to conflicting description', { |
+ 'myapp 0.0.0': { |
+ 'a-x': 'any', |
+ 'b': 'any', |
+ 'c': 'any' |
+ }, |
+ 'a-x 1.0.0': {}, |
+ 'a-y 1.0.0': {}, |
+ 'b 1.0.0': { |
+ 'a-x': 'any' |
+ }, |
+ 'b 2.0.0': { |
+ 'a-y': 'any' |
+ }, |
+ 'c 1.0.0': {}, |
+ 'c 2.0.0': {}, |
+ 'c 3.0.0': {}, |
+ 'c 4.0.0': {}, |
+ 'c 5.0.0': {}, |
+ }, result: { |
+ 'myapp from root': '0.0.0', |
+ 'a': '1.0.0', |
+ 'b': '1.0.0', |
+ 'c': '5.0.0' |
+ }, maxTries: 2); |
+ |
+ // Similar to the above two tests but where there is no solution. It should |
+ // fail in this case with no backtracking. |
+ testResolve('backjump to conflicting source', { |
+ 'myapp 0.0.0': { |
+ 'a': 'any', |
+ 'b': 'any', |
+ 'c': 'any' |
+ }, |
+ 'a 1.0.0': {}, |
+ 'a 1.0.0 from mock2': {}, |
+ 'b 1.0.0': { |
+ 'a from mock2': 'any' |
+ }, |
+ 'c 1.0.0': {}, |
+ 'c 2.0.0': {}, |
+ 'c 3.0.0': {}, |
+ 'c 4.0.0': {}, |
+ 'c 5.0.0': {}, |
+ }, error: sourceMismatch('myapp', 'b'), maxTries: 1); |
+ |
+ testResolve('backjump to conflicting description', { |
+ 'myapp 0.0.0': { |
+ 'a-x': 'any', |
+ 'b': 'any', |
+ 'c': 'any' |
+ }, |
+ 'a-x 1.0.0': {}, |
+ 'a-y 1.0.0': {}, |
+ 'b 1.0.0': { |
+ 'a-y': 'any' |
+ }, |
+ 'c 1.0.0': {}, |
+ 'c 2.0.0': {}, |
+ 'c 3.0.0': {}, |
+ 'c 4.0.0': {}, |
+ 'c 5.0.0': {}, |
+ }, error: descriptionMismatch('myapp', 'b'), maxTries: 1); |
+ |
// Dependencies are ordered so that packages with fewer versions are tried |
// first. Here, there are two valid solutions (either a or b must be |
// downgraded once). The chosen one depends on which dep is traversed first. |
@@ -619,13 +728,31 @@ sdkConstraint() { |
}, useBleedingEdgeSdkVersion: true); |
} |
-testResolve(description, packages, |
- {lockfile, result, FailMatcherBuilder error, int maxTries, |
+testResolve(description, packages, { |
+ lockfile, result, FailMatcherBuilder error, int maxTries, |
+ bool useBleedingEdgeSdkVersion}) { |
+ _testResolve(test, description, packages, lockfile: lockfile, result: result, |
+ error: error, maxTries: maxTries, |
+ useBleedingEdgeSdkVersion: useBleedingEdgeSdkVersion); |
+} |
+ |
+solo_testResolve(description, packages, { |
+ lockfile, result, FailMatcherBuilder error, int maxTries, |
+ bool useBleedingEdgeSdkVersion}) { |
+ log.showSolver(); |
+ _testResolve(solo_test, description, packages, lockfile: lockfile, |
+ result: result, error: error, maxTries: maxTries, |
+ useBleedingEdgeSdkVersion: useBleedingEdgeSdkVersion); |
+} |
+ |
+_testResolve(void testFn(String description, Function body), |
+ description, packages, { |
+ lockfile, result, FailMatcherBuilder error, int maxTries, |
bool useBleedingEdgeSdkVersion}) { |
if (maxTries == null) maxTries = 1; |
if (useBleedingEdgeSdkVersion == null) useBleedingEdgeSdkVersion = false; |
- test(description, () { |
+ testFn(description, () { |
var cache = new SystemCache('.'); |
source1 = new MockSource('mock1'); |
source2 = new MockSource('mock2'); |