Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(26)

Side by Side Diff: pkg/barback/test/package_graph/errors_test.dart

Issue 22685006: Better handling of asset collisions in barback. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, 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 library barback.test.package_graph.source_test; 5 library barback.test.package_graph.source_test;
6 6
7 import 'dart:async'; 7 import 'dart:async';
8 8
9 import 'package:barback/barback.dart'; 9 import 'package:barback/barback.dart';
10 import 'package:barback/src/utils.dart';
10 import 'package:scheduled_test/scheduled_test.dart'; 11 import 'package:scheduled_test/scheduled_test.dart';
11 12
12 import '../utils.dart'; 13 import '../utils.dart';
13 14
14 main() { 15 main() {
15 initConfig(); 16 initConfig();
16 17
17 test("errors if two transformers output the same file", () { 18 test("errors if two transformers output the same file", () {
18 initGraph(["app|foo.a"], {"app": [ 19 initGraph(["app|foo.a"], {"app": [
19 [ 20 [
(...skipping 11 matching lines...) Expand all
31 initGraph(["app|foo.a", "app|foo.b"], {"app": [ 32 initGraph(["app|foo.a", "app|foo.b"], {"app": [
32 [ 33 [
33 new RewriteTransformer("a", "c"), 34 new RewriteTransformer("a", "c"),
34 new RewriteTransformer("b", "c") 35 new RewriteTransformer("b", "c")
35 ] 36 ]
36 ]}); 37 ]});
37 updateSources(["app|foo.a"]); 38 updateSources(["app|foo.a"]);
38 expectAsset("app|foo.c", "foo.c"); 39 expectAsset("app|foo.c", "foo.c");
39 buildShouldSucceed(); 40 buildShouldSucceed();
40 41
41 schedule(() => updateSources(["app|foo.b"])); 42 updateSources(["app|foo.b"]);
42 buildShouldFail([isAssetCollisionException("app|foo.c")]); 43 buildShouldFail([isAssetCollisionException("app|foo.c")]);
43 }); 44 });
44 45
45 test("does not report asset not found errors in results", () { 46 test("does not report asset not found errors in results", () {
46 initGraph(["app|bar.txt"]); 47 initGraph(["app|bar.txt"]);
47 48
48 // Trigger a build. 49 // Trigger a build.
49 updateSources(["app|bar.txt"]); 50 updateSources(["app|bar.txt"]);
50 51
51 expectNoAsset("app|foo.txt"); 52 expectNoAsset("app|foo.txt");
52 buildShouldSucceed(); 53 buildShouldSucceed();
53 }); 54 });
54 55
55 test("reports an error for an unprovided package", () { 56 test("reports an error for an unprovided package", () {
56 initGraph(); 57 initGraph();
57 expect(() => updateSources(["unknown|foo.txt"]), throwsArgumentError); 58 expect(() => updateSourcesSync(["unknown|foo.txt"]), throwsArgumentError);
58 }); 59 });
59 60
60 test("reports an error for an unprovided source", () { 61 test("reports an error for an unprovided source", () {
61 initGraph(["app|known.txt"]); 62 initGraph(["app|known.txt"]);
62 updateSources(["app|unknown.txt"]); 63 updateSources(["app|unknown.txt"]);
63 64
64 buildShouldFail([isAssetNotFoundException("app|unknown.txt")]); 65 buildShouldFail([isAssetNotFoundException("app|unknown.txt")]);
65 }); 66 });
66 67
67 test("reports missing input errors in results", () { 68 test("reports missing input errors in results", () {
68 initGraph({"app|a.txt": "a.inc"}, {"app": [ 69 initGraph({"app|a.txt": "a.inc"}, {"app": [
69 [new ManyToOneTransformer("txt")] 70 [new ManyToOneTransformer("txt")]
70 ]}); 71 ]});
71 72
73 updateSources(["app|a.txt"]);
74 expectNoAsset("app|a.out");
72 buildShouldFail([isMissingInputException("app|a.inc")]); 75 buildShouldFail([isMissingInputException("app|a.inc")]);
73
74 updateSources(["app|a.txt"]);
75
76 expectNoAsset("app|a.out");
77 }); 76 });
78 77
79 test("reports an error if a transformer emits an asset for another package", 78 test("reports an error if a transformer emits an asset for another package",
80 () { 79 () {
81 initGraph(["app|foo.txt"], { 80 initGraph(["app|foo.txt"], {
82 "app": [[new CreateAssetTransformer("wrong|foo.txt")]] 81 "app": [[new CreateAssetTransformer("wrong|foo.txt")]]
83 }); 82 });
84 83
84 updateSources(["app|foo.txt"]);
85 buildShouldFail([isInvalidOutputException("app", "wrong|foo.txt")]); 85 buildShouldFail([isInvalidOutputException("app", "wrong|foo.txt")]);
86
87 updateSources(["app|foo.txt"]);
88 }); 86 });
89 87
90 test("fails if a non-primary input is removed", () { 88 test("fails if a non-primary input is removed", () {
91 initGraph({ 89 initGraph({
92 "app|a.txt": "a.inc,b.inc,c.inc", 90 "app|a.txt": "a.inc,b.inc,c.inc",
93 "app|a.inc": "a", 91 "app|a.inc": "a",
94 "app|b.inc": "b", 92 "app|b.inc": "b",
95 "app|c.inc": "c" 93 "app|c.inc": "c"
96 }, {"app": [ 94 }, {"app": [
97 [new ManyToOneTransformer("txt")] 95 [new ManyToOneTransformer("txt")]
98 ]}); 96 ]});
99 97
100 updateSources(["app|a.txt", "app|a.inc", "app|b.inc", "app|c.inc"]); 98 updateSources(["app|a.txt", "app|a.inc", "app|b.inc", "app|c.inc"]);
101 expectAsset("app|a.out", "abc"); 99 expectAsset("app|a.out", "abc");
102 buildShouldSucceed(); 100 buildShouldSucceed();
103 101
104 schedule(() { 102 removeSources(["app|b.inc"]);
105 removeSources(["app|b.inc"]);
106 });
107
108 buildShouldFail([isMissingInputException("app|b.inc")]); 103 buildShouldFail([isMissingInputException("app|b.inc")]);
109 expectNoAsset("app|a.out"); 104 expectNoAsset("app|a.out");
110 }); 105 });
111 106
112 test("catches transformer exceptions and reports them", () { 107 test("catches transformer exceptions and reports them", () {
113 initGraph(["app|foo.txt"], {"app": [ 108 initGraph(["app|foo.txt"], {"app": [
114 [new BadTransformer(["app|foo.out"])] 109 [new BadTransformer(["app|foo.out"])]
115 ]}); 110 ]});
116 111
117 schedule(() { 112 updateSources(["app|foo.txt"]);
118 updateSources(["app|foo.txt"]);
119 });
120
121 expectNoAsset("app|foo.out"); 113 expectNoAsset("app|foo.out");
122
123 buildShouldFail([equals(BadTransformer.ERROR)]); 114 buildShouldFail([equals(BadTransformer.ERROR)]);
124 }); 115 });
125 116
126 test("doesn't yield a source if a transform fails on it", () { 117 test("doesn't yield a source if a transform fails on it", () {
127 initGraph(["app|foo.txt"], {"app": [ 118 initGraph(["app|foo.txt"], {"app": [
128 [new BadTransformer(["app|foo.txt"])] 119 [new BadTransformer(["app|foo.txt"])]
129 ]}); 120 ]});
130 121
131 schedule(() { 122 updateSources(["app|foo.txt"]);
132 updateSources(["app|foo.txt"]);
133 });
134
135 expectNoAsset("app|foo.txt"); 123 expectNoAsset("app|foo.txt");
136 }); 124 });
137 125
138 test("catches errors even if nothing is waiting for process results", () { 126 test("catches errors even if nothing is waiting for process results", () {
139 initGraph(["app|foo.txt"], {"app": [[new BadTransformer([])]]}); 127 initGraph(["app|foo.txt"], {"app": [[new BadTransformer([])]]});
140 128
141 schedule(() { 129 updateSources(["app|foo.txt"]);
142 updateSources(["app|foo.txt"]);
143 });
144
145 // Note: No asset requests here. 130 // Note: No asset requests here.
146
147 buildShouldFail([equals(BadTransformer.ERROR)]); 131 buildShouldFail([equals(BadTransformer.ERROR)]);
148 }); 132 });
149 133
150 test("discards outputs from failed transforms", () { 134 test("discards outputs from failed transforms", () {
151 initGraph(["app|foo.txt"], {"app": [ 135 initGraph(["app|foo.txt"], {"app": [
152 [new BadTransformer(["a.out", "b.out"])] 136 [new BadTransformer(["a.out", "b.out"])]
153 ]}); 137 ]});
154 138
155 schedule(() { 139 updateSources(["app|foo.txt"]);
156 updateSources(["app|foo.txt"]);
157 });
158
159 expectNoAsset("app|a.out"); 140 expectNoAsset("app|a.out");
160 }); 141 });
161 142
162 test("fails if only one package fails", () { 143 test("fails if only one package fails", () {
163 initGraph(["pkg1|foo.txt", "pkg2|foo.txt"], 144 initGraph(["pkg1|foo.txt", "pkg2|foo.txt"],
164 {"pkg1": [[new BadTransformer([])]]}); 145 {"pkg1": [[new BadTransformer([])]]});
165 146
166 schedule(() { 147 updateSources(["pkg1|foo.txt", "pkg2|foo.txt"]);
167 updateSources(["pkg1|foo.txt", "pkg2|foo.txt"]);
168 });
169
170 expectAsset("pkg2|foo.txt", "foo"); 148 expectAsset("pkg2|foo.txt", "foo");
171 buildShouldFail([equals(BadTransformer.ERROR)]); 149 buildShouldFail([equals(BadTransformer.ERROR)]);
172 }); 150 });
173 151
174 test("emits multiple failures if multiple packages fail", () { 152 test("emits multiple failures if multiple packages fail", () {
175 initGraph(["pkg1|foo.txt", "pkg2|foo.txt"], { 153 initGraph(["pkg1|foo.txt", "pkg2|foo.txt"], {
176 "pkg1": [[new BadTransformer([])]], 154 "pkg1": [[new BadTransformer([])]],
177 "pkg2": [[new BadTransformer([])]] 155 "pkg2": [[new BadTransformer([])]]
178 }); 156 });
179 157
180 schedule(() { 158 updateSources(["pkg1|foo.txt", "pkg2|foo.txt"]);
181 updateSources(["pkg1|foo.txt", "pkg2|foo.txt"]);
182 });
183
184 buildShouldFail([ 159 buildShouldFail([
185 equals(BadTransformer.ERROR), 160 equals(BadTransformer.ERROR),
186 equals(BadTransformer.ERROR) 161 equals(BadTransformer.ERROR)
187 ]); 162 ]);
188 }); 163 });
189 164
190 test("an error loading an asset removes the asset from the graph", () { 165 test("an error loading an asset removes the asset from the graph", () {
191 initGraph(["app|foo.txt"]); 166 initGraph(["app|foo.txt"]);
192 167
193 setAssetError("app|foo.txt"); 168 setAssetError("app|foo.txt");
194 schedule(() => updateSources(["app|foo.txt"])); 169 updateSources(["app|foo.txt"]);
195 expectNoAsset("app|foo.txt"); 170 expectNoAsset("app|foo.txt");
196 buildShouldFail([isMockLoadException("app|foo.txt")]); 171 buildShouldFail([isMockLoadException("app|foo.txt")]);
197 }); 172 });
173
174 test("a collision returns the first-produced output", () {
175 var rewrite1 = new RewriteTransformer("one", "out");
176 var rewrite2 = new RewriteTransformer("two", "out");
177 initGraph({
178 "app|foo.one": "one",
179 "app|foo.two": "two"
180 }, {"app": [[rewrite1, rewrite2]]});
181
182 rewrite1.pauseApply();
183 updateSources(["app|foo.one", "app|foo.two"]);
184 // Wait long enough to ensure that rewrite2 has completed.
185 schedule(pumpEventQueue);
186
187 rewrite1.resumeApply();
188 expectAsset("app|foo.out", "two.out");
189 buildShouldFail([isAssetCollisionException("app|foo.out")]);
190
191 // Even after the collision is discovered, the first-produced output should
192 // be returned.
193 expectAsset("app|foo.out", "two.out");
194
195 // Even if the other output is updated more recently, the first output
196 // should continue to take precedence.
197 updateSources(["app|foo.one"]);
198 expectAsset("app|foo.out", "two.out");
199 });
200
201 test("a collision that is later resolved produces an output", () {
202 initGraph({
203 "app|foo.one": "one",
204 "app|foo.two": "two"
205 }, {"app": [
206 [
207 new RewriteTransformer("one", "out"),
208 new RewriteTransformer("two", "out")
209 ]
210 ]});
211
212 updateSources(["app|foo.one"]);
213 expectAsset("app|foo.out", "one.out");
214 buildShouldSucceed();
215
216 updateSources(["app|foo.two"]);
217 expectAsset("app|foo.out", "one.out");
218 buildShouldFail([isAssetCollisionException("app|foo.out")]);
219
220 removeSources(["app|foo.one"]);
221 expectAsset("app|foo.out", "two.out");
222 buildShouldSucceed();
223 });
224
225 test("a collision that is later resolved runs transforms", () {
226 initGraph({
227 "app|foo.one": "one",
228 "app|foo.two": "two"
229 }, {"app": [
230 [
231 new RewriteTransformer("one", "mid"),
232 new RewriteTransformer("two", "mid")
233 ],
234 [new RewriteTransformer("mid", "out")]
235 ]});
236
237 updateSources(["app|foo.one"]);
238 expectAsset("app|foo.out", "one.mid.out");
239 buildShouldSucceed();
240
241 updateSources(["app|foo.two"]);
242 expectAsset("app|foo.out", "one.mid.out");
243 buildShouldFail([isAssetCollisionException("app|foo.mid")]);
244
245 removeSources(["app|foo.one"]);
246 expectAsset("app|foo.out", "two.mid.out");
247 buildShouldSucceed();
248 });
249
250 test("a collision that is partially resolved returns the second completed "
251 "output", () {
252 var rewrite1 = new RewriteTransformer("one", "out");
253 var rewrite2 = new RewriteTransformer("two", "out");
254 var rewrite3 = new RewriteTransformer("three", "out");
255 initGraph({
256 "app|foo.one": "one",
257 "app|foo.two": "two",
258 "app|foo.three": "three"
259 }, {"app": [[rewrite1, rewrite2, rewrite3]]});
260
261 // Make rewrite3 the most-recently-completed transformer from the first run.
262 rewrite2.pauseApply();
263 rewrite3.pauseApply();
264 updateSources(["app|foo.one", "app|foo.two", "app|foo.three"]);
265 schedule(pumpEventQueue);
266 rewrite2.resumeApply();
267 schedule(pumpEventQueue);
268 rewrite3.resumeApply();
269 buildShouldFail([isAssetCollisionException("app|foo.out")]);
270
271 // Then update rewrite3 in a separate build. rewrite2 should still be the
272 // next version of foo.out in line.
273 // TODO(nweiz): Should this emit a collision error as well? Or should they
274 // only be emitted when a file is added or removed?
275 updateSources(["app|foo.three"]);
276 buildShouldSucceed();
277
278 removeSources(["app|foo.one"]);
279 expectAsset("app|foo.out", "two.out");
280 buildShouldFail([isAssetCollisionException("app|foo.out")]);
281 });
Bob Nystrom 2013/08/09 22:44:43 Great tests!
nweiz 2013/08/12 21:15:44 Thanks!
198 } 282 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698