 Chromium Code Reviews
 Chromium Code Reviews Issue 10540151:
  First pass at version constraint solver.  (Closed) 
  Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
    
  
    Issue 10540151:
  First pass at version constraint solver.  (Closed) 
  Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart| Index: utils/tests/pub/version_test.dart | 
| diff --git a/utils/tests/pub/version_test.dart b/utils/tests/pub/version_test.dart | 
| index 2922a5bbe98871d00cde53f3d7390af00d0e0a55..75a1acbe881803eb52d534a76cab33a68735aa93 100644 | 
| --- a/utils/tests/pub/version_test.dart | 
| +++ b/utils/tests/pub/version_test.dart | 
| @@ -9,6 +9,14 @@ | 
| #import('../../pub/version.dart'); | 
| main() { | 
| + final v123 = new Version.parse('1.2.3'); | 
| + final v114 = new Version.parse('1.1.4'); | 
| + final v124 = new Version.parse('1.2.4'); | 
| + final v200 = new Version.parse('2.0.0'); | 
| + final v234 = new Version.parse('2.3.4'); | 
| + final v250 = new Version.parse('2.5.0'); | 
| + final v300 = new Version.parse('3.0.0'); | 
| + | 
| group('Version', () { | 
| test('none', () { | 
| expect(Version.none.toString(), equals('0.0.0')); | 
| @@ -73,9 +81,27 @@ main() { | 
| }); | 
| test('allows()', () { | 
| - expect(new Version.parse('1.2.3').allows(new Version.parse('1.2.3'))); | 
| - expect(!new Version.parse('1.2.3').allows(new Version.parse('1.1.4'))); | 
| - expect(!new Version.parse('1.2.3').allows(new Version.parse('1.2.4'))); | 
| + expect(v123.allows(v123)); | 
| + expect(v123.allows(v114), isFalse); | 
| + expect(v123.allows(v124), isFalse); | 
| + }); | 
| + | 
| + test('intersect()', () { | 
| + // Intersecting the same version returns the version. | 
| + expect(v123.intersect(v123), equals(v123)); | 
| + | 
| + // Intersecting a different version allows no versions. | 
| + expect(v123.intersect(v114).isEmpty); | 
| + | 
| + // Intersecting a range returns the version if the range allows it. | 
| + expect(v123.intersect(new VersionRange(v114, v124)), equals(v123)); | 
| + | 
| + // Intersecting a range allows no versions if the range doesn't allow it. | 
| + expect(v114.intersect(new VersionRange(v123, v124)).isEmpty); | 
| + }); | 
| + | 
| + test('isEmpty', () { | 
| + expect(v123.isEmpty, isFalse); | 
| }); | 
| test('parse()', () { | 
| @@ -123,17 +149,14 @@ main() { | 
| group('VersionRange', () { | 
| group('constructor', () { | 
| test('takes a min and max', () { | 
| - var min = new Version.parse('1.2.3'); | 
| - var max = new Version.parse('1.3.5'); | 
| - var range = new VersionRange(min, max); | 
| - expect(range.min, equals(min)); | 
| - expect(range.max, equals(max)); | 
| + var range = new VersionRange(v123, v124); | 
| + expect(range.min, equals(v123)); | 
| + expect(range.max, equals(v124)); | 
| }); | 
| test('allows omitting max', () { | 
| - var min = new Version.parse('1.2.3'); | 
| - var range = new VersionRange(min); | 
| - expect(range.min, equals(min)); | 
| + var range = new VersionRange(v123); | 
| + expect(range.min, equals(v123)); | 
| expect(range.max, isNull); | 
| }); | 
| @@ -143,18 +166,43 @@ main() { | 
| expect(range.max, isNull); | 
| }); | 
| - test('throws if min > max', () { | 
| - var min = new Version.parse('1.2.3'); | 
| - var max = new Version.parse('1.0.0'); | 
| + test('takes includeMin', () { | 
| + var range = new VersionRange(min: v123, includeMin: true); | 
| + expect(range.includeMin); | 
| + }); | 
| - throwsIllegalArg(() => new VersionRange(min, max)); | 
| + test('includeMin defaults to false if omitted', () { | 
| + var range = new VersionRange(min: v123); | 
| + expect(range.includeMin, isFalse); | 
| + }); | 
| + | 
| + test('takes includeMax', () { | 
| + var range = new VersionRange(max: v123, includeMax: true); | 
| + expect(range.includeMax); | 
| + }); | 
| + | 
| + test('includeMax defaults to false if omitted', () { | 
| + var range = new VersionRange(max: v123); | 
| + expect(range.includeMax, isFalse); | 
| + }); | 
| + | 
| + test('throws if min > max', () { | 
| + throwsIllegalArg(() => new VersionRange(v124, v123)); | 
| }); | 
| }); | 
| group('allows()', () { | 
| - test('version must be min or greater', () { | 
| - var range = new VersionRange( | 
| - new Version.parse('1.2.3'), new Version.parse('2.3.4')); | 
| + test('version must be greater than min', () { | 
| + var range = new VersionRange(v123, v234); | 
| + | 
| + expect(!range.allows(new Version.parse('1.2.2'))); | 
| 
nweiz
2012/06/18 18:29:19
It seems weird that you use expect(..., isFalse) s
 
Bob Nystrom
2012/06/20 01:40:04
Done.
 | 
| + expect(!range.allows(new Version.parse('1.2.3'))); | 
| + expect(range.allows(new Version.parse('1.3.3'))); | 
| + expect(range.allows(new Version.parse('2.3.3'))); | 
| + }); | 
| + | 
| + test('version must be min or greater if includeMin', () { | 
| + var range = new VersionRange(v123, v234, includeMin: true); | 
| expect(!range.allows(new Version.parse('1.2.2'))); | 
| expect(range.allows(new Version.parse('1.2.3'))); | 
| @@ -163,25 +211,32 @@ main() { | 
| }); | 
| test('version must be less than max', () { | 
| - var range = new VersionRange( | 
| - new Version.parse('1.2.3'), new Version.parse('2.3.4')); | 
| + var range = new VersionRange(v123, v234); | 
| expect(range.allows(new Version.parse('2.3.3'))); | 
| expect(!range.allows(new Version.parse('2.3.4'))); | 
| expect(!range.allows(new Version.parse('2.4.3'))); | 
| }); | 
| + test('version must be max or less if includeMax', () { | 
| + var range = new VersionRange(v123, v234, includeMax: true); | 
| + | 
| + expect(range.allows(new Version.parse('2.3.3'))); | 
| + expect(range.allows(new Version.parse('2.3.4'))); | 
| + expect(!range.allows(new Version.parse('2.4.3'))); | 
| + }); | 
| + | 
| test('has no min if one was not set', () { | 
| - var range = new VersionRange(max: new Version.parse('1.2.3')); | 
| + var range = new VersionRange(max: v123); | 
| expect(range.allows(new Version.parse('0.0.0'))); | 
| expect(!range.allows(new Version.parse('1.2.3'))); | 
| }); | 
| test('has no max if one was not set', () { | 
| - var range = new VersionRange(new Version.parse('1.2.3')); | 
| + var range = new VersionRange(v123); | 
| - expect(range.allows(new Version.parse('1.2.3'))); | 
| + expect(!range.allows(new Version.parse('1.2.3'))); | 
| expect(range.allows(new Version.parse('1.3.3'))); | 
| expect(range.allows(new Version.parse('999.3.3'))); | 
| }); | 
| @@ -193,9 +248,173 @@ main() { | 
| expect(range.allows(new Version.parse('999.99.9'))); | 
| }); | 
| }); | 
| + | 
| + group('intersect()', () { | 
| + test('two overlapping ranges', () { | 
| + var a = new VersionRange(v123, v250); | 
| + var b = new VersionRange(v200, v300); | 
| + var intersect = a.intersect(b); | 
| + expect(intersect.min, equals(v200)); | 
| + expect(intersect.max, equals(v250)); | 
| + expect(intersect.includeMin, isFalse); | 
| + expect(intersect.includeMax, isFalse); | 
| + }); | 
| + | 
| + test('a non-overlapping range allows no versions', () { | 
| + var a = new VersionRange(v114, v124); | 
| + var b = new VersionRange(v200, v250); | 
| + expect(a.intersect(b).isEmpty); | 
| + }); | 
| + | 
| + test('adjacent ranges allow no versions if exclusive', () { | 
| + var a = new VersionRange(v114, v124, includeMax: false); | 
| + var b = new VersionRange(v124, v200, includeMin: true); | 
| + expect(a.intersect(b).isEmpty); | 
| + }); | 
| + | 
| + test('adjacent ranges allow version if inclusive', () { | 
| + var a = new VersionRange(v114, v124, includeMax: true); | 
| + var b = new VersionRange(v124, v200, includeMin: true); | 
| + expect(a.intersect(b), equals(v124)); | 
| + }); | 
| + | 
| + test('with an open range', () { | 
| + var open = new VersionRange(); | 
| + var a = new VersionRange(v114, v124); | 
| + expect(open.intersect(open), equals(open)); | 
| + expect(a.intersect(open), equals(a)); | 
| + }); | 
| + | 
| + test('returns the version if the range allows it', () { | 
| + expect(new VersionRange(v114, v124).intersect(v123), equals(v123)); | 
| + expect(new VersionRange(v123, v124).intersect(v114).isEmpty); | 
| + }); | 
| + }); | 
| + | 
| + test('isEmpty', () { | 
| + expect(new VersionRange().isEmpty, isFalse); | 
| + expect(new VersionRange(v123, v124).isEmpty, isFalse); | 
| + }); | 
| + }); | 
| + | 
| + group('VersionConstraint', () { | 
| + test('empty', () { | 
| + expect(new VersionConstraint.empty().isEmpty); | 
| + }); | 
| + | 
| + group('parseConstraint()', () { | 
| + test('parses an exact version', () { | 
| + var constraint = new VersionConstraint.parse('1.2.3-alpha'); | 
| + expect(constraint is Version); | 
| + expect(constraint, equals(new Version(1, 2, 3, pre: 'alpha'))); | 
| + }); | 
| + | 
| + test('parses a ">" minimum version', () { | 
| + expect(new VersionConstraint.parse('>1.2.3'), allows([ | 
| + new Version.parse('1.2.3+foo'), | 
| + new Version.parse('1.2.4')])); | 
| + expect(new VersionConstraint.parse('>1.2.3'), doesNotAllow([ | 
| + new Version.parse('1.2.1'), | 
| + new Version.parse('1.2.3-build'), | 
| + new Version.parse('1.2.3')])); | 
| + }); | 
| + | 
| + test('parses a ">=" minimum version', () { | 
| + expect(new VersionConstraint.parse('>=1.2.3'), allows([ | 
| + new Version.parse('1.2.3'), | 
| + new Version.parse('1.2.3+foo'), | 
| + new Version.parse('1.2.4')])); | 
| + expect(new VersionConstraint.parse('>=1.2.3'), doesNotAllow([ | 
| + new Version.parse('1.2.1'), | 
| + new Version.parse('1.2.3-build')])); | 
| + }); | 
| + | 
| + test('parses a "<" maximum version', () { | 
| + expect(new VersionConstraint.parse('<1.2.3'), allows([ | 
| + new Version.parse('1.2.1'), | 
| + new Version.parse('1.2.3-build')])); | 
| + expect(new VersionConstraint.parse('<1.2.3'), doesNotAllow([ | 
| + new Version.parse('1.2.3'), | 
| + new Version.parse('1.2.3+foo'), | 
| + new Version.parse('1.2.4')])); | 
| + }); | 
| + | 
| + test('parses a "<=" maximum version', () { | 
| + expect(new VersionConstraint.parse('<=1.2.3'), allows([ | 
| + new Version.parse('1.2.1'), | 
| + new Version.parse('1.2.3-build'), | 
| + new Version.parse('1.2.3')])); | 
| + expect(new VersionConstraint.parse('<=1.2.3'), doesNotAllow([ | 
| + new Version.parse('1.2.3+foo'), | 
| + new Version.parse('1.2.4')])); | 
| + }); | 
| + | 
| + test('parses a series of space-separated constraints', () { | 
| + var constraint = new VersionConstraint.parse('>1.0.0 >=1.2.3 <1.3.0'); | 
| + expect(constraint, allows([ | 
| + new Version.parse('1.2.3'), | 
| + new Version.parse('1.2.5')])); | 
| + expect(constraint, doesNotAllow([ | 
| + new Version.parse('1.2.3-pre'), | 
| + new Version.parse('1.3.0'), | 
| + new Version.parse('3.4.5')])); | 
| + }); | 
| + | 
| + test('throws FormatException on a bad string', () { | 
| + throwsBadFormat(() => new VersionConstraint.parse('')); | 
| + throwsBadFormat(() => new VersionConstraint.parse(' ')); | 
| + throwsBadFormat(() => new VersionConstraint.parse('not a version')); | 
| + }); | 
| + }); | 
| }); | 
| } | 
| +class VersionConstraintMatcher implements Matcher { | 
| + final List<Version> _expected; | 
| + final bool _allow; | 
| + | 
| + VersionConstraintMatcher(this._expected, this._allow); | 
| + | 
| + bool matches(item) => (item is VersionConstraint) && | 
| + _expected.every((version) => item.allows(version) == _allow); | 
| + | 
| + Description describe(Description description) => | 
| + description.add(' ${_allow ? "allows" : "does not allow"} versions'); | 
| + | 
| + Description describeMismatch(item, Description mismatchDescription) { | 
| + if (item is! VersionConstraint) { | 
| + mismatchDescription.add('was not a VersionConstraint'); | 
| + return mismatchDescription; | 
| + } | 
| + | 
| + bool first = true; | 
| + for (var version in _expected) { | 
| + if (item.allows(version) != _allow) { | 
| + if (first) { | 
| + if (_allow) { | 
| + mismatchDescription.addDescriptionOf(item).add('did not allow '); | 
| + } else { | 
| + mismatchDescription.addDescriptionOf(item).add('allowed '); | 
| + } | 
| + } else { | 
| + mismatchDescription.add(' and '); | 
| + } | 
| + first = false; | 
| + | 
| + mismatchDescription.add(version.toString()); | 
| + } | 
| + } | 
| + | 
| + return mismatchDescription; | 
| + } | 
| +} | 
| + | 
| +Matcher allows(List<Version> versions) => | 
| + new VersionConstraintMatcher(versions, true); | 
| + | 
| +Matcher doesNotAllow(List<Version> versions) => | 
| + new VersionConstraintMatcher(versions, false); | 
| + | 
| throwsIllegalArg(function) { | 
| expectThrow(function, (e) => e is IllegalArgumentException); | 
| } |