• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "gn/ninja_create_bundle_target_writer.h"
6 
7 #include <algorithm>
8 #include <memory>
9 #include <sstream>
10 
11 #include "gn/target.h"
12 #include "gn/test_with_scope.h"
13 #include "util/test/test.h"
14 
15 namespace {
16 
SetupBundleDataDir(BundleData * bundle_data,const std::string & root_dir)17 void SetupBundleDataDir(BundleData* bundle_data, const std::string& root_dir) {
18   std::string bundle_root_dir = root_dir + "/bar.bundle";
19   bundle_data->root_dir() = SourceDir(bundle_root_dir);
20   bundle_data->contents_dir() = SourceDir(bundle_root_dir + "/Contents");
21   bundle_data->resources_dir() =
22       SourceDir(bundle_data->contents_dir().value() + "/Resources");
23   bundle_data->executable_dir() =
24       SourceDir(bundle_data->contents_dir().value() + "/MacOS");
25 }
26 
NewAction(const TestWithScope & setup)27 std::unique_ptr<Target> NewAction(const TestWithScope& setup) {
28   Err err;
29   auto action = std::make_unique<Target>(setup.settings(),
30                                          Label(SourceDir("//foo/"), "bar"));
31   action->set_output_type(Target::ACTION);
32   action->visibility().SetPublic();
33   action->action_values().set_script(SourceFile("//foo/script.py"));
34 
35   action->action_values().outputs() =
36       SubstitutionList::MakeForTest("//out/Debug/foo.out");
37 
38   action->SetToolchain(setup.toolchain());
39   return action;
40 }
41 
42 }  // namespace
43 
44 // Tests multiple files with an output pattern.
TEST(NinjaCreateBundleTargetWriter,Run)45 TEST(NinjaCreateBundleTargetWriter, Run) {
46   Err err;
47   TestWithScope setup;
48 
49   std::unique_ptr<Target> action = NewAction(setup);
50   ASSERT_TRUE(action->OnResolved(&err)) << err.message();
51 
52   Target bundle_data(setup.settings(), Label(SourceDir("//foo/"), "data"));
53   bundle_data.set_output_type(Target::BUNDLE_DATA);
54   bundle_data.sources().push_back(SourceFile("//foo/input1.txt"));
55   bundle_data.sources().push_back(SourceFile("//foo/input2.txt"));
56   bundle_data.action_values().outputs() = SubstitutionList::MakeForTest(
57       "{{bundle_resources_dir}}/{{source_file_part}}");
58   bundle_data.SetToolchain(setup.toolchain());
59   bundle_data.visibility().SetPublic();
60   ASSERT_TRUE(bundle_data.OnResolved(&err));
61 
62   Target create_bundle(
63       setup.settings(),
64       Label(SourceDir("//baz/"), "bar", setup.toolchain()->label().dir(),
65             setup.toolchain()->label().name()));
66   SetupBundleDataDir(&create_bundle.bundle_data(), "//out/Debug");
67   create_bundle.set_output_type(Target::CREATE_BUNDLE);
68   create_bundle.private_deps().push_back(LabelTargetPair(&bundle_data));
69   create_bundle.private_deps().push_back(LabelTargetPair(action.get()));
70   create_bundle.SetToolchain(setup.toolchain());
71   ASSERT_TRUE(create_bundle.OnResolved(&err));
72 
73   std::ostringstream out;
74   NinjaCreateBundleTargetWriter writer(&create_bundle, out);
75   writer.Run();
76 
77   const char expected[] =
78       "build obj/baz/bar.inputdeps.stamp: stamp obj/foo/bar.stamp "
79       "obj/foo/data.stamp\n"
80       "build bar.bundle/Contents/Resources/input1.txt: copy_bundle_data "
81       "../../foo/input1.txt || obj/baz/bar.inputdeps.stamp\n"
82       "build bar.bundle/Contents/Resources/input2.txt: copy_bundle_data "
83       "../../foo/input2.txt || obj/baz/bar.inputdeps.stamp\n"
84       "build obj/baz/bar.stamp: stamp "
85       "bar.bundle/Contents/Resources/input1.txt "
86       "bar.bundle/Contents/Resources/input2.txt"
87       " || obj/baz/bar.inputdeps.stamp\n"
88       "build bar.bundle: phony obj/baz/bar.stamp\n";
89   std::string out_str = out.str();
90   EXPECT_EQ(expected, out_str);
91 }
92 
93 // Tests creating a bundle in a sub-directory of $root_out_dir.
TEST(NinjaCreateBundleTargetWriter,InSubDirectory)94 TEST(NinjaCreateBundleTargetWriter, InSubDirectory) {
95   Err err;
96   TestWithScope setup;
97 
98   std::unique_ptr<Target> action = NewAction(setup);
99   ASSERT_TRUE(action->OnResolved(&err)) << err.message();
100 
101   Target bundle_data(setup.settings(), Label(SourceDir("//foo/"), "data"));
102   bundle_data.set_output_type(Target::BUNDLE_DATA);
103   bundle_data.sources().push_back(SourceFile("//foo/input1.txt"));
104   bundle_data.sources().push_back(SourceFile("//foo/input2.txt"));
105   bundle_data.action_values().outputs() = SubstitutionList::MakeForTest(
106       "{{bundle_resources_dir}}/{{source_file_part}}");
107   bundle_data.SetToolchain(setup.toolchain());
108   bundle_data.visibility().SetPublic();
109   ASSERT_TRUE(bundle_data.OnResolved(&err));
110 
111   Target create_bundle(
112       setup.settings(),
113       Label(SourceDir("//baz/"), "bar", setup.toolchain()->label().dir(),
114             setup.toolchain()->label().name()));
115   SetupBundleDataDir(&create_bundle.bundle_data(), "//out/Debug/gen");
116   create_bundle.set_output_type(Target::CREATE_BUNDLE);
117   create_bundle.private_deps().push_back(LabelTargetPair(&bundle_data));
118   create_bundle.private_deps().push_back(LabelTargetPair(action.get()));
119   create_bundle.SetToolchain(setup.toolchain());
120   ASSERT_TRUE(create_bundle.OnResolved(&err));
121 
122   std::ostringstream out;
123   NinjaCreateBundleTargetWriter writer(&create_bundle, out);
124   writer.Run();
125 
126   const char expected[] =
127       "build obj/baz/bar.inputdeps.stamp: stamp obj/foo/bar.stamp "
128       "obj/foo/data.stamp\n"
129       "build gen/bar.bundle/Contents/Resources/input1.txt: copy_bundle_data "
130       "../../foo/input1.txt || obj/baz/bar.inputdeps.stamp\n"
131       "build gen/bar.bundle/Contents/Resources/input2.txt: copy_bundle_data "
132       "../../foo/input2.txt || obj/baz/bar.inputdeps.stamp\n"
133       "build obj/baz/bar.stamp: stamp "
134       "gen/bar.bundle/Contents/Resources/input1.txt "
135       "gen/bar.bundle/Contents/Resources/input2.txt || "
136       "obj/baz/bar.inputdeps.stamp\n"
137       "build gen/bar.bundle: phony obj/baz/bar.stamp\n";
138   std::string out_str = out.str();
139   EXPECT_EQ(expected, out_str);
140 }
141 
142 // Tests empty asset catalog with partial_info_plist property defined.
TEST(NinjaCreateBundleTargetWriter,JustPartialInfoPlist)143 TEST(NinjaCreateBundleTargetWriter, JustPartialInfoPlist) {
144   Err err;
145   TestWithScope setup;
146 
147   std::unique_ptr<Target> action = NewAction(setup);
148   ASSERT_TRUE(action->OnResolved(&err)) << err.message();
149 
150   Target create_bundle(
151       setup.settings(),
152       Label(SourceDir("//baz/"), "bar", setup.toolchain()->label().dir(),
153             setup.toolchain()->label().name()));
154   SetupBundleDataDir(&create_bundle.bundle_data(), "//out/Debug");
155   create_bundle.set_output_type(Target::CREATE_BUNDLE);
156   create_bundle.private_deps().push_back(LabelTargetPair(action.get()));
157   create_bundle.bundle_data().product_type().assign("com.apple.product-type");
158   create_bundle.bundle_data().set_partial_info_plist(
159       SourceFile("//out/Debug/baz/bar/bar_partial_info.plist"));
160   create_bundle.SetToolchain(setup.toolchain());
161   ASSERT_TRUE(create_bundle.OnResolved(&err));
162 
163   std::ostringstream out;
164   NinjaCreateBundleTargetWriter writer(&create_bundle, out);
165   writer.Run();
166 
167   const char expected[] =
168       "build baz/bar/bar_partial_info.plist: stamp || obj/foo/bar.stamp\n"
169       "build obj/baz/bar.stamp: stamp "
170       "baz/bar/bar_partial_info.plist || obj/foo/bar.stamp\n"
171       "build bar.bundle: phony obj/baz/bar.stamp\n";
172   std::string out_str = out.str();
173   EXPECT_EQ(expected, out_str);
174 }
175 
176 // Tests multiple files from asset catalog.
TEST(NinjaCreateBundleTargetWriter,AssetCatalog)177 TEST(NinjaCreateBundleTargetWriter, AssetCatalog) {
178   Err err;
179   TestWithScope setup;
180 
181   std::unique_ptr<Target> action = NewAction(setup);
182   ASSERT_TRUE(action->OnResolved(&err)) << err.message();
183 
184   Target bundle_data(setup.settings(), Label(SourceDir("//foo/"), "data"));
185   bundle_data.set_output_type(Target::BUNDLE_DATA);
186   bundle_data.sources().push_back(
187       SourceFile("//foo/Foo.xcassets/Contents.json"));
188   bundle_data.sources().push_back(
189       SourceFile("//foo/Foo.xcassets/foo.colorset/Contents.json"));
190   bundle_data.sources().push_back(
191       SourceFile("//foo/Foo.xcassets/foo.imageset/Contents.json"));
192   bundle_data.sources().push_back(
193       SourceFile("//foo/Foo.xcassets/foo.imageset/FooIcon-29.png"));
194   bundle_data.sources().push_back(
195       SourceFile("//foo/Foo.xcassets/foo.imageset/FooIcon-29@2x.png"));
196   bundle_data.sources().push_back(
197       SourceFile("//foo/Foo.xcassets/foo.imageset/FooIcon-29@3x.png"));
198   bundle_data.sources().push_back(
199       SourceFile("//foo/Foo.xcassets/foo.dataset/Contents.json"));
200   bundle_data.sources().push_back(
201       SourceFile("//foo/Foo.xcassets/foo.dataset/FooScript.js"));
202   bundle_data.action_values().outputs() = SubstitutionList::MakeForTest(
203       "{{bundle_resources_dir}}/{{source_file_part}}");
204   bundle_data.SetToolchain(setup.toolchain());
205   bundle_data.visibility().SetPublic();
206   ASSERT_TRUE(bundle_data.OnResolved(&err));
207 
208   Target create_bundle(
209       setup.settings(),
210       Label(SourceDir("//baz/"), "bar", setup.toolchain()->label().dir(),
211             setup.toolchain()->label().name()));
212   SetupBundleDataDir(&create_bundle.bundle_data(), "//out/Debug");
213   create_bundle.set_output_type(Target::CREATE_BUNDLE);
214   create_bundle.private_deps().push_back(LabelTargetPair(&bundle_data));
215   create_bundle.private_deps().push_back(LabelTargetPair(action.get()));
216   create_bundle.bundle_data().product_type().assign("com.apple.product-type");
217   create_bundle.bundle_data().xcasset_compiler_flags() =
218       SubstitutionList::MakeForTest("--app-icon", "foo");
219 
220   create_bundle.SetToolchain(setup.toolchain());
221   ASSERT_TRUE(create_bundle.OnResolved(&err));
222 
223   std::ostringstream out;
224   NinjaCreateBundleTargetWriter writer(&create_bundle, out);
225   writer.Run();
226 
227   const char expected[] =
228       "build obj/baz/bar.inputdeps.stamp: stamp obj/foo/bar.stamp "
229       "obj/foo/data.stamp\n"
230       "build bar.bundle/Contents/Resources/Assets.car: compile_xcassets "
231       "../../foo/Foo.xcassets | obj/foo/data.stamp || "
232       "obj/baz/bar.inputdeps.stamp\n"
233       "  product_type = com.apple.product-type\n"
234       "  xcasset_compiler_flags = --app-icon foo\n"
235       "build obj/baz/bar.stamp: stamp "
236       "bar.bundle/Contents/Resources/Assets.car || "
237       "obj/baz/bar.inputdeps.stamp\n"
238       "build bar.bundle: phony obj/baz/bar.stamp\n";
239   std::string out_str = out.str();
240   EXPECT_EQ(expected, out_str);
241 }
242 
243 // Tests that the phony target for the top-level bundle directory is generated
244 // correctly.
TEST(NinjaCreateBundleTargetWriter,PhonyTarget)245 TEST(NinjaCreateBundleTargetWriter, PhonyTarget) {
246   Err err;
247   TestWithScope setup;
248 
249   Target create_bundle(
250       setup.settings(),
251       Label(SourceDir("//baz/"), "bar", setup.toolchain()->label().dir(),
252             setup.toolchain()->label().name()));
253   SetupBundleDataDir(&create_bundle.bundle_data(), "//out/Debug");
254   create_bundle.set_output_type(Target::CREATE_BUNDLE);
255   create_bundle.SetToolchain(setup.toolchain());
256   ASSERT_TRUE(create_bundle.OnResolved(&err));
257 
258   std::ostringstream out;
259   NinjaCreateBundleTargetWriter writer(&create_bundle, out);
260   writer.Run();
261 
262   const char expected[] =
263       "build obj/baz/bar.stamp: stamp\n"
264       "build bar.bundle: phony obj/baz/bar.stamp\n";
265   std::string out_str = out.str();
266   EXPECT_EQ(expected, out_str);
267 }
268 
269 // Tests complex target with multiple bundle_data sources, including
270 // some asset catalog.
TEST(NinjaCreateBundleTargetWriter,Complex)271 TEST(NinjaCreateBundleTargetWriter, Complex) {
272   Err err;
273   TestWithScope setup;
274 
275   std::unique_ptr<Target> action = NewAction(setup);
276   ASSERT_TRUE(action->OnResolved(&err)) << err.message();
277 
278   Target bundle_data0(setup.settings(),
279                       Label(SourceDir("//qux/"), "info_plist"));
280   bundle_data0.set_output_type(Target::BUNDLE_DATA);
281   bundle_data0.sources().push_back(SourceFile("//qux/qux-Info.plist"));
282   bundle_data0.action_values().outputs() =
283       SubstitutionList::MakeForTest("{{bundle_contents_dir}}/Info.plist");
284   bundle_data0.SetToolchain(setup.toolchain());
285   bundle_data0.visibility().SetPublic();
286   ASSERT_TRUE(bundle_data0.OnResolved(&err));
287 
288   Target bundle_data1(setup.settings(), Label(SourceDir("//foo/"), "data"));
289   bundle_data1.set_output_type(Target::BUNDLE_DATA);
290   bundle_data1.sources().push_back(SourceFile("//foo/input1.txt"));
291   bundle_data1.sources().push_back(SourceFile("//foo/input2.txt"));
292   bundle_data1.action_values().outputs() = SubstitutionList::MakeForTest(
293       "{{bundle_resources_dir}}/{{source_file_part}}");
294   bundle_data1.SetToolchain(setup.toolchain());
295   bundle_data1.visibility().SetPublic();
296   ASSERT_TRUE(bundle_data1.OnResolved(&err));
297 
298   Target bundle_data2(setup.settings(), Label(SourceDir("//foo/"), "assets"));
299   bundle_data2.set_output_type(Target::BUNDLE_DATA);
300   bundle_data2.sources().push_back(
301       SourceFile("//foo/Foo.xcassets/Contents.json"));
302   bundle_data2.sources().push_back(
303       SourceFile("//foo/Foo.xcassets/foo.colorset/Contents.json"));
304   bundle_data2.sources().push_back(
305       SourceFile("//foo/Foo.xcassets/foo.imageset/Contents.json"));
306   bundle_data2.sources().push_back(
307       SourceFile("//foo/Foo.xcassets/foo.imageset/FooIcon-29.png"));
308   bundle_data2.sources().push_back(
309       SourceFile("//foo/Foo.xcassets/foo.imageset/FooIcon-29@2x.png"));
310   bundle_data2.sources().push_back(
311       SourceFile("//foo/Foo.xcassets/foo.imageset/FooIcon-29@3x.png"));
312   bundle_data2.sources().push_back(
313       SourceFile("//foo/Foo.xcassets/foo.dataset/Contents.json"));
314   bundle_data2.sources().push_back(
315       SourceFile("//foo/Foo.xcassets/foo.dataset/FooScript.js"));
316   bundle_data2.action_values().outputs() = SubstitutionList::MakeForTest(
317       "{{bundle_resources_dir}}/{{source_file_part}}");
318   bundle_data2.SetToolchain(setup.toolchain());
319   bundle_data2.visibility().SetPublic();
320   ASSERT_TRUE(bundle_data2.OnResolved(&err));
321 
322   Target bundle_data3(setup.settings(), Label(SourceDir("//quz/"), "assets"));
323   bundle_data3.set_output_type(Target::BUNDLE_DATA);
324   bundle_data3.sources().push_back(
325       SourceFile("//quz/Quz.xcassets/Contents.json"));
326   bundle_data3.sources().push_back(
327       SourceFile("//quz/Quz.xcassets/quz.imageset/Contents.json"));
328   bundle_data3.sources().push_back(
329       SourceFile("//quz/Quz.xcassets/quz.imageset/QuzIcon-29.png"));
330   bundle_data3.sources().push_back(
331       SourceFile("//quz/Quz.xcassets/quz.imageset/QuzIcon-29@2x.png"));
332   bundle_data3.sources().push_back(
333       SourceFile("//quz/Quz.xcassets/quz.imageset/QuzIcon-29@3x.png"));
334   bundle_data3.sources().push_back(
335       SourceFile("//quz/Quz.xcassets/quz.dataset/Contents.json"));
336   bundle_data3.sources().push_back(
337       SourceFile("//quz/Quz.xcassets/quz.dataset/QuzScript.js"));
338   bundle_data3.action_values().outputs() = SubstitutionList::MakeForTest(
339       "{{bundle_resources_dir}}/{{source_file_part}}");
340   bundle_data3.SetToolchain(setup.toolchain());
341   bundle_data3.visibility().SetPublic();
342   ASSERT_TRUE(bundle_data3.OnResolved(&err));
343 
344   Target bundle_data4(setup.settings(), Label(SourceDir("//biz/"), "assets"));
345   bundle_data4.set_output_type(Target::BUNDLE_DATA);
346   bundle_data4.sources().push_back(
347       SourceFile("//biz/Biz.xcassets/Contents.json"));
348   bundle_data4.sources().push_back(
349       SourceFile("//biz/Biz.xcassets/biz.colorset/Contents.json"));
350   bundle_data4.action_values().outputs() = SubstitutionList::MakeForTest(
351       "{{bundle_resources_dir}}/{{source_file_part}}");
352   bundle_data4.SetToolchain(setup.toolchain());
353   bundle_data4.visibility().SetPublic();
354   ASSERT_TRUE(bundle_data4.OnResolved(&err));
355 
356   Target create_bundle(
357       setup.settings(),
358       Label(SourceDir("//baz/"), "bar", setup.toolchain()->label().dir(),
359             setup.toolchain()->label().name()));
360   SetupBundleDataDir(&create_bundle.bundle_data(), "//out/Debug");
361   create_bundle.set_output_type(Target::CREATE_BUNDLE);
362   create_bundle.private_deps().push_back(LabelTargetPair(&bundle_data0));
363   create_bundle.private_deps().push_back(LabelTargetPair(&bundle_data1));
364   create_bundle.private_deps().push_back(LabelTargetPair(&bundle_data2));
365   create_bundle.private_deps().push_back(LabelTargetPair(&bundle_data3));
366   create_bundle.private_deps().push_back(LabelTargetPair(&bundle_data4));
367   create_bundle.private_deps().push_back(LabelTargetPair(action.get()));
368   create_bundle.bundle_data().product_type().assign("com.apple.product-type");
369   create_bundle.bundle_data().set_partial_info_plist(
370       SourceFile("//out/Debug/baz/bar/bar_partial_info.plist"));
371   create_bundle.SetToolchain(setup.toolchain());
372   ASSERT_TRUE(create_bundle.OnResolved(&err));
373 
374   std::ostringstream out;
375   NinjaCreateBundleTargetWriter writer(&create_bundle, out);
376   writer.Run();
377 
378   const char expected[] =
379       "build obj/baz/bar.inputdeps.stamp: stamp obj/biz/assets.stamp "
380       "obj/foo/assets.stamp obj/foo/bar.stamp obj/foo/data.stamp "
381       "obj/qux/info_plist.stamp obj/quz/assets.stamp\n"
382       "build bar.bundle/Contents/Info.plist: copy_bundle_data "
383       "../../qux/qux-Info.plist || obj/baz/bar.inputdeps.stamp\n"
384       "build bar.bundle/Contents/Resources/input1.txt: copy_bundle_data "
385       "../../foo/input1.txt || obj/baz/bar.inputdeps.stamp\n"
386       "build bar.bundle/Contents/Resources/input2.txt: copy_bundle_data "
387       "../../foo/input2.txt || obj/baz/bar.inputdeps.stamp\n"
388       "build obj/baz/bar.xcassets.inputdeps.stamp: stamp "
389       "obj/foo/assets.stamp "
390       "obj/quz/assets.stamp obj/biz/assets.stamp\n"
391       "build bar.bundle/Contents/Resources/Assets.car | "
392       "baz/bar/bar_partial_info.plist: compile_xcassets "
393       "../../foo/Foo.xcassets ../../quz/Quz.xcassets "
394       "../../biz/Biz.xcassets | obj/baz/bar.xcassets.inputdeps.stamp || "
395       "obj/baz/bar.inputdeps.stamp\n"
396       "  product_type = com.apple.product-type\n"
397       "  partial_info_plist = baz/bar/bar_partial_info.plist\n"
398       "build obj/baz/bar.stamp: stamp "
399       "bar.bundle/Contents/Info.plist "
400       "bar.bundle/Contents/Resources/input1.txt "
401       "bar.bundle/Contents/Resources/input2.txt "
402       "bar.bundle/Contents/Resources/Assets.car "
403       "baz/bar/bar_partial_info.plist || obj/baz/bar.inputdeps.stamp\n"
404       "build bar.bundle: phony obj/baz/bar.stamp\n";
405   std::string out_str = out.str();
406   EXPECT_EQ(expected, out_str);
407 }
408 
409 // Tests code signing steps.
TEST(NinjaCreateBundleTargetWriter,CodeSigning)410 TEST(NinjaCreateBundleTargetWriter, CodeSigning) {
411   Err err;
412   TestWithScope setup;
413 
414   std::unique_ptr<Target> action = NewAction(setup);
415   ASSERT_TRUE(action->OnResolved(&err)) << err.message();
416 
417   Target executable(setup.settings(), Label(SourceDir("//baz/"), "quz"));
418   executable.set_output_type(Target::EXECUTABLE);
419   executable.sources().push_back(SourceFile("//baz/quz.c"));
420   executable.SetToolchain(setup.toolchain());
421   executable.visibility().SetPublic();
422   ASSERT_TRUE(executable.OnResolved(&err));
423 
424   Target bundle_data(setup.settings(), Label(SourceDir("//foo/"), "data"));
425   bundle_data.set_output_type(Target::BUNDLE_DATA);
426   bundle_data.sources().push_back(SourceFile("//foo/input1.txt"));
427   bundle_data.sources().push_back(SourceFile("//foo/input2.txt"));
428   bundle_data.action_values().outputs() = SubstitutionList::MakeForTest(
429       "{{bundle_resources_dir}}/{{source_file_part}}");
430   bundle_data.SetToolchain(setup.toolchain());
431   bundle_data.visibility().SetPublic();
432   ASSERT_TRUE(bundle_data.OnResolved(&err));
433 
434   Target create_bundle(
435       setup.settings(),
436       Label(SourceDir("//baz/"), "bar", setup.toolchain()->label().dir(),
437             setup.toolchain()->label().name()));
438   SetupBundleDataDir(&create_bundle.bundle_data(), "//out/Debug");
439   create_bundle.set_output_type(Target::CREATE_BUNDLE);
440   create_bundle.bundle_data().set_code_signing_script(
441       SourceFile("//build/codesign.py"));
442   create_bundle.bundle_data().code_signing_sources().push_back(
443       SourceFile("//out/Debug/quz"));
444   create_bundle.bundle_data().code_signing_outputs() =
445       SubstitutionList::MakeForTest(
446           "//out/Debug/bar.bundle/Contents/quz",
447           "//out/Debug/bar.bundle/_CodeSignature/CodeResources");
448   create_bundle.bundle_data().code_signing_args() =
449       SubstitutionList::MakeForTest("-b=quz", "bar.bundle");
450   create_bundle.public_deps().push_back(LabelTargetPair(&executable));
451   create_bundle.private_deps().push_back(LabelTargetPair(&bundle_data));
452   create_bundle.private_deps().push_back(LabelTargetPair(action.get()));
453   create_bundle.SetToolchain(setup.toolchain());
454   ASSERT_TRUE(create_bundle.OnResolved(&err));
455 
456   std::ostringstream out;
457   NinjaCreateBundleTargetWriter writer(&create_bundle, out);
458   writer.Run();
459 
460   const char expected[] =
461       "build obj/baz/bar.inputdeps.stamp: stamp ./quz obj/foo/bar.stamp "
462       "obj/foo/data.stamp\n"
463       "rule __baz_bar___toolchain_default__code_signing_rule\n"
464       "  command =  ../../build/codesign.py -b=quz bar.bundle\n"
465       "  description = CODE SIGNING //baz:bar(//toolchain:default)\n"
466       "  restat = 1\n"
467       "\n"
468       "build bar.bundle/Contents/Resources/input1.txt: copy_bundle_data "
469       "../../foo/input1.txt || obj/baz/bar.inputdeps.stamp\n"
470       "build bar.bundle/Contents/Resources/input2.txt: copy_bundle_data "
471       "../../foo/input2.txt || obj/baz/bar.inputdeps.stamp\n"
472       "build obj/baz/bar.codesigning.inputdeps.stamp: stamp "
473       "../../build/codesign.py "
474       "quz "
475       "bar.bundle/Contents/Resources/input1.txt "
476       "bar.bundle/Contents/Resources/input2.txt || "
477       "obj/baz/bar.inputdeps.stamp\n"
478       "build bar.bundle/Contents/quz bar.bundle/_CodeSignature/CodeResources: "
479       "__baz_bar___toolchain_default__code_signing_rule "
480       "| obj/baz/bar.codesigning.inputdeps.stamp\n"
481       "build obj/baz/bar.stamp: stamp "
482       "bar.bundle/Contents/quz "
483       "bar.bundle/_CodeSignature/CodeResources || obj/baz/bar.inputdeps.stamp\n"
484       "build bar.bundle: phony obj/baz/bar.stamp\n";
485   std::string out_str = out.str();
486   EXPECT_EQ(expected, out_str);
487 }
488