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