| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 library pubspec_test; | |
| 6 | |
| 7 import 'dart:async'; | |
| 8 | |
| 9 import 'package:pub_semver/pub_semver.dart'; | |
| 10 import 'package:unittest/unittest.dart'; | |
| 11 | |
| 12 import '../lib/src/package.dart'; | |
| 13 import '../lib/src/pubspec.dart'; | |
| 14 import '../lib/src/source.dart'; | |
| 15 import '../lib/src/source/path.dart'; | |
| 16 import '../lib/src/source_registry.dart'; | |
| 17 import 'test_pub.dart'; | |
| 18 | |
| 19 class MockSource extends Source { | |
| 20 final String name = "mock"; | |
| 21 | |
| 22 Future<Pubspec> doDescribe(PackageId id) => throw new UnsupportedError( | |
| 23 "Cannot describe mock packages."); | |
| 24 | |
| 25 Future get(PackageId id, String symlink) => throw new UnsupportedError( | |
| 26 "Cannot get a mock package."); | |
| 27 | |
| 28 Future<String> getDirectory(PackageId id) => throw new UnsupportedError( | |
| 29 "Cannot get the directory for mock packages."); | |
| 30 | |
| 31 dynamic parseDescription(String filePath, description, | |
| 32 {bool fromLockFile: false}) { | |
| 33 if (description != 'ok') throw new FormatException('Bad'); | |
| 34 return description; | |
| 35 } | |
| 36 | |
| 37 bool descriptionsEqual(description1, description2) => | |
| 38 description1 == description2; | |
| 39 | |
| 40 String packageName(description) => 'foo'; | |
| 41 } | |
| 42 | |
| 43 main() { | |
| 44 initConfig(); | |
| 45 group('parse()', () { | |
| 46 var sources = new SourceRegistry(); | |
| 47 sources.register(new MockSource()); | |
| 48 sources.register(new PathSource()); | |
| 49 | |
| 50 var throwsPubspecException = | |
| 51 throwsA(new isInstanceOf<PubspecException>('PubspecException')); | |
| 52 | |
| 53 expectPubspecException(String contents, fn(Pubspec pubspec), | |
| 54 [String expectedContains]) { | |
| 55 var expectation = throwsPubspecException; | |
| 56 if (expectedContains != null) { | |
| 57 expectation = throwsA(allOf( | |
| 58 new isInstanceOf<PubspecException>('PubspecException'), | |
| 59 predicate((error) => error.message.contains(expectedContains)))); | |
| 60 } | |
| 61 | |
| 62 var pubspec = new Pubspec.parse(contents, sources); | |
| 63 expect(() => fn(pubspec), expectation); | |
| 64 } | |
| 65 | |
| 66 test("doesn't eagerly throw an error for an invalid field", () { | |
| 67 // Shouldn't throw an error. | |
| 68 new Pubspec.parse('version: not a semver', sources); | |
| 69 }); | |
| 70 | |
| 71 test("eagerly throws an error if the pubspec name doesn't match the " | |
| 72 "expected name", () { | |
| 73 expect(() => new Pubspec.parse("name: foo", sources, expectedName: 'bar'), | |
| 74 throwsPubspecException); | |
| 75 }); | |
| 76 | |
| 77 test("eagerly throws an error if the pubspec doesn't have a name and an " | |
| 78 "expected name is passed", () { | |
| 79 expect(() => new Pubspec.parse("{}", sources, expectedName: 'bar'), | |
| 80 throwsPubspecException); | |
| 81 }); | |
| 82 | |
| 83 test("allows a version constraint for dependencies", () { | |
| 84 var pubspec = new Pubspec.parse(''' | |
| 85 dependencies: | |
| 86 foo: | |
| 87 mock: ok | |
| 88 version: ">=1.2.3 <3.4.5" | |
| 89 ''', sources); | |
| 90 | |
| 91 var foo = pubspec.dependencies[0]; | |
| 92 expect(foo.name, equals('foo')); | |
| 93 expect(foo.constraint.allows(new Version(1, 2, 3)), isTrue); | |
| 94 expect(foo.constraint.allows(new Version(1, 2, 5)), isTrue); | |
| 95 expect(foo.constraint.allows(new Version(3, 4, 5)), isFalse); | |
| 96 }); | |
| 97 | |
| 98 test("allows an empty dependencies map", () { | |
| 99 var pubspec = new Pubspec.parse(''' | |
| 100 dependencies: | |
| 101 ''', sources); | |
| 102 | |
| 103 expect(pubspec.dependencies, isEmpty); | |
| 104 }); | |
| 105 | |
| 106 test("allows a version constraint for dev dependencies", () { | |
| 107 var pubspec = new Pubspec.parse(''' | |
| 108 dev_dependencies: | |
| 109 foo: | |
| 110 mock: ok | |
| 111 version: ">=1.2.3 <3.4.5" | |
| 112 ''', sources); | |
| 113 | |
| 114 var foo = pubspec.devDependencies[0]; | |
| 115 expect(foo.name, equals('foo')); | |
| 116 expect(foo.constraint.allows(new Version(1, 2, 3)), isTrue); | |
| 117 expect(foo.constraint.allows(new Version(1, 2, 5)), isTrue); | |
| 118 expect(foo.constraint.allows(new Version(3, 4, 5)), isFalse); | |
| 119 }); | |
| 120 | |
| 121 test("allows an empty dev dependencies map", () { | |
| 122 var pubspec = new Pubspec.parse(''' | |
| 123 dev_dependencies: | |
| 124 ''', sources); | |
| 125 | |
| 126 expect(pubspec.devDependencies, isEmpty); | |
| 127 }); | |
| 128 | |
| 129 test("allows a version constraint for dependency overrides", () { | |
| 130 var pubspec = new Pubspec.parse(''' | |
| 131 dependency_overrides: | |
| 132 foo: | |
| 133 mock: ok | |
| 134 version: ">=1.2.3 <3.4.5" | |
| 135 ''', sources); | |
| 136 | |
| 137 var foo = pubspec.dependencyOverrides[0]; | |
| 138 expect(foo.name, equals('foo')); | |
| 139 expect(foo.constraint.allows(new Version(1, 2, 3)), isTrue); | |
| 140 expect(foo.constraint.allows(new Version(1, 2, 5)), isTrue); | |
| 141 expect(foo.constraint.allows(new Version(3, 4, 5)), isFalse); | |
| 142 }); | |
| 143 | |
| 144 test("allows an empty dependency overrides map", () { | |
| 145 var pubspec = new Pubspec.parse(''' | |
| 146 dependency_overrides: | |
| 147 ''', sources); | |
| 148 | |
| 149 expect(pubspec.dependencyOverrides, isEmpty); | |
| 150 }); | |
| 151 | |
| 152 test("allows an unknown source", () { | |
| 153 var pubspec = new Pubspec.parse(''' | |
| 154 dependencies: | |
| 155 foo: | |
| 156 unknown: blah | |
| 157 ''', sources); | |
| 158 | |
| 159 var foo = pubspec.dependencies[0]; | |
| 160 expect(foo.name, equals('foo')); | |
| 161 expect(foo.source, equals('unknown')); | |
| 162 }); | |
| 163 | |
| 164 test("throws if a package is in dependencies and dev_dependencies", () { | |
| 165 expectPubspecException(''' | |
| 166 dependencies: | |
| 167 foo: | |
| 168 mock: ok | |
| 169 dev_dependencies: | |
| 170 foo: | |
| 171 mock: ok | |
| 172 ''', (pubspec) { | |
| 173 // This check only triggers if both [dependencies] and [devDependencies] | |
| 174 // are accessed. | |
| 175 pubspec.dependencies; | |
| 176 pubspec.devDependencies; | |
| 177 }); | |
| 178 }); | |
| 179 | |
| 180 test("throws if it dependes on itself", () { | |
| 181 expectPubspecException(''' | |
| 182 name: myapp | |
| 183 dependencies: | |
| 184 myapp: | |
| 185 mock: ok | |
| 186 ''', (pubspec) => pubspec.dependencies); | |
| 187 }); | |
| 188 | |
| 189 test("throws if it has a dev dependency on itself", () { | |
| 190 expectPubspecException(''' | |
| 191 name: myapp | |
| 192 dev_dependencies: | |
| 193 myapp: | |
| 194 mock: ok | |
| 195 ''', (pubspec) => pubspec.devDependencies); | |
| 196 }); | |
| 197 | |
| 198 test("throws if it has an override on itself", () { | |
| 199 expectPubspecException(''' | |
| 200 name: myapp | |
| 201 dependency_overrides: | |
| 202 myapp: | |
| 203 mock: ok | |
| 204 ''', (pubspec) => pubspec.dependencyOverrides); | |
| 205 }); | |
| 206 | |
| 207 test("throws if the description isn't valid", () { | |
| 208 expectPubspecException(''' | |
| 209 dependencies: | |
| 210 foo: | |
| 211 mock: bad | |
| 212 ''', (pubspec) => pubspec.dependencies); | |
| 213 }); | |
| 214 | |
| 215 test("throws if dependency version is not a string", () { | |
| 216 expectPubspecException(''' | |
| 217 dependencies: | |
| 218 foo: | |
| 219 mock: ok | |
| 220 version: 1.2 | |
| 221 ''', (pubspec) => pubspec.dependencies); | |
| 222 }); | |
| 223 | |
| 224 test("throws if version is not a version constraint", () { | |
| 225 expectPubspecException(''' | |
| 226 dependencies: | |
| 227 foo: | |
| 228 mock: ok | |
| 229 version: not constraint | |
| 230 ''', (pubspec) => pubspec.dependencies); | |
| 231 }); | |
| 232 | |
| 233 test("throws if 'name' is not a string", () { | |
| 234 expectPubspecException('name: [not, a, string]', | |
| 235 (pubspec) => pubspec.name); | |
| 236 }); | |
| 237 | |
| 238 test("throws if version is not a string", () { | |
| 239 expectPubspecException('version: [2, 0, 0]', | |
| 240 (pubspec) => pubspec.version, | |
| 241 '"version" field must be a string'); | |
| 242 }); | |
| 243 | |
| 244 test("throws if version is malformed (looking like a double)", () { | |
| 245 expectPubspecException('version: 2.1', | |
| 246 (pubspec) => pubspec.version, | |
| 247 '"version" field must have three numeric components: major, minor, ' | |
| 248 'and patch. Instead of "2.1", consider "2.1.0"'); | |
| 249 }); | |
| 250 | |
| 251 test("throws if version is malformed (looking like an int)", () { | |
| 252 expectPubspecException('version: 2', | |
| 253 (pubspec) => pubspec.version, | |
| 254 '"version" field must have three numeric components: major, minor, ' | |
| 255 'and patch. Instead of "2", consider "2.0.0"'); | |
| 256 }); | |
| 257 | |
| 258 test("throws if version is not a version", () { | |
| 259 expectPubspecException('version: not version', | |
| 260 (pubspec) => pubspec.version); | |
| 261 }); | |
| 262 | |
| 263 test("throws if transformers isn't a list", () { | |
| 264 expectPubspecException('transformers: "not list"', | |
| 265 (pubspec) => pubspec.transformers, | |
| 266 '"transformers" field must be a list'); | |
| 267 }); | |
| 268 | |
| 269 test("throws if a transformer isn't a string or map", () { | |
| 270 expectPubspecException('transformers: [12]', | |
| 271 (pubspec) => pubspec.transformers, | |
| 272 'A transformer must be a string or map.'); | |
| 273 }); | |
| 274 | |
| 275 test("throws if a transformer's configuration isn't a map", () { | |
| 276 expectPubspecException('transformers: [{pkg: 12}]', | |
| 277 (pubspec) => pubspec.transformers, | |
| 278 "A transformer's configuration must be a map."); | |
| 279 }); | |
| 280 | |
| 281 test("throws if a transformer's configuration contains an unknown " | |
| 282 "reserved key at the top level", () { | |
| 283 expectPubspecException(''' | |
| 284 name: pkg | |
| 285 transformers: [{pkg: {\$key: "value"}}]''', | |
| 286 (pubspec) => pubspec.transformers, | |
| 287 'Invalid transformer config: Unknown reserved field.'); | |
| 288 }); | |
| 289 | |
| 290 test("doesn't throw if a transformer's configuration contains a " | |
| 291 "non-top-level key beginning with a dollar sign", () { | |
| 292 var pubspec = new Pubspec.parse(''' | |
| 293 name: pkg | |
| 294 transformers: | |
| 295 - pkg: {outer: {\$inner: value}} | |
| 296 ''', sources); | |
| 297 | |
| 298 var pkg = pubspec.transformers[0].single; | |
| 299 expect(pkg.configuration["outer"]["\$inner"], equals("value")); | |
| 300 }); | |
| 301 | |
| 302 test("throws if the \$include value is not a string or list", () { | |
| 303 expectPubspecException(''' | |
| 304 name: pkg | |
| 305 transformers: | |
| 306 - pkg: {\$include: 123}''', | |
| 307 (pubspec) => pubspec.transformers, | |
| 308 'Invalid transformer config: "\$include" field must be a string or ' | |
| 309 'list.'); | |
| 310 }); | |
| 311 | |
| 312 test("throws if the \$include list contains a non-string", () { | |
| 313 expectPubspecException(''' | |
| 314 name: pkg | |
| 315 transformers: | |
| 316 - pkg: {\$include: ["ok", 123, "alright", null]}''', | |
| 317 (pubspec) => pubspec.transformers, | |
| 318 'Invalid transformer config: "\$include" field may contain only ' | |
| 319 'strings.'); | |
| 320 }); | |
| 321 | |
| 322 test("throws if the \$exclude value is not a string or list", () { | |
| 323 expectPubspecException(''' | |
| 324 name: pkg | |
| 325 transformers: | |
| 326 - pkg: {\$exclude: 123}''', | |
| 327 (pubspec) => pubspec.transformers, | |
| 328 'Invalid transformer config: "\$exclude" field must be a string or ' | |
| 329 'list.'); | |
| 330 }); | |
| 331 | |
| 332 test("throws if the \$exclude list contains a non-string", () { | |
| 333 expectPubspecException(''' | |
| 334 name: pkg | |
| 335 transformers: | |
| 336 - pkg: {\$exclude: ["ok", 123, "alright", null]}''', | |
| 337 (pubspec) => pubspec.transformers, | |
| 338 'Invalid transformer config: "\$exclude" field may contain only ' | |
| 339 'strings.'); | |
| 340 }); | |
| 341 | |
| 342 test("throws if a transformer is not from a dependency", () { | |
| 343 expectPubspecException(''' | |
| 344 name: pkg | |
| 345 transformers: [foo] | |
| 346 ''', | |
| 347 (pubspec) => pubspec.transformers, | |
| 348 '"foo" is not a dependency.'); | |
| 349 }); | |
| 350 | |
| 351 test("allows a transformer from a normal dependency", () { | |
| 352 var pubspec = new Pubspec.parse(''' | |
| 353 name: pkg | |
| 354 dependencies: | |
| 355 foo: | |
| 356 mock: ok | |
| 357 transformers: | |
| 358 - foo''', sources); | |
| 359 | |
| 360 expect(pubspec.transformers[0].single.id.package, equals("foo")); | |
| 361 }); | |
| 362 | |
| 363 test("allows a transformer from a dev dependency", () { | |
| 364 var pubspec = new Pubspec.parse(''' | |
| 365 name: pkg | |
| 366 dev_dependencies: | |
| 367 foo: | |
| 368 mock: ok | |
| 369 transformers: | |
| 370 - foo''', sources); | |
| 371 | |
| 372 expect(pubspec.transformers[0].single.id.package, equals("foo")); | |
| 373 }); | |
| 374 | |
| 375 test("allows a transformer from a dependency override", () { | |
| 376 var pubspec = new Pubspec.parse(''' | |
| 377 name: pkg | |
| 378 dependency_overrides: | |
| 379 foo: | |
| 380 mock: ok | |
| 381 transformers: | |
| 382 - foo''', sources); | |
| 383 | |
| 384 expect(pubspec.transformers[0].single.id.package, equals("foo")); | |
| 385 }); | |
| 386 | |
| 387 test("allows comment-only files", () { | |
| 388 var pubspec = new Pubspec.parse(''' | |
| 389 # No external dependencies yet | |
| 390 # Including for completeness | |
| 391 # ...and hoping the spec expands to include details about author, version, etc | |
| 392 # See http://www.dartlang.org/docs/pub-package-manager/ for details | |
| 393 ''', sources); | |
| 394 expect(pubspec.version, equals(Version.none)); | |
| 395 expect(pubspec.dependencies, isEmpty); | |
| 396 }); | |
| 397 | |
| 398 test("throws a useful error for unresolvable path dependencies", () { | |
| 399 expectPubspecException(''' | |
| 400 name: pkg | |
| 401 dependencies: | |
| 402 from_path: {path: non_local_path} | |
| 403 ''', (pubspec) => pubspec.dependencies, | |
| 404 '"non_local_path" is a relative path, but this isn\'t a local ' | |
| 405 'pubspec.'); | |
| 406 }); | |
| 407 | |
| 408 group("environment", () { | |
| 409 test("defaults to any SDK constraint if environment is omitted", () { | |
| 410 var pubspec = new Pubspec.parse('', sources); | |
| 411 expect(pubspec.environment.sdkVersion, equals(VersionConstraint.any)); | |
| 412 }); | |
| 413 | |
| 414 test("allows an empty environment map", () { | |
| 415 var pubspec = new Pubspec.parse(''' | |
| 416 environment: | |
| 417 ''', sources); | |
| 418 expect(pubspec.environment.sdkVersion, equals(VersionConstraint.any)); | |
| 419 }); | |
| 420 | |
| 421 test("throws if the environment value isn't a map", () { | |
| 422 expectPubspecException('environment: []', | |
| 423 (pubspec) => pubspec.environment); | |
| 424 }); | |
| 425 | |
| 426 test("allows a version constraint for the sdk", () { | |
| 427 var pubspec = new Pubspec.parse(''' | |
| 428 environment: | |
| 429 sdk: ">=1.2.3 <2.3.4" | |
| 430 ''', sources); | |
| 431 expect(pubspec.environment.sdkVersion, | |
| 432 equals(new VersionConstraint.parse(">=1.2.3 <2.3.4"))); | |
| 433 }); | |
| 434 | |
| 435 test("throws if the sdk isn't a string", () { | |
| 436 expectPubspecException('environment: {sdk: []}', | |
| 437 (pubspec) => pubspec.environment); | |
| 438 expectPubspecException('environment: {sdk: 1.0}', | |
| 439 (pubspec) => pubspec.environment); | |
| 440 }); | |
| 441 | |
| 442 test("throws if the sdk isn't a valid version constraint", () { | |
| 443 expectPubspecException('environment: {sdk: "oopies"}', | |
| 444 (pubspec) => pubspec.environment); | |
| 445 }); | |
| 446 }); | |
| 447 | |
| 448 group("publishTo", () { | |
| 449 test("defaults to null if omitted", () { | |
| 450 var pubspec = new Pubspec.parse('', sources); | |
| 451 expect(pubspec.publishTo, isNull); | |
| 452 }); | |
| 453 | |
| 454 test("throws if not a string", () { | |
| 455 expectPubspecException('publish_to: 123', | |
| 456 (pubspec) => pubspec.publishTo); | |
| 457 }); | |
| 458 | |
| 459 test("allows a URL", () { | |
| 460 var pubspec = new Pubspec.parse(''' | |
| 461 publish_to: http://example.com | |
| 462 ''', sources); | |
| 463 expect(pubspec.publishTo, equals("http://example.com")); | |
| 464 }); | |
| 465 | |
| 466 test("allows none", () { | |
| 467 var pubspec = new Pubspec.parse(''' | |
| 468 publish_to: none | |
| 469 ''', sources); | |
| 470 expect(pubspec.publishTo, equals("none")); | |
| 471 }); | |
| 472 | |
| 473 test("throws on other strings", () { | |
| 474 expectPubspecException('publish_to: http://bad.url:not-port', | |
| 475 (pubspec) => pubspec.publishTo); | |
| 476 }); | |
| 477 }); | |
| 478 | |
| 479 group("executables", () { | |
| 480 test("defaults to an empty map if omitted", () { | |
| 481 var pubspec = new Pubspec.parse('', sources); | |
| 482 expect(pubspec.executables, isEmpty); | |
| 483 }); | |
| 484 | |
| 485 test("allows simple names for keys and most characters in values", () { | |
| 486 var pubspec = new Pubspec.parse(''' | |
| 487 executables: | |
| 488 abcDEF-123_: "abc DEF-123._" | |
| 489 ''', sources); | |
| 490 expect(pubspec.executables['abcDEF-123_'], equals('abc DEF-123._')); | |
| 491 }); | |
| 492 | |
| 493 test("throws if not a map", () { | |
| 494 expectPubspecException('executables: not map', | |
| 495 (pubspec) => pubspec.executables); | |
| 496 }); | |
| 497 | |
| 498 test("throws if key is not a string", () { | |
| 499 expectPubspecException('executables: {123: value}', | |
| 500 (pubspec) => pubspec.executables); | |
| 501 }); | |
| 502 | |
| 503 test("throws if a key isn't a simple name", () { | |
| 504 expectPubspecException('executables: {funny/name: ok}', | |
| 505 (pubspec) => pubspec.executables); | |
| 506 }); | |
| 507 | |
| 508 test("throws if a value is not a string", () { | |
| 509 expectPubspecException('executables: {command: 123}', | |
| 510 (pubspec) => pubspec.executables); | |
| 511 }); | |
| 512 | |
| 513 test("throws if a value contains a path separator", () { | |
| 514 expectPubspecException('executables: {command: funny_name/part}', | |
| 515 (pubspec) => pubspec.executables); | |
| 516 }); | |
| 517 | |
| 518 test("throws if a value contains a windows path separator", () { | |
| 519 expectPubspecException(r'executables: {command: funny_name\part}', | |
| 520 (pubspec) => pubspec.executables); | |
| 521 }); | |
| 522 | |
| 523 test("uses the key if the value is null", () { | |
| 524 var pubspec = new Pubspec.parse(''' | |
| 525 executables: | |
| 526 command: | |
| 527 ''', sources); | |
| 528 expect(pubspec.executables['command'], equals('command')); | |
| 529 }); | |
| 530 }); | |
| 531 }); | |
| 532 } | |
| OLD | NEW |