OLD | NEW |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 import 'dart:async'; | 5 import 'dart:async'; |
6 import 'dart:convert'; | 6 import 'dart:convert'; |
| 7 import 'dart:io'; |
7 | 8 |
8 import 'package:path/path.dart' as p; | 9 import 'package:path/path.dart' as p; |
9 import 'package:pub/src/exit_codes.dart' as exit_codes; | 10 import 'package:pub/src/exit_codes.dart' as exit_codes; |
10 import 'package:pub/src/io.dart'; | 11 import 'package:pub/src/io.dart'; |
| 12 import 'package:scheduled_test/scheduled_stream.dart'; |
11 import 'package:scheduled_test/scheduled_test.dart'; | 13 import 'package:scheduled_test/scheduled_test.dart'; |
12 | 14 |
13 import 'descriptor.dart' as d; | 15 import 'descriptor.dart' as d; |
14 import 'test_pub.dart'; | 16 import 'test_pub.dart'; |
15 | 17 |
16 main() { | 18 main() { |
17 group("requires the user to run pub get first if", () { | 19 setUp(() { |
18 setUp(() { | 20 servePackages((builder) { |
19 d.dir(appPath, [ | 21 builder.serve("foo", "1.0.0"); |
20 d.appPubspec(), | 22 builder.serve("foo", "2.0.0"); |
21 d.dir("web", []), | |
22 d.dir("bin", [ | |
23 d.file("script.dart", "main() => print('hello!');") | |
24 ]) | |
25 ]).create(); | |
26 | |
27 pubGet(); | |
28 | |
29 // Delay a bit to make sure the modification times are noticeably | |
30 // different. 1s seems to be the finest granularity that dart:io reports. | |
31 schedule(() => new Future.delayed(new Duration(seconds: 1))); | |
32 }); | 23 }); |
33 | 24 |
| 25 d.dir(appPath, [ |
| 26 d.appPubspec(), |
| 27 d.dir("web", []), |
| 28 d.dir("bin", [ |
| 29 d.file("script.dart", "main() => print('hello!');") |
| 30 ]) |
| 31 ]).create(); |
| 32 |
| 33 pubGet(); |
| 34 }); |
| 35 |
| 36 group("requires the user to run pub get first if", () { |
34 group("there's no lockfile", () { | 37 group("there's no lockfile", () { |
35 setUp(() { | 38 setUp(() { |
36 schedule(() => deleteEntry(p.join(sandboxDir, "myapp/pubspec.lock"))); | 39 schedule(() => deleteEntry(p.join(sandboxDir, "myapp/pubspec.lock"))); |
37 }); | 40 }); |
38 | 41 |
39 _forEveryCommand( | 42 _requiresPubGet( |
40 'No pubspec.lock file found, please run "pub get" first.'); | 43 'No pubspec.lock file found, please run "pub get" first.'); |
41 }); | 44 }); |
42 | 45 |
43 group("there's no package spec", () { | 46 group("there's no package spec", () { |
44 setUp(() { | 47 setUp(() { |
45 schedule(() => deleteEntry(p.join(sandboxDir, "myapp/.packages"))); | 48 schedule(() => deleteEntry(p.join(sandboxDir, "myapp/.packages"))); |
46 }); | 49 }); |
47 | 50 |
48 _forEveryCommand('No .packages file found, please run "pub get" first.'); | 51 _requiresPubGet('No .packages file found, please run "pub get" first.'); |
49 }); | 52 }); |
50 | 53 |
51 group("the pubspec is newer than the package spec", () { | 54 group("the pubspec has a new dependency", () { |
52 setUp(() { | 55 setUp(() { |
53 schedule(() => _touch("pubspec.yaml")); | 56 d.dir("foo", [ |
54 }); | 57 d.libPubspec("foo", "1.0.0") |
55 | 58 ]).create(); |
56 _forEveryCommand('The pubspec.yaml file has changed since the .packages ' | 59 |
| 60 d.dir(appPath, [ |
| 61 d.appPubspec({"foo": {"path": "../foo"}}) |
| 62 ]).create(); |
| 63 |
| 64 // Ensure that the pubspec looks newer than the lockfile. |
| 65 _touch("pubspec.yaml"); |
| 66 }); |
| 67 |
| 68 _requiresPubGet('The pubspec.yaml file has changed since the ' |
| 69 'pubspec.lock file was generated, please run "pub get" again.'); |
| 70 }); |
| 71 |
| 72 group("the lockfile has a dependency from the wrong source", () { |
| 73 setUp(() { |
| 74 d.dir(appPath, [ |
| 75 d.appPubspec({"foo": "1.0.0"}) |
| 76 ]).create(); |
| 77 |
| 78 pubGet(); |
| 79 |
| 80 createLockFile(appPath, sandbox: ["foo"]); |
| 81 |
| 82 // Ensure that the pubspec looks newer than the lockfile. |
| 83 _touch("pubspec.yaml"); |
| 84 }); |
| 85 |
| 86 _requiresPubGet('The pubspec.yaml file has changed since the ' |
| 87 'pubspec.lock file was generated, please run "pub get" again.'); |
| 88 }); |
| 89 |
| 90 group("the lockfile has a dependency from an unknown source", () { |
| 91 setUp(() { |
| 92 d.dir(appPath, [ |
| 93 d.appPubspec({"foo": "1.0.0"}) |
| 94 ]).create(); |
| 95 |
| 96 pubGet(); |
| 97 |
| 98 d.dir(appPath, [ |
| 99 d.file("pubspec.lock", yaml({ |
| 100 "packages": { |
| 101 "foo": { |
| 102 "description": "foo", |
| 103 "version": "1.0.0", |
| 104 "source": "sdk" |
| 105 } |
| 106 } |
| 107 })) |
| 108 ]).create(); |
| 109 |
| 110 // Ensure that the pubspec looks newer than the lockfile. |
| 111 _touch("pubspec.yaml"); |
| 112 }); |
| 113 |
| 114 _requiresPubGet('The pubspec.yaml file has changed since the ' |
| 115 'pubspec.lock file was generated, please run "pub get" again.'); |
| 116 }); |
| 117 |
| 118 group("the lockfile has a dependency with the wrong description", () { |
| 119 setUp(() { |
| 120 d.dir("bar", [ |
| 121 d.libPubspec("foo", "1.0.0") |
| 122 ]).create(); |
| 123 |
| 124 d.dir(appPath, [ |
| 125 d.appPubspec({"foo": {"path": "../bar"}}) |
| 126 ]).create(); |
| 127 |
| 128 pubGet(); |
| 129 |
| 130 createLockFile(appPath, sandbox: ["foo"]); |
| 131 |
| 132 // Ensure that the pubspec looks newer than the lockfile. |
| 133 _touch("pubspec.yaml"); |
| 134 }); |
| 135 |
| 136 _requiresPubGet('The pubspec.yaml file has changed since the ' |
| 137 'pubspec.lock file was generated, please run "pub get" again.'); |
| 138 }); |
| 139 |
| 140 group("the pubspec has an incompatible version of a dependency", () { |
| 141 setUp(() { |
| 142 d.dir(appPath, [ |
| 143 d.appPubspec({"foo": "1.0.0"}) |
| 144 ]).create(); |
| 145 |
| 146 pubGet(); |
| 147 |
| 148 d.dir(appPath, [ |
| 149 d.appPubspec({"foo": "2.0.0"}) |
| 150 ]).create(); |
| 151 |
| 152 // Ensure that the pubspec looks newer than the lockfile. |
| 153 _touch("pubspec.yaml"); |
| 154 }); |
| 155 |
| 156 _requiresPubGet('The pubspec.yaml file has changed since the ' |
| 157 'pubspec.lock file was generated, please run "pub get" again.'); |
| 158 }); |
| 159 |
| 160 group("the lockfile is pointing to an unavailable package with a newer " |
| 161 "pubspec", () { |
| 162 setUp(() { |
| 163 d.dir(appPath, [ |
| 164 d.appPubspec({"foo": "1.0.0"}) |
| 165 ]).create(); |
| 166 |
| 167 pubGet(); |
| 168 |
| 169 schedule(() => deleteEntry(p.join(sandboxDir, cachePath))); |
| 170 |
| 171 // Ensure that the pubspec looks newer than the lockfile. |
| 172 _touch("pubspec.yaml"); |
| 173 }); |
| 174 |
| 175 _requiresPubGet('The pubspec.yaml file has changed since the ' |
| 176 'pubspec.lock file was generated, please run "pub get" again.'); |
| 177 }); |
| 178 |
| 179 group("the lockfile is pointing to an unavailable package with an older " |
| 180 ".packages", () { |
| 181 setUp(() { |
| 182 d.dir(appPath, [ |
| 183 d.appPubspec({"foo": "1.0.0"}) |
| 184 ]).create(); |
| 185 |
| 186 pubGet(); |
| 187 |
| 188 schedule(() => deleteEntry(p.join(sandboxDir, cachePath))); |
| 189 |
| 190 // Ensure that the lockfile looks newer than the .packages file. |
| 191 _touch("pubspec.lock"); |
| 192 }); |
| 193 |
| 194 _requiresPubGet('The pubspec.lock file has changed since the .packages ' |
57 'file was generated, please run "pub get" again.'); | 195 'file was generated, please run "pub get" again.'); |
58 }); | 196 }); |
59 | 197 |
60 group("the lockfile is newer than the package spec", () { | 198 group("the lockfile has a package that the .packages file doesn't", () { |
61 setUp(() { | 199 setUp(() { |
62 schedule(() => _touch("pubspec.lock")); | 200 d.dir("foo", [ |
63 }); | 201 d.libPubspec("foo", "1.0.0") |
64 | 202 ]).create(); |
65 _forEveryCommand('The pubspec.lock file has changed since the .packages ' | 203 |
| 204 d.dir(appPath, [ |
| 205 d.appPubspec({"foo": {"path": "../foo"}}) |
| 206 ]).create(); |
| 207 |
| 208 pubGet(); |
| 209 |
| 210 createPackagesFile(appPath); |
| 211 |
| 212 // Ensure that the pubspec looks newer than the lockfile. |
| 213 _touch("pubspec.lock"); |
| 214 }); |
| 215 |
| 216 _requiresPubGet('The pubspec.lock file has changed since the .packages ' |
66 'file was generated, please run "pub get" again.'); | 217 'file was generated, please run "pub get" again.'); |
67 }); | 218 }); |
| 219 |
| 220 group("the .packages file has a package with a non-file URI", () { |
| 221 setUp(() { |
| 222 d.dir("foo", [ |
| 223 d.libPubspec("foo", "1.0.0") |
| 224 ]).create(); |
| 225 |
| 226 d.dir(appPath, [ |
| 227 d.appPubspec({"foo": {"path": "../foo"}}) |
| 228 ]).create(); |
| 229 |
| 230 pubGet(); |
| 231 |
| 232 d.dir(appPath, [ |
| 233 d.file(".packages", """ |
| 234 myapp:lib |
| 235 foo:http://example.com/ |
| 236 """) |
| 237 ]).create(); |
| 238 |
| 239 // Ensure that the pubspec looks newer than the lockfile. |
| 240 _touch("pubspec.lock"); |
| 241 }); |
| 242 |
| 243 _requiresPubGet('The pubspec.lock file has changed since the .packages ' |
| 244 'file was generated, please run "pub get" again.'); |
| 245 }); |
| 246 |
| 247 group("the .packages file points to the wrong place", () { |
| 248 setUp(() { |
| 249 d.dir("bar", [ |
| 250 d.libPubspec("foo", "1.0.0") |
| 251 ]).create(); |
| 252 |
| 253 d.dir(appPath, [ |
| 254 d.appPubspec({"foo": {"path": "../bar"}}) |
| 255 ]).create(); |
| 256 |
| 257 pubGet(); |
| 258 |
| 259 createPackagesFile(appPath, sandbox: ["foo"]); |
| 260 |
| 261 // Ensure that the pubspec looks newer than the lockfile. |
| 262 _touch("pubspec.lock"); |
| 263 }); |
| 264 |
| 265 _requiresPubGet('The pubspec.lock file has changed since the .packages ' |
| 266 'file was generated, please run "pub get" again.'); |
| 267 }); |
| 268 }); |
| 269 |
| 270 group("doesn't require the user to run pub get first if", () { |
| 271 group("the pubspec is older than the lockfile which is older than the " |
| 272 "packages file, even if the contents are wrong", () { |
| 273 setUp(() { |
| 274 d.dir(appPath, [ |
| 275 d.appPubspec({"foo": "1.0.0"}) |
| 276 ]).create(); |
| 277 |
| 278 _touch("pubspec.lock"); |
| 279 _touch(".packages"); |
| 280 }); |
| 281 |
| 282 _runsSuccessfully(runDeps: false); |
| 283 }); |
| 284 |
| 285 group("the pubspec is newer than the lockfile, but they're up-to-date", () { |
| 286 setUp(() { |
| 287 d.dir(appPath, [ |
| 288 d.appPubspec({"foo": "1.0.0"}) |
| 289 ]).create(); |
| 290 |
| 291 pubGet(); |
| 292 |
| 293 _touch("pubspec.yaml"); |
| 294 }); |
| 295 |
| 296 _runsSuccessfully(); |
| 297 }); |
| 298 |
| 299 group("the lockfile is newer than .packages, but they're up-to-date", () { |
| 300 setUp(() { |
| 301 d.dir(appPath, [ |
| 302 d.appPubspec({"foo": "1.0.0"}) |
| 303 ]).create(); |
| 304 |
| 305 pubGet(); |
| 306 |
| 307 _touch("pubspec.lock"); |
| 308 }); |
| 309 |
| 310 _runsSuccessfully(); |
| 311 }); |
68 }); | 312 }); |
69 } | 313 } |
70 | 314 |
71 /// Runs every command that care about the world being up-to-date, and asserts | 315 /// Runs every command that care about the world being up-to-date, and asserts |
72 /// that it prints [message] as part of its error. | 316 /// that it prints [message] as part of its error. |
73 void _forEveryCommand(String message) { | 317 void _requiresPubGet(String message) { |
74 for (var command in ["build", "serve", "run", "deps"]) { | 318 for (var command in ["build", "serve", "run", "deps"]) { |
75 integration("for pub $command", () { | 319 integration("for pub $command", () { |
76 var args = [command]; | 320 var args = [command]; |
77 if (command == "run") args.add("script"); | 321 if (command == "run") args.add("script"); |
78 | 322 |
79 var output; | |
80 var error; | |
81 if (command == "list-package-dirs") { | |
82 output = contains(JSON.encode(message)); | |
83 } else { | |
84 error = contains(message); | |
85 } | |
86 | |
87 schedulePub( | 323 schedulePub( |
88 args: args, | 324 args: args, |
89 output: output, | 325 error: contains(message), |
90 error: error, | |
91 exitCode: exit_codes.DATA); | 326 exitCode: exit_codes.DATA); |
92 }); | 327 }); |
93 } | 328 } |
94 } | 329 } |
95 | 330 |
| 331 /// Ensures that pub doesn't require "pub get" for the current package. |
| 332 /// |
| 333 /// If [runDeps] is false, `pub deps` isn't included in the test. This is |
| 334 /// sometimes not desirable, since it uses slightly stronger checks for pubspec |
| 335 /// and lockfile consistency. |
| 336 void _runsSuccessfully({bool runDeps: true}) { |
| 337 var commands = ["build", "serve", "run"]; |
| 338 if (runDeps) commands.add("deps"); |
| 339 |
| 340 for (var command in commands) { |
| 341 integration("for pub $command", () { |
| 342 var args = [command]; |
| 343 if (command == "run") args.add("bin/script.dart"); |
| 344 if (command == "serve") ; |
| 345 |
| 346 if (command != "serve") { |
| 347 schedulePub(args: args); |
| 348 } else { |
| 349 var pub = startPub(args: ["serve", "--port=0"]); |
| 350 pub.stdout.expect(consumeThrough(startsWith("Serving myapp web"))); |
| 351 pub.kill(); |
| 352 } |
| 353 |
| 354 schedule(() { |
| 355 // If pub determines that everything is up-to-date, it should set the |
| 356 // mtimes to indicate that. |
| 357 var pubspecModified = new File(p.join(sandboxDir, "myapp/pubspec.yaml")) |
| 358 .lastModifiedSync(); |
| 359 var lockFileModified = |
| 360 new File(p.join(sandboxDir, "myapp/pubspec.lock")) |
| 361 .lastModifiedSync(); |
| 362 var packagesModified = new File(p.join(sandboxDir, "myapp/.packages")) |
| 363 .lastModifiedSync(); |
| 364 |
| 365 expect(!pubspecModified.isAfter(lockFileModified), isTrue); |
| 366 expect(!lockFileModified.isAfter(packagesModified), isTrue); |
| 367 }, "testing last-modified times"); |
| 368 }); |
| 369 } |
| 370 } |
| 371 |
| 372 /// Schedules a non-semantic modification to [path]. |
96 void _touch(String path) { | 373 void _touch(String path) { |
97 path = p.join(sandboxDir, "myapp", path); | 374 schedule(() async { |
98 writeTextFile(path, readTextFile(path) + " "); | 375 // Delay a bit to make sure the modification times are noticeably different. |
| 376 // 1s seems to be the finest granularity that dart:io reports. |
| 377 await new Future.delayed(new Duration(seconds: 1)); |
| 378 |
| 379 path = p.join(sandboxDir, "myapp", path); |
| 380 touch(path); |
| 381 }, "touching $path"); |
99 } | 382 } |
OLD | NEW |