Handle pre-release semantics by adjusting the max version of ranges (dart-lang/pub_semver#28) Rather than adding special-cases to each operation, we now just modify the upper bound of version ranges when the special pre-release semantics would come into play. Closes dart-lang/pubdart-lang/pub_semver#1885
diff --git a/pkgs/pub_semver/CHANGELOG.md b/pkgs/pub_semver/CHANGELOG.md index f2e5e62..0efa0ea 100644 --- a/pkgs/pub_semver/CHANGELOG.md +++ b/pkgs/pub_semver/CHANGELOG.md
@@ -1,3 +1,25 @@ +# 1.4.0 + +* Add a `Version.firstPreRelease` getter that returns the first possible + pre-release of a version. + +* Add a `Version.isFirstPreRelease` getter that returns whether a version is the + first possible pre-release. + +* `new VersionRange()` with an exclusive maximum now replaces the maximum with + its first pre-release version. This matches the existing semantics, where an + exclusive maximum would exclude pre-release versions of that maximum. + + Explicitly representing this by changing the maximum version ensures that all + operations behave correctly with respect to the special pre-release semantics. + In particular, it fixes bugs where, for example, + `(>=1.0.0 <2.0.0-dev).union(>=2.0.0-dev <2.0.0)` and + `(>=1.0.0 <3.0.0).difference(^1.0.0)` wouldn't include `2.0.0-dev`. + +* Add an `alwaysIncludeMaxPreRelease` parameter to `new VersionRange()`, which + disables the replacement described above and allows users to create ranges + that do include the pre-release versions of an exclusive max version. + # 1.3.7 * Fix more bugs with `VersionRange.intersect()`, `VersionRange.difference()`,
diff --git a/pkgs/pub_semver/lib/pub_semver.dart b/pkgs/pub_semver/lib/pub_semver.dart index bfb7d8b..4b6487c 100644 --- a/pkgs/pub_semver/lib/pub_semver.dart +++ b/pkgs/pub_semver/lib/pub_semver.dart
@@ -4,5 +4,5 @@ export 'src/version.dart'; export 'src/version_constraint.dart'; -export 'src/version_range.dart'; +export 'src/version_range.dart' hide CompatibleWithVersionRange; export 'src/version_union.dart';
diff --git a/pkgs/pub_semver/lib/src/utils.dart b/pkgs/pub_semver/lib/src/utils.dart index 55025a0..60617f2 100644 --- a/pkgs/pub_semver/lib/src/utils.dart +++ b/pkgs/pub_semver/lib/src/utils.dart
@@ -30,12 +30,6 @@ if (range1.max == null) return range2.max != null; if (range2.max == null) return false; - // `<1.0.0-dev.1` allows higher versions than `<1.0.0`, such as `1.0.0-dev.0`. - if (disallowedByPreRelease(range2, range1.max)) return true; - - // `<1.0.0` doesn't allow any versions higher than `<1.0.0-dev`. - if (disallowedByPreRelease(range1, range2.max)) return false; - var comparison = range1.max.compareTo(range2.max); if (comparison == 1) return true; if (comparison == -1) return false; @@ -47,11 +41,6 @@ bool strictlyLower(VersionRange range1, VersionRange range2) { if (range1.max == null || range2.min == null) return false; - // `<1.0.0` doesn't allow any versions allowed by `>=1.0.0-dev.0`. - if (disallowedByPreRelease(range1, range2.min)) return true; - - //if (disallowedByPreRelease(range2, range1.min)) return true; - var comparison = range1.max.compareTo(range2.min); if (comparison == -1) return true; if (comparison == 1) return false; @@ -63,40 +52,7 @@ bool strictlyHigher(VersionRange range1, VersionRange range2) => strictlyLower(range2, range1); -// Returns whether [other] is disallowed by [range] because we disallow -// pre-release versions that have the same major, minor, and patch version as -// the max of a range, but only if neither the max nor the min is a pre-release -// of that version. -// -// This ensures that `^1.2.3` doesn't include `2.0.0-pre`, while also allowing -// both `>=2.0.0-pre.2 <2.0.0` and `>=1.2.3 <2.0.0-pre.7` to match -// `2.0.0-pre.5`. -// -// It's worth noting that this is different than [NPM's semantics][]. NPM -// disallows **all** pre-release versions unless their major, minor, and -// patch numbers match those of a prerelease min or max. This ensures that -// no prerelease versions will ever be selected if the user doesn't -// explicitly allow them. -// -// [NPM's semantics]: https://www.npmjs.org/doc/misc/semver.html#prerelease-tags -// -// Instead, we ensure that release versions will always be preferred over -// prerelease versions by ordering the release versions first in -// [Version.prioritize]. This means that constraints like `any` or -// `>1.2.3` can still match prerelease versions if they're the only things -// available. -bool disallowedByPreRelease(VersionRange range, Version other) { - var maxIsReleaseOfOther = !range.includeMax && - !range.max.isPreRelease && - other.isPreRelease && - _equalsWithoutPreRelease(other, range.max); - var minIsPreReleaseOfOther = range.min != null && - range.min.isPreRelease && - _equalsWithoutPreRelease(other, range.min); - return maxIsReleaseOfOther && !minIsPreReleaseOfOther; -} - -bool _equalsWithoutPreRelease(Version version1, Version version2) => +bool equalsWithoutPreRelease(Version version1, Version version2) => version1.major == version2.major && version1.minor == version2.minor && version1.patch == version2.patch;
diff --git a/pkgs/pub_semver/lib/src/version.dart b/pkgs/pub_semver/lib/src/version.dart index 6665873..19ae962 100644 --- a/pkgs/pub_semver/lib/src/version.dart +++ b/pkgs/pub_semver/lib/src/version.dart
@@ -239,6 +239,12 @@ return _incrementMajor(); } + /// Returns the first possible pre-release of this version. + Version get firstPreRelease => new Version(major, minor, patch, pre: "0"); + + /// Returns whether this is the first possible pre-release of its version. + bool get isFirstPreRelease => preRelease.length == 1 && preRelease.first == 0; + Version _incrementMajor() => new Version(major + 1, 0, 0); Version _incrementMinor() => new Version(major, minor + 1, 0); Version _incrementPatch() => new Version(major, minor, patch + 1); @@ -262,7 +268,8 @@ min: other.min, max: other.max, includeMin: true, - includeMax: other.includeMax); + includeMax: other.includeMax, + alwaysIncludeMaxPreRelease: true); } if (other.max == this) { @@ -270,7 +277,8 @@ min: other.min, max: other.max, includeMin: other.includeMin, - includeMax: true); + includeMax: true, + alwaysIncludeMaxPreRelease: true); } }
diff --git a/pkgs/pub_semver/lib/src/version_constraint.dart b/pkgs/pub_semver/lib/src/version_constraint.dart index 670a928..67093f7 100644 --- a/pkgs/pub_semver/lib/src/version_constraint.dart +++ b/pkgs/pub_semver/lib/src/version_constraint.dart
@@ -84,7 +84,10 @@ case '<=': return new VersionRange(max: version, includeMax: true); case '<': - return new VersionRange(max: version, includeMax: false); + return new VersionRange( + max: version, + includeMax: false, + alwaysIncludeMaxPreRelease: true); case '>=': return new VersionRange(min: version, includeMin: true); case '>': @@ -175,7 +178,7 @@ /// are greater than or equal to [version], but less than the next breaking /// version ([Version.nextBreaking]) of [version]. factory VersionConstraint.compatibleWith(Version version) => - new _CompatibleWithVersionRange(version); + new CompatibleWithVersionRange(version); /// Creates a new version constraint that is the intersection of /// [constraints]. @@ -278,14 +281,3 @@ VersionConstraint difference(VersionConstraint other) => this; String toString() => '<empty>'; } - -class _CompatibleWithVersionRange extends VersionRange { - _CompatibleWithVersionRange(Version version) - : super( - min: version, - includeMin: true, - max: version.nextBreaking, - includeMax: false); - - String toString() => '^$min'; -}
diff --git a/pkgs/pub_semver/lib/src/version_range.dart b/pkgs/pub_semver/lib/src/version_range.dart index ec15de4..99dc718 100644 --- a/pkgs/pub_semver/lib/src/version_range.dart +++ b/pkgs/pub_semver/lib/src/version_range.dart
@@ -53,14 +53,36 @@ /// /// If [includeMin] is `true`, then the minimum end of the range is inclusive. /// Likewise, passing [includeMax] as `true` makes the upper end inclusive. - VersionRange( - {this.min, this.max, this.includeMin: false, this.includeMax: false}) { + /// + /// If [alwaysIncludeMaxPreRelease] is `true`, this will always include + /// pre-release versions of an exclusive [max]. Otherwise, it will use the + /// default behavior for pre-release versions of [max]. + factory VersionRange( + {Version min, + Version max, + bool includeMin: false, + bool includeMax: false, + bool alwaysIncludeMaxPreRelease: false}) { if (min != null && max != null && min > max) { throw new ArgumentError( 'Minimum version ("$min") must be less than maximum ("$max").'); } + + if (!alwaysIncludeMaxPreRelease && + !includeMax && + max != null && + !max.isPreRelease && + (min == null || + !min.isPreRelease || + !equalsWithoutPreRelease(min, max))) { + max = max.firstPreRelease; + } + + return new VersionRange._(min, max, includeMin, includeMax); } + VersionRange._(this.min, this.max, this.includeMin, this.includeMax); + bool operator ==(other) { if (other is! VersionRange) return false; @@ -90,7 +112,6 @@ if (max != null) { if (other > max) return false; if (!includeMax && other == max) return false; - if (disallowedByPreRelease(this, other)) return false; } return true; @@ -177,7 +198,8 @@ min: intersectMin, max: intersectMax, includeMin: intersectIncludeMin, - includeMax: intersectIncludeMax); + includeMax: intersectIncludeMax, + alwaysIncludeMaxPreRelease: true); } throw new ArgumentError('Unknown VersionConstraint type $other.'); @@ -192,7 +214,8 @@ min: this.min, max: this.max, includeMin: true, - includeMax: this.includeMax); + includeMax: this.includeMax, + alwaysIncludeMaxPreRelease: true); } if (other == max) { @@ -200,7 +223,8 @@ min: this.min, max: this.max, includeMin: this.includeMin, - includeMax: true); + includeMax: true, + alwaysIncludeMaxPreRelease: true); } return new VersionConstraint.unionOf([this, other]); @@ -239,7 +263,8 @@ min: unionMin, max: unionMax, includeMin: unionIncludeMin, - includeMax: unionIncludeMax); + includeMax: unionIncludeMax, + alwaysIncludeMaxPreRelease: true); } return new VersionConstraint.unionOf([this, other]); @@ -254,20 +279,36 @@ if (other == min) { if (!includeMin) return this; return new VersionRange( - min: min, max: max, includeMin: false, includeMax: includeMax); + min: min, + max: max, + includeMin: false, + includeMax: includeMax, + alwaysIncludeMaxPreRelease: true); } if (other == max) { if (!includeMax) return this; return new VersionRange( - min: min, max: max, includeMin: includeMin, includeMax: false); + min: min, + max: max, + includeMin: includeMin, + includeMax: false, + alwaysIncludeMaxPreRelease: true); } return new VersionUnion.fromRanges([ new VersionRange( - min: min, max: other, includeMin: includeMin, includeMax: false), + min: min, + max: other, + includeMin: includeMin, + includeMax: false, + alwaysIncludeMaxPreRelease: true), new VersionRange( - min: other, max: max, includeMin: false, includeMax: includeMax) + min: other, + max: max, + includeMin: false, + includeMax: includeMax, + alwaysIncludeMaxPreRelease: true) ]); } else if (other is VersionRange) { if (!allowsAny(other)) return this; @@ -284,7 +325,8 @@ min: min, max: other.min, includeMin: includeMin, - includeMax: !other.includeMin); + includeMax: !other.includeMin, + alwaysIncludeMaxPreRelease: true); } VersionRange after; @@ -299,7 +341,8 @@ min: other.max, max: max, includeMin: !other.includeMax, - includeMax: includeMax); + includeMax: includeMax, + alwaysIncludeMaxPreRelease: true); } if (before == null && after == null) return VersionConstraint.empty; @@ -379,11 +422,36 @@ if (max != null) { if (min != null) buffer.write(' '); - buffer.write(includeMax ? '<=' : '<'); - buffer.write(max); + if (includeMax) { + buffer.write('<='); + buffer.write(max); + } else { + buffer.write('<'); + if (max.isFirstPreRelease) { + // Since `"<$max"` would parse the same as `"<$max-0"`, we just emit + // `<$max` to avoid confusing "-0" suffixes. + buffer.write("${max.major}.${max.minor}.${max.patch}"); + } else { + buffer.write(max); + + // If `">=$min <$max"` would parse as `">=$min <$max-0"`, add `-*` to + // indicate that actually does allow pre-release versions. + var minIsPreReleaseOfMax = min != null && + min.isPreRelease && + equalsWithoutPreRelease(min, max); + if (!max.isPreRelease && !minIsPreReleaseOfMax) buffer.write("-∞"); + } + } } if (min == null && max == null) buffer.write('any'); return buffer.toString(); } } + +class CompatibleWithVersionRange extends VersionRange { + CompatibleWithVersionRange(Version version) + : super._(version, version.nextBreaking.firstPreRelease, true, false); + + String toString() => '^$min'; +}
diff --git a/pkgs/pub_semver/pubspec.yaml b/pkgs/pub_semver/pubspec.yaml index 1b738d7..a537788 100644 --- a/pkgs/pub_semver/pubspec.yaml +++ b/pkgs/pub_semver/pubspec.yaml
@@ -1,5 +1,5 @@ name: pub_semver -version: 1.3.7 +version: 1.4.0 author: Dart Team <misc@dartlang.org> description: > Versions and version constraints implementing pub's versioning policy. This
diff --git a/pkgs/pub_semver/test/utils.dart b/pkgs/pub_semver/test/utils.dart index 9b28c6a..8ecd8f5 100644 --- a/pkgs/pub_semver/test/utils.dart +++ b/pkgs/pub_semver/test/utils.dart
@@ -22,6 +22,10 @@ final v250 = new Version.parse('2.5.0'); final v300 = new Version.parse('3.0.0'); +/// A range that allows pre-release versions of its max version. +final includeMaxPreReleaseRange = + new VersionRange(max: v200, alwaysIncludeMaxPreRelease: true); + /// A [Matcher] that tests if a [VersionConstraint] allows or does not allow a /// given list of [Version]s. class _VersionConstraintMatcher implements Matcher {
diff --git a/pkgs/pub_semver/test/version_range_test.dart b/pkgs/pub_semver/test/version_range_test.dart index 63cf302..4bdc21c 100644 --- a/pkgs/pub_semver/test/version_range_test.dart +++ b/pkgs/pub_semver/test/version_range_test.dart
@@ -14,7 +14,25 @@ var range = new VersionRange(min: v123, max: v124); expect(range.isAny, isFalse); expect(range.min, equals(v123)); - expect(range.max, equals(v124)); + expect(range.max, equals(v124.firstPreRelease)); + }); + + group("doesn't make the max a pre-release if", () { + test("it's already a pre-release", () { + expect(new VersionRange(max: new Version.parse("1.2.4-pre")).max, + equals(new Version.parse("1.2.4-pre"))); + }); + + test("includeMax is true", () { + expect(new VersionRange(max: v124, includeMax: true).max, equals(v124)); + }); + + test("min is a prerelease of max", () { + expect( + new VersionRange(min: new Version.parse("1.2.4-pre"), max: v124) + .max, + equals(v124)); + }); }); test('allows omitting max', () { @@ -158,6 +176,10 @@ expect(range, allows(new Version.parse('0.0.0'), new Version.parse('999.99.9'))); }); + + test('allows pre-releases of the max with includeMaxPreRelease', () { + expect(includeMaxPreReleaseRange, allows(new Version.parse('2.0.0-dev'))); + }); }); group('allowsAll()', () { @@ -247,6 +269,13 @@ isFalse); }); + test('of non-pre-release max are included with includeMaxPreRelease', () { + expect( + includeMaxPreReleaseRange + .allowsAll(new VersionConstraint.parse('<2.0.0-dev')), + isTrue); + }); + test( 'of non-pre-release max are included if min is a pre-release of the ' 'same version', () { @@ -388,6 +417,13 @@ isFalse); }); + test('of non-pre-release max are included with includeMaxPreRelease', () { + expect( + includeMaxPreReleaseRange + .allowsAny(new VersionConstraint.parse('>2.0.0-dev')), + isTrue); + }); + test( 'of non-pre-release max are included if min is a pre-release of the ' 'same version', () { @@ -419,15 +455,10 @@ group('intersect()', () { test('two overlapping ranges', () { - var a = new VersionRange(min: v123, max: v250); - var b = new VersionRange(min: v200, max: v300); - var intersect = a.intersect(b); - expect(intersect, new isInstanceOf<VersionRange>()); - var intersectRange = intersect as VersionRange; - expect(intersectRange.min, equals(v200)); - expect(intersectRange.max, equals(v250)); - expect(intersectRange.includeMin, isFalse); - expect(intersectRange.includeMax, isFalse); + expect( + new VersionRange(min: v123, max: v250) + .intersect(new VersionRange(min: v200, max: v300)), + equals(new VersionRange(min: v200, max: v250))); }); test('a non-overlapping range allows no versions', () { @@ -476,6 +507,48 @@ .intersect(new VersionConstraint.parse("<2.0.0-dev")), equals(new VersionRange(max: v200))); }); + + group("with includeMaxPreRelease", () { + test('preserves includeMaxPreRelease if the max version is included', () { + expect( + includeMaxPreReleaseRange + .intersect(new VersionConstraint.parse("<1.0.0")), + equals(new VersionConstraint.parse("<1.0.0"))); + expect( + includeMaxPreReleaseRange + .intersect(new VersionConstraint.parse("<2.0.0")), + equals(new VersionConstraint.parse("<2.0.0"))); + expect(includeMaxPreReleaseRange.intersect(includeMaxPreReleaseRange), + equals(includeMaxPreReleaseRange)); + expect( + includeMaxPreReleaseRange + .intersect(new VersionConstraint.parse("<3.0.0")), + equals(includeMaxPreReleaseRange)); + expect( + includeMaxPreReleaseRange + .intersect(new VersionConstraint.parse(">1.1.4")), + equals(new VersionRange( + min: v114, max: v200, alwaysIncludeMaxPreRelease: true))); + }); + + test( + "and a range with a pre-release min, returns " + "an intersection", () { + expect( + includeMaxPreReleaseRange + .intersect(new VersionConstraint.parse(">=2.0.0-dev")), + equals(new VersionConstraint.parse(">=2.0.0-dev <2.0.0"))); + }); + + test( + "and a range with a pre-release max, returns " + "the narrower constraint", () { + expect( + includeMaxPreReleaseRange + .intersect(new VersionConstraint.parse("<2.0.0-dev")), + equals(new VersionConstraint.parse("<2.0.0-dev"))); + }); + }); }); group('union()', () { @@ -485,7 +558,10 @@ }); test("with a version on the edge of the range, expands the range", () { - expect(new VersionRange(min: v114, max: v124).union(v124), + expect( + new VersionRange( + min: v114, max: v124, alwaysIncludeMaxPreRelease: true) + .union(v124), equals(new VersionRange(min: v114, max: v124, includeMax: true))); expect(new VersionRange(min: v114, max: v124).union(v114), equals(new VersionRange(min: v114, max: v124, includeMin: true))); @@ -533,7 +609,8 @@ .union(new VersionRange(min: v114, max: v200)); expect(result, equals(new VersionRange(min: v003, max: v200))); - result = new VersionRange(min: v003, max: v114) + result = new VersionRange( + min: v003, max: v114, alwaysIncludeMaxPreRelease: true) .union(new VersionRange(min: v114, max: v200, includeMin: true)); expect(result, equals(new VersionRange(min: v003, max: v200))); @@ -541,8 +618,9 @@ .union(new VersionRange(min: v003, max: v114, includeMax: true)); expect(result, equals(new VersionRange(min: v003, max: v200))); - result = new VersionRange(min: v114, max: v200, includeMin: true) - .union(new VersionRange(min: v003, max: v114)); + result = new VersionRange(min: v114, max: v200, includeMin: true).union( + new VersionRange( + min: v003, max: v114, alwaysIncludeMaxPreRelease: true)); expect(result, equals(new VersionRange(min: v003, max: v200))); }); @@ -573,6 +651,39 @@ .union(new VersionConstraint.parse("<2.0.0-dev")), equals(new VersionConstraint.parse("<2.0.0-dev"))); }); + + group("with includeMaxPreRelease", () { + test('adds includeMaxPreRelease if the max version is included', () { + expect( + includeMaxPreReleaseRange + .union(new VersionConstraint.parse("<1.0.0")), + equals(includeMaxPreReleaseRange)); + expect(includeMaxPreReleaseRange.union(includeMaxPreReleaseRange), + equals(includeMaxPreReleaseRange)); + expect( + includeMaxPreReleaseRange + .union(new VersionConstraint.parse("<2.0.0")), + equals(includeMaxPreReleaseRange)); + expect( + includeMaxPreReleaseRange + .union(new VersionConstraint.parse("<3.0.0")), + equals(new VersionConstraint.parse("<3.0.0"))); + }); + + test("and a range with a pre-release min, returns any", () { + expect( + includeMaxPreReleaseRange + .union(new VersionConstraint.parse(">=2.0.0-dev")), + equals(VersionConstraint.any)); + }); + + test("and a range with a pre-release max, returns the original", () { + expect( + includeMaxPreReleaseRange + .union(new VersionConstraint.parse("<2.0.0-dev")), + equals(includeMaxPreReleaseRange)); + }); + }); }); group('difference()', () { @@ -592,7 +703,8 @@ expect( new VersionRange(min: v003, max: v114).difference(v072), equals(new VersionConstraint.unionOf([ - new VersionRange(min: v003, max: v072), + new VersionRange( + min: v003, max: v072, alwaysIncludeMaxPreRelease: true), new VersionRange(min: v072, max: v114) ]))); }); @@ -601,7 +713,8 @@ expect( new VersionRange(min: v003, max: v114, includeMax: true) .difference(v114), - equals(new VersionRange(min: v003, max: v114))); + equals(new VersionRange( + min: v003, max: v114, alwaysIncludeMaxPreRelease: true))); }); test("with the min version makes the min exclusive", () { @@ -630,11 +743,11 @@ expect( new VersionRange(min: v080, max: v130) .difference(new VersionRange(min: v010, max: v114)), - equals(new VersionRange(min: v114, max: v130, includeMin: true))); + equals(new VersionConstraint.parse(">=1.1.4-0 <1.3.0"))); expect( new VersionRange(min: v080, max: v130) .difference(new VersionRange(max: v114)), - equals(new VersionRange(min: v114, max: v130, includeMin: true))); + equals(new VersionConstraint.parse(">=1.1.4-0 <1.3.0"))); expect( new VersionRange(min: v080, max: v130).difference( new VersionRange(min: v010, max: v114, includeMax: true)), @@ -646,7 +759,7 @@ expect( new VersionRange(min: v080, max: v130, includeMax: true) .difference(new VersionRange(min: v080, max: v130)), - equals(v130)); + equals(new VersionConstraint.parse(">=1.3.0-0 <=1.3.0"))); }); test("with a range at the end cuts off the end of the range", () { @@ -661,11 +774,13 @@ expect( new VersionRange(min: v080, max: v130).difference( new VersionRange(min: v114, max: v140, includeMin: true)), - equals(new VersionRange(min: v080, max: v114))); + equals(new VersionRange( + min: v080, max: v114, alwaysIncludeMaxPreRelease: true))); expect( new VersionRange(min: v080, max: v130, includeMax: true).difference( new VersionRange(min: v130, max: v140, includeMin: true)), - equals(new VersionRange(min: v080, max: v130))); + equals(new VersionRange( + min: v080, max: v130, alwaysIncludeMaxPreRelease: true))); expect( new VersionRange(min: v080, max: v130, includeMin: true) .difference(new VersionRange(min: v080, max: v130)), @@ -678,7 +793,7 @@ .difference(new VersionRange(min: v072, max: v114)), equals(new VersionConstraint.unionOf([ new VersionRange(min: v003, max: v072, includeMax: true), - new VersionRange(min: v114, max: v130, includeMin: true) + new VersionConstraint.parse(">=1.1.4-0 <1.3.0") ]))); }); @@ -715,8 +830,7 @@ new VersionRange(min: v080, max: v123), new VersionRange(min: v130, max: v200) ])), - equals(new VersionRange( - min: v123, max: v130, includeMin: true, includeMax: true))); + equals(new VersionConstraint.parse(">=1.2.3-0 <=1.3.0"))); }); test("with a version union that intersects the middle, chops it up", () { @@ -724,9 +838,12 @@ new VersionRange(min: v114, max: v140) .difference(new VersionConstraint.unionOf([v123, v124, v130])), equals(new VersionConstraint.unionOf([ - new VersionRange(min: v114, max: v123), - new VersionRange(min: v123, max: v124), - new VersionRange(min: v124, max: v130), + new VersionRange( + min: v114, max: v123, alwaysIncludeMaxPreRelease: true), + new VersionRange( + min: v123, max: v124, alwaysIncludeMaxPreRelease: true), + new VersionRange( + min: v124, max: v130, alwaysIncludeMaxPreRelease: true), new VersionRange(min: v130, max: v140) ]))); }); @@ -752,6 +869,84 @@ .difference(new VersionConstraint.parse("<2.0.0-dev")), equals(VersionConstraint.empty)); }); + + group("with includeMaxPreRelease", () { + group("for the minuend", () { + test("preserves includeMaxPreRelease if the max version is included", + () { + expect( + includeMaxPreReleaseRange + .difference(new VersionConstraint.parse("<1.0.0")), + equals(new VersionRange( + min: new Version.parse("1.0.0-0"), + max: v200, + includeMin: true, + alwaysIncludeMaxPreRelease: true))); + expect( + includeMaxPreReleaseRange + .difference(new VersionConstraint.parse("<2.0.0")), + equals(new VersionRange( + min: v200.firstPreRelease, + max: v200, + includeMin: true, + alwaysIncludeMaxPreRelease: true))); + expect( + includeMaxPreReleaseRange.difference(includeMaxPreReleaseRange), + equals(VersionConstraint.empty)); + expect( + includeMaxPreReleaseRange + .difference(new VersionConstraint.parse("<3.0.0")), + equals(VersionConstraint.empty)); + }); + + test("with a range with a pre-release min, adjusts the max", () { + expect( + includeMaxPreReleaseRange + .difference(new VersionConstraint.parse(">=2.0.0-dev")), + equals(new VersionConstraint.parse("<2.0.0-dev"))); + }); + + test("with a range with a pre-release max, adjusts the min", () { + expect( + includeMaxPreReleaseRange + .difference(new VersionConstraint.parse("<2.0.0-dev")), + equals(new VersionConstraint.parse(">=2.0.0-dev <2.0.0"))); + }); + }); + + group("for the subtrahend", () { + group("doesn't create a pre-release minimum", () { + test("when cutting off the bottom", () { + expect( + new VersionConstraint.parse("<3.0.0") + .difference(includeMaxPreReleaseRange), + equals( + new VersionRange(min: v200, max: v300, includeMin: true))); + }); + + test("with splitting down the middle", () { + expect( + new VersionConstraint.parse("<4.0.0").difference( + new VersionRange( + min: v200, + max: v300, + includeMin: true, + alwaysIncludeMaxPreRelease: true)), + equals(new VersionConstraint.unionOf([ + new VersionRange(max: v200, alwaysIncludeMaxPreRelease: true), + new VersionConstraint.parse(">=3.0.0 <4.0.0") + ]))); + }); + + test("can leave a single version", () { + expect( + new VersionConstraint.parse("<=2.0.0") + .difference(includeMaxPreReleaseRange), + equals(v200)); + }); + }); + }); + }); }); test('isEmpty', () { @@ -786,6 +981,11 @@ new VersionRange(min: v003, max: v080, includeMax: true)); }); + test("includeMaxPreRelease comes after !includeMaxPreRelease", () { + _expectComparesSmaller( + new VersionRange(max: v200), includeMaxPreReleaseRange); + }); + test("no minimum comes before small minimum", () { _expectComparesSmaller( new VersionRange(max: v010), new VersionRange(min: v003, max: v010));
diff --git a/pkgs/pub_semver/test/version_test.dart b/pkgs/pub_semver/test/version_test.dart index fc6dfef..448435a 100644 --- a/pkgs/pub_semver/test/version_test.dart +++ b/pkgs/pub_semver/test/version_test.dart
@@ -190,8 +190,14 @@ }); test("with a range with the version on the edge, expands the range", () { - expect(v124.union(new VersionRange(min: v114, max: v124)), + expect( + v124.union(new VersionRange( + min: v114, max: v124, alwaysIncludeMaxPreRelease: true)), equals(new VersionRange(min: v114, max: v124, includeMax: true))); + expect( + v124.firstPreRelease.union(new VersionRange(min: v114, max: v124)), + equals(new VersionRange( + min: v114, max: v124.firstPreRelease, includeMax: true))); expect(v114.union(new VersionRange(min: v114, max: v124)), equals(new VersionRange(min: v114, max: v124, includeMin: true))); });
diff --git a/pkgs/pub_semver/test/version_union_test.dart b/pkgs/pub_semver/test/version_union_test.dart index f948832..f629c83 100644 --- a/pkgs/pub_semver/test/version_union_test.dart +++ b/pkgs/pub_semver/test/version_union_test.dart
@@ -101,25 +101,32 @@ new VersionConstraint.unionOf([ new VersionRange(min: v003, max: v072, includeMax: true), new VersionRange(min: v072, max: v080), - new VersionRange(min: v114, max: v124), - new VersionRange(min: v124, max: v130, includeMin: true) + new VersionRange( + min: v114, max: v124, alwaysIncludeMaxPreRelease: true), + new VersionRange(min: v124, max: v130, includeMin: true), + new VersionRange( + min: v130.firstPreRelease, max: v200, includeMin: true) ]), equals(new VersionConstraint.unionOf([ new VersionRange(min: v003, max: v080), - new VersionRange(min: v114, max: v130) + new VersionRange(min: v114, max: v200) ]))); }); test("doesn't merge not-quite-adjacent ranges", () { expect( new VersionConstraint.unionOf([ + new VersionRange(min: v114, max: v124), + new VersionRange(min: v124, max: v130, includeMin: true) + ]), + isNot(equals(new VersionRange(min: v114, max: v130)))); + + expect( + new VersionConstraint.unionOf([ new VersionRange(min: v003, max: v072), new VersionRange(min: v072, max: v080) ]), - equals(new VersionConstraint.unionOf([ - new VersionRange(min: v003, max: v072), - new VersionRange(min: v072, max: v080) - ]))); + isNot(equals(new VersionRange(min: v003, max: v080)))); }); test("merges version numbers into ranges", () { @@ -139,16 +146,30 @@ test("merges adjacent version numbers into ranges", () { expect( new VersionConstraint.unionOf([ - new VersionRange(min: v003, max: v072), + new VersionRange( + min: v003, max: v072, alwaysIncludeMaxPreRelease: true), v072, v114, - new VersionRange(min: v114, max: v124) + new VersionRange(min: v114, max: v124), + v124.firstPreRelease ]), equals(new VersionConstraint.unionOf([ new VersionRange(min: v003, max: v072, includeMax: true), - new VersionRange(min: v114, max: v124, includeMin: true) + new VersionRange( + min: v114, + max: v124.firstPreRelease, + includeMin: true, + includeMax: true) ]))); }); + + test("doesn't merge not-quite-adjacent version numbers into ranges", () { + expect( + new VersionConstraint.unionOf( + [new VersionRange(min: v003, max: v072), v072]), + isNot(equals( + new VersionRange(min: v003, max: v072, includeMax: true)))); + }); }); test('isEmpty returns false', () { @@ -424,7 +445,8 @@ new VersionRange(min: v124) ])), equals(new VersionConstraint.unionOf([ - new VersionRange(min: v072, max: v080, includeMin: true), + new VersionRange( + min: v072.firstPreRelease, max: v080, includeMin: true), new VersionRange(min: v123, max: v124, includeMax: true) ]))); }); @@ -436,8 +458,10 @@ new VersionRange(min: v130, max: v200) ]).difference(new VersionConstraint.unionOf([v072, v080])), equals(new VersionConstraint.unionOf([ - new VersionRange(min: v010, max: v072), - new VersionRange(min: v072, max: v080), + new VersionRange( + min: v010, max: v072, alwaysIncludeMaxPreRelease: true), + new VersionRange( + min: v072, max: v080, alwaysIncludeMaxPreRelease: true), new VersionRange(min: v080, max: v114), new VersionRange(min: v130, max: v200) ]))); @@ -455,7 +479,8 @@ equals(new VersionConstraint.unionOf([ new VersionRange(min: v010, max: v072), new VersionRange(min: v080, max: v114, includeMax: true), - new VersionRange(min: v201, max: v234, includeMin: true), + new VersionRange( + min: v201.firstPreRelease, max: v234, includeMin: true), new VersionRange(min: v250, max: v300) ]))); });