• 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.SetToolchain(setup.toolchain());
218   ASSERT_TRUE(create_bundle.OnResolved(&err));
219 
220   std::ostringstream out;
221   NinjaCreateBundleTargetWriter writer(&create_bundle, out);
222   writer.Run();
223 
224   const char expected[] =
225       "build obj/baz/bar.inputdeps.stamp: stamp obj/foo/bar.stamp "
226       "obj/foo/data.stamp\n"
227       "build bar.bundle/Contents/Resources/Assets.car: compile_xcassets "
228       "../../foo/Foo.xcassets | obj/foo/data.stamp || "
229       "obj/baz/bar.inputdeps.stamp\n"
230       "  product_type = com.apple.product-type\n"
231       "build obj/baz/bar.stamp: stamp "
232       "bar.bundle/Contents/Resources/Assets.car || "
233       "obj/baz/bar.inputdeps.stamp\n"
234       "build bar.bundle: phony obj/baz/bar.stamp\n";
235   std::string out_str = out.str();
236   EXPECT_EQ(expected, out_str);
237 }
238 
239 // Tests that the phony target for the top-level bundle directory is generated
240 // correctly.
TEST(NinjaCreateBundleTargetWriter,PhonyTarget)241 TEST(NinjaCreateBundleTargetWriter, PhonyTarget) {
242   Err err;
243   TestWithScope setup;
244 
245   Target create_bundle(
246       setup.settings(),
247       Label(SourceDir("//baz/"), "bar", setup.toolchain()->label().dir(),
248             setup.toolchain()->label().name()));
249   SetupBundleDataDir(&create_bundle.bundle_data(), "//out/Debug");
250   create_bundle.set_output_type(Target::CREATE_BUNDLE);
251   create_bundle.SetToolchain(setup.toolchain());
252   ASSERT_TRUE(create_bundle.OnResolved(&err));
253 
254   std::ostringstream out;
255   NinjaCreateBundleTargetWriter writer(&create_bundle, out);
256   writer.Run();
257 
258   const char expected[] =
259       "build obj/baz/bar.stamp: stamp\n"
260       "build bar.bundle: phony obj/baz/bar.stamp\n";
261   std::string out_str = out.str();
262   EXPECT_EQ(expected, out_str);
263 }
264 
265 // Tests complex target with multiple bundle_data sources, including
266 // some asset catalog.
TEST(NinjaCreateBundleTargetWriter,Complex)267 TEST(NinjaCreateBundleTargetWriter, Complex) {
268   Err err;
269   TestWithScope setup;
270 
271   std::unique_ptr<Target> action = NewAction(setup);
272   ASSERT_TRUE(action->OnResolved(&err)) << err.message();
273 
274   Target bundle_data0(setup.settings(),
275                       Label(SourceDir("//qux/"), "info_plist"));
276   bundle_data0.set_output_type(Target::BUNDLE_DATA);
277   bundle_data0.sources().push_back(SourceFile("//qux/qux-Info.plist"));
278   bundle_data0.action_values().outputs() =
279       SubstitutionList::MakeForTest("{{bundle_contents_dir}}/Info.plist");
280   bundle_data0.SetToolchain(setup.toolchain());
281   bundle_data0.visibility().SetPublic();
282   ASSERT_TRUE(bundle_data0.OnResolved(&err));
283 
284   Target bundle_data1(setup.settings(), Label(SourceDir("//foo/"), "data"));
285   bundle_data1.set_output_type(Target::BUNDLE_DATA);
286   bundle_data1.sources().push_back(SourceFile("//foo/input1.txt"));
287   bundle_data1.sources().push_back(SourceFile("//foo/input2.txt"));
288   bundle_data1.action_values().outputs() = SubstitutionList::MakeForTest(
289       "{{bundle_resources_dir}}/{{source_file_part}}");
290   bundle_data1.SetToolchain(setup.toolchain());
291   bundle_data1.visibility().SetPublic();
292   ASSERT_TRUE(bundle_data1.OnResolved(&err));
293 
294   Target bundle_data2(setup.settings(), Label(SourceDir("//foo/"), "assets"));
295   bundle_data2.set_output_type(Target::BUNDLE_DATA);
296   bundle_data2.sources().push_back(
297       SourceFile("//foo/Foo.xcassets/Contents.json"));
298   bundle_data2.sources().push_back(
299       SourceFile("//foo/Foo.xcassets/foo.colorset/Contents.json"));
300   bundle_data2.sources().push_back(
301       SourceFile("//foo/Foo.xcassets/foo.imageset/Contents.json"));
302   bundle_data2.sources().push_back(
303       SourceFile("//foo/Foo.xcassets/foo.imageset/FooIcon-29.png"));
304   bundle_data2.sources().push_back(
305       SourceFile("//foo/Foo.xcassets/foo.imageset/FooIcon-29@2x.png"));
306   bundle_data2.sources().push_back(
307       SourceFile("//foo/Foo.xcassets/foo.imageset/FooIcon-29@3x.png"));
308   bundle_data2.sources().push_back(
309       SourceFile("//foo/Foo.xcassets/foo.dataset/Contents.json"));
310   bundle_data2.sources().push_back(
311       SourceFile("//foo/Foo.xcassets/foo.dataset/FooScript.js"));
312   bundle_data2.action_values().outputs() = SubstitutionList::MakeForTest(
313       "{{bundle_resources_dir}}/{{source_file_part}}");
314   bundle_data2.SetToolchain(setup.toolchain());
315   bundle_data2.visibility().SetPublic();
316   ASSERT_TRUE(bundle_data2.OnResolved(&err));
317 
318   Target bundle_data3(setup.settings(), Label(SourceDir("//quz/"), "assets"));
319   bundle_data3.set_output_type(Target::BUNDLE_DATA);
320   bundle_data3.sources().push_back(
321       SourceFile("//quz/Quz.xcassets/Contents.json"));
322   bundle_data3.sources().push_back(
323       SourceFile("//quz/Quz.xcassets/quz.imageset/Contents.json"));
324   bundle_data3.sources().push_back(
325       SourceFile("//quz/Quz.xcassets/quz.imageset/QuzIcon-29.png"));
326   bundle_data3.sources().push_back(
327       SourceFile("//quz/Quz.xcassets/quz.imageset/QuzIcon-29@2x.png"));
328   bundle_data3.sources().push_back(
329       SourceFile("//quz/Quz.xcassets/quz.imageset/QuzIcon-29@3x.png"));
330   bundle_data3.sources().push_back(
331       SourceFile("//quz/Quz.xcassets/quz.dataset/Contents.json"));
332   bundle_data3.sources().push_back(
333       SourceFile("//quz/Quz.xcassets/quz.dataset/QuzScript.js"));
334   bundle_data3.action_values().outputs() = SubstitutionList::MakeForTest(
335       "{{bundle_resources_dir}}/{{source_file_part}}");
336   bundle_data3.SetToolchain(setup.toolchain());
337   bundle_data3.visibility().SetPublic();
338   ASSERT_TRUE(bundle_data3.OnResolved(&err));
339 
340   Target bundle_data4(setup.settings(), Label(SourceDir("//biz/"), "assets"));
341   bundle_data4.set_output_type(Target::BUNDLE_DATA);
342   bundle_data4.sources().push_back(
343       SourceFile("//biz/Biz.xcassets/Contents.json"));
344   bundle_data4.sources().push_back(
345       SourceFile("//biz/Biz.xcassets/biz.colorset/Contents.json"));
346   bundle_data4.action_values().outputs() = SubstitutionList::MakeForTest(
347       "{{bundle_resources_dir}}/{{source_file_part}}");
348   bundle_data4.SetToolchain(setup.toolchain());
349   bundle_data4.visibility().SetPublic();
350   ASSERT_TRUE(bundle_data4.OnResolved(&err));
351 
352   Target create_bundle(
353       setup.settings(),
354       Label(SourceDir("//baz/"), "bar", setup.toolchain()->label().dir(),
355             setup.toolchain()->label().name()));
356   SetupBundleDataDir(&create_bundle.bundle_data(), "//out/Debug");
357   create_bundle.set_output_type(Target::CREATE_BUNDLE);
358   create_bundle.private_deps().push_back(LabelTargetPair(&bundle_data0));
359   create_bundle.private_deps().push_back(LabelTargetPair(&bundle_data1));
360   create_bundle.private_deps().push_back(LabelTargetPair(&bundle_data2));
361   create_bundle.private_deps().push_back(LabelTargetPair(&bundle_data3));
362   create_bundle.private_deps().push_back(LabelTargetPair(&bundle_data4));
363   create_bundle.private_deps().push_back(LabelTargetPair(action.get()));
364   create_bundle.bundle_data().product_type().assign("com.apple.product-type");
365   create_bundle.bundle_data().set_partial_info_plist(
366       SourceFile("//out/Debug/baz/bar/bar_partial_info.plist"));
367   create_bundle.SetToolchain(setup.toolchain());
368   ASSERT_TRUE(create_bundle.OnResolved(&err));
369 
370   std::ostringstream out;
371   NinjaCreateBundleTargetWriter writer(&create_bundle, out);
372   writer.Run();
373 
374   const char expected[] =
375       "build obj/baz/bar.inputdeps.stamp: stamp obj/biz/assets.stamp "
376       "obj/foo/assets.stamp obj/foo/bar.stamp obj/foo/data.stamp "
377       "obj/qux/info_plist.stamp obj/quz/assets.stamp\n"
378       "build bar.bundle/Contents/Info.plist: copy_bundle_data "
379       "../../qux/qux-Info.plist || obj/baz/bar.inputdeps.stamp\n"
380       "build bar.bundle/Contents/Resources/input1.txt: copy_bundle_data "
381       "../../foo/input1.txt || obj/baz/bar.inputdeps.stamp\n"
382       "build bar.bundle/Contents/Resources/input2.txt: copy_bundle_data "
383       "../../foo/input2.txt || obj/baz/bar.inputdeps.stamp\n"
384       "build obj/baz/bar.xcassets.inputdeps.stamp: stamp "
385       "obj/foo/assets.stamp "
386       "obj/quz/assets.stamp obj/biz/assets.stamp\n"
387       "build bar.bundle/Contents/Resources/Assets.car | "
388       "baz/bar/bar_partial_info.plist: compile_xcassets "
389       "../../foo/Foo.xcassets ../../quz/Quz.xcassets "
390       "../../biz/Biz.xcassets | obj/baz/bar.xcassets.inputdeps.stamp || "
391       "obj/baz/bar.inputdeps.stamp\n"
392       "  product_type = com.apple.product-type\n"
393       "  partial_info_plist = baz/bar/bar_partial_info.plist\n"
394       "build obj/baz/bar.stamp: stamp "
395       "bar.bundle/Contents/Info.plist "
396       "bar.bundle/Contents/Resources/input1.txt "
397       "bar.bundle/Contents/Resources/input2.txt "
398       "bar.bundle/Contents/Resources/Assets.car "
399       "baz/bar/bar_partial_info.plist || obj/baz/bar.inputdeps.stamp\n"
400       "build bar.bundle: phony obj/baz/bar.stamp\n";
401   std::string out_str = out.str();
402   EXPECT_EQ(expected, out_str);
403 }
404 
405 // Tests code signing steps.
TEST(NinjaCreateBundleTargetWriter,CodeSigning)406 TEST(NinjaCreateBundleTargetWriter, CodeSigning) {
407   Err err;
408   TestWithScope setup;
409 
410   std::unique_ptr<Target> action = NewAction(setup);
411   ASSERT_TRUE(action->OnResolved(&err)) << err.message();
412 
413   Target executable(setup.settings(), Label(SourceDir("//baz/"), "quz"));
414   executable.set_output_type(Target::EXECUTABLE);
415   executable.sources().push_back(SourceFile("//baz/quz.c"));
416   executable.SetToolchain(setup.toolchain());
417   executable.visibility().SetPublic();
418   ASSERT_TRUE(executable.OnResolved(&err));
419 
420   Target bundle_data(setup.settings(), Label(SourceDir("//foo/"), "data"));
421   bundle_data.set_output_type(Target::BUNDLE_DATA);
422   bundle_data.sources().push_back(SourceFile("//foo/input1.txt"));
423   bundle_data.sources().push_back(SourceFile("//foo/input2.txt"));
424   bundle_data.action_values().outputs() = SubstitutionList::MakeForTest(
425       "{{bundle_resources_dir}}/{{source_file_part}}");
426   bundle_data.SetToolchain(setup.toolchain());
427   bundle_data.visibility().SetPublic();
428   ASSERT_TRUE(bundle_data.OnResolved(&err));
429 
430   Target create_bundle(
431       setup.settings(),
432       Label(SourceDir("//baz/"), "bar", setup.toolchain()->label().dir(),
433             setup.toolchain()->label().name()));
434   SetupBundleDataDir(&create_bundle.bundle_data(), "//out/Debug");
435   create_bundle.set_output_type(Target::CREATE_BUNDLE);
436   create_bundle.bundle_data().set_code_signing_script(
437       SourceFile("//build/codesign.py"));
438   create_bundle.bundle_data().code_signing_sources().push_back(
439       SourceFile("//out/Debug/quz"));
440   create_bundle.bundle_data().code_signing_outputs() =
441       SubstitutionList::MakeForTest(
442           "//out/Debug/bar.bundle/Contents/quz",
443           "//out/Debug/bar.bundle/_CodeSignature/CodeResources");
444   create_bundle.bundle_data().code_signing_args() =
445       SubstitutionList::MakeForTest("-b=quz", "bar.bundle");
446   create_bundle.public_deps().push_back(LabelTargetPair(&executable));
447   create_bundle.private_deps().push_back(LabelTargetPair(&bundle_data));
448   create_bundle.private_deps().push_back(LabelTargetPair(action.get()));
449   create_bundle.SetToolchain(setup.toolchain());
450   ASSERT_TRUE(create_bundle.OnResolved(&err));
451 
452   std::ostringstream out;
453   NinjaCreateBundleTargetWriter writer(&create_bundle, out);
454   writer.Run();
455 
456   const char expected[] =
457       "build obj/baz/bar.inputdeps.stamp: stamp ./quz obj/foo/bar.stamp "
458       "obj/foo/data.stamp\n"
459       "rule __baz_bar___toolchain_default__code_signing_rule\n"
460       "  command =  ../../build/codesign.py -b=quz bar.bundle\n"
461       "  description = CODE SIGNING //baz:bar(//toolchain:default)\n"
462       "  restat = 1\n"
463       "\n"
464       "build bar.bundle/Contents/Resources/input1.txt: copy_bundle_data "
465       "../../foo/input1.txt || obj/baz/bar.inputdeps.stamp\n"
466       "build bar.bundle/Contents/Resources/input2.txt: copy_bundle_data "
467       "../../foo/input2.txt || obj/baz/bar.inputdeps.stamp\n"
468       "build obj/baz/bar.codesigning.inputdeps.stamp: stamp "
469       "../../build/codesign.py "
470       "quz "
471       "bar.bundle/Contents/Resources/input1.txt "
472       "bar.bundle/Contents/Resources/input2.txt || "
473       "obj/baz/bar.inputdeps.stamp\n"
474       "build bar.bundle/Contents/quz bar.bundle/_CodeSignature/CodeResources: "
475       "__baz_bar___toolchain_default__code_signing_rule "
476       "| obj/baz/bar.codesigning.inputdeps.stamp\n"
477       "build obj/baz/bar.stamp: stamp "
478       "bar.bundle/Contents/quz "
479       "bar.bundle/_CodeSignature/CodeResources || obj/baz/bar.inputdeps.stamp\n"
480       "build bar.bundle: phony obj/baz/bar.stamp\n";
481   std::string out_str = out.str();
482   EXPECT_EQ(expected, out_str);
483 }
484