xref: /aosp_15_r20/external/bazelbuild-rules_cc/tools/migration/ctoolchain_comparator_lib_test.py (revision eed53cd41c5909d05eedc7ad9720bb158fd93452)
1# Copyright 2018 The Bazel Authors. All rights reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#    http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15import unittest
16
17from py import mock
18
19from google.protobuf import text_format
20from third_party.com.github.bazelbuild.bazel.src.main.protobuf import crosstool_config_pb2
21from tools.migration.ctoolchain_comparator_lib import compare_ctoolchains
22
23try:
24  # Python 2
25  from cStringIO import StringIO
26except ImportError:
27  # Python 3
28  from io import StringIO
29
30
31def make_toolchain(toolchain_proto):
32  toolchain = crosstool_config_pb2.CToolchain()
33  text_format.Merge(toolchain_proto, toolchain)
34  return toolchain
35
36
37class CtoolchainComparatorLibTest(unittest.TestCase):
38
39  def test_string_fields(self):
40    first = make_toolchain("""
41          toolchain_identifier: "first-id"
42          host_system_name: "first-host"
43          target_system_name: "first-target"
44          target_cpu: "first-cpu"
45          target_libc: "first-libc"
46          compiler: "first-compiler"
47          abi_version: "first-abi"
48          abi_libc_version: "first-abi-libc"
49          builtin_sysroot: "sysroot"
50        """)
51    second = make_toolchain("""
52          toolchain_identifier: "second-id"
53          host_system_name: "second-host"
54          target_system_name: "second-target"
55          target_cpu: "second-cpu"
56          target_libc: "second-libc"
57          compiler: "second-compiler"
58          abi_version: "second-abi"
59          abi_libc_version: "second-abi-libc"
60          cc_target_os: "os"
61        """)
62    error_toolchain_identifier = (
63        "Difference in 'toolchain_identifier' field:\n"
64        "Value before change:\t'first-id'\n"
65        "Value after change:\t'second-id'\n"
66    )
67    error_host_system_name = (
68        "Difference in 'host_system_name' field:\n"
69        "Value before change:\t'first-host'\n"
70        "Value after change:\t'second-host'\n"
71    )
72    error_target_system_name = (
73        "Difference in 'target_system_name' field:\n"
74        "Value before change:\t'first-target'\n"
75        "Value after change:\t'second-target'\n"
76    )
77    error_target_cpu = (
78        "Difference in 'target_cpu' field:\n"
79        "Value before change:\t'first-cpu'\n"
80        "Value after change:\t'second-cpu'\n"
81    )
82    error_target_libc = (
83        "Difference in 'target_libc' field:\n"
84        "Value before change:\t'first-libc'\n"
85        "Value after change:\t'second-libc'\n"
86    )
87    error_compiler = (
88        "Difference in 'compiler' field:\n"
89        "Value before change:\t'first-compiler'\n"
90        "Value after change:\t'second-compiler'\n"
91    )
92    error_abi_version = (
93        "Difference in 'abi_version' field:\n"
94        "Value before change:\t'first-abi'\n"
95        "Value after change:\t'second-abi'\n"
96    )
97    error_abi_libc_version = (
98        "Difference in 'abi_libc_version' field:\n"
99        "Value before change:\t'first-abi-libc'\n"
100        "Value after change:\t'second-abi-libc'\n"
101    )
102    error_builtin_sysroot = (
103        "Difference in 'builtin_sysroot' field:\n"
104        "Value before change is set to 'sysroot'\n"
105        "Value after change is not set\n"
106    )
107    error_cc_target_os = (
108        "Difference in 'cc_target_os' field:\n"
109        "Value before change is not set\n"
110        "Value after change is set to 'os'\n"
111    )
112    mock_stdout = StringIO()
113    with mock.patch("sys.stdout", mock_stdout):
114      compare_ctoolchains(first, second)
115      self.assertIn(error_toolchain_identifier, mock_stdout.getvalue())
116      self.assertIn(error_host_system_name, mock_stdout.getvalue())
117      self.assertIn(error_target_system_name, mock_stdout.getvalue())
118      self.assertIn(error_target_cpu, mock_stdout.getvalue())
119      self.assertIn(error_target_libc, mock_stdout.getvalue())
120      self.assertIn(error_compiler, mock_stdout.getvalue())
121      self.assertIn(error_abi_version, mock_stdout.getvalue())
122      self.assertIn(error_abi_libc_version, mock_stdout.getvalue())
123      self.assertIn(error_builtin_sysroot, mock_stdout.getvalue())
124      self.assertIn(error_cc_target_os, mock_stdout.getvalue())
125
126  def test_tool_path(self):
127    first = make_toolchain("""
128        tool_path {
129          name: "only_first"
130          path: "/a/b/c"
131        }
132        tool_path {
133          name: "paths_differ"
134          path: "/path/first"
135        }
136    """)
137    second = make_toolchain("""
138        tool_path {
139          name: "paths_differ"
140          path: "/path/second"
141        }
142        tool_path {
143          name: "only_second_1"
144          path: "/a/b/c"
145        }
146        tool_path {
147          name: "only_second_2"
148          path: "/a/b/c"
149        }
150    """)
151    error_only_first = (
152        "* List before change contains entries for the "
153        "following tools that the list after the change "
154        "doesn't:\n[only_first]\n"
155    )
156    error_only_second = (
157        "* List after change contains entries for the "
158        "following tools that the list before the change "
159        "doesn't:\n"
160        "[\n"
161        "\tonly_second_1\n"
162        "\tonly_second_2\n"
163        "]\n"
164    )
165    error_paths_differ = (
166        "* Path for tool 'paths_differ' differs before and "
167        "after the change:\n"
168        "Value before change:\t'/path/first'\n"
169        "Value after change:\t'/path/second'\n"
170    )
171    mock_stdout = StringIO()
172    with mock.patch("sys.stdout", mock_stdout):
173      compare_ctoolchains(first, second)
174      self.assertIn(error_only_first, mock_stdout.getvalue())
175      self.assertIn(error_only_second, mock_stdout.getvalue())
176      self.assertIn(error_paths_differ, mock_stdout.getvalue())
177
178  def test_make_variable(self):
179    first = make_toolchain("""
180        make_variable {
181          name: "only_first"
182          value: "val"
183        }
184        make_variable {
185          name: "value_differs"
186          value: "first_value"
187        }
188    """)
189    second = make_toolchain("""
190        make_variable {
191          name: "value_differs"
192          value: "second_value"
193        }
194        make_variable {
195          name: "only_second_1"
196          value: "val"
197        }
198        make_variable {
199          name: "only_second_2"
200          value: "val"
201        }
202    """)
203    error_only_first = (
204        "* List before change contains entries for the "
205        "following variables that the list after the "
206        "change doesn't:\n[only_first]\n"
207    )
208    error_only_second = (
209        "* List after change contains entries for the "
210        "following variables that the list before the "
211        "change doesn't:\n"
212        "[\n"
213        "\tonly_second_1\n"
214        "\tonly_second_2\n"
215        "]\n"
216    )
217    error_value_differs = (
218        "* Value for variable 'value_differs' differs before"
219        " and after the change:\n"
220        "Value before change:\t'first_value'\n"
221        "Value after change:\t'second_value'\n"
222    )
223    mock_stdout = StringIO()
224    with mock.patch("sys.stdout", mock_stdout):
225      compare_ctoolchains(first, second)
226      self.assertIn(error_only_first, mock_stdout.getvalue())
227      self.assertIn(error_only_second, mock_stdout.getvalue())
228      self.assertIn(error_value_differs, mock_stdout.getvalue())
229
230  def test_cxx_builtin_include_directories(self):
231    first = make_toolchain("""
232        cxx_builtin_include_directory: "a/b/c"
233        cxx_builtin_include_directory: "d/e/f"
234    """)
235    second = make_toolchain("""
236        cxx_builtin_include_directory: "d/e/f"
237        cxx_builtin_include_directory: "a/b/c"
238    """)
239    expect_error = (
240        "Difference in 'cxx_builtin_include_directory' field:\n"
241        "List of elements before change:\n"
242        "[\n"
243        "\ta/b/c\n"
244        "\td/e/f\n"
245        "]\n"
246        "List of elements after change:\n"
247        "[\n"
248        "\td/e/f\n"
249        "\ta/b/c\n"
250        "]\n"
251    )
252    mock_stdout = StringIO()
253    with mock.patch("sys.stdout", mock_stdout):
254      compare_ctoolchains(first, second)
255      self.assertIn(expect_error, mock_stdout.getvalue())
256
257  def test_artifact_name_pattern(self):
258    first = make_toolchain("""
259        artifact_name_pattern {
260          category_name: 'object_file'
261          prefix: ''
262          extension: '.obj1'
263        }
264        artifact_name_pattern {
265          category_name: 'executable'
266          prefix: 'first'
267          extension: '.exe'
268        }
269        artifact_name_pattern {
270          category_name: 'dynamic_library'
271          prefix: ''
272          extension: '.dll'
273        }
274    """)
275    second = make_toolchain("""
276        artifact_name_pattern {
277          category_name: 'object_file'
278          prefix: ''
279          extension: '.obj2'
280        }
281        artifact_name_pattern {
282          category_name: 'static_library'
283          prefix: ''
284          extension: '.lib'
285        }
286        artifact_name_pattern {
287          category_name: 'executable'
288          prefix: 'second'
289          extension: '.exe'
290        }
291        artifact_name_pattern {
292          category_name: 'interface_library'
293          prefix: ''
294          extension: '.if.lib'
295        }
296    """)
297    error_only_first = (
298        "* List before change contains entries for the "
299        "following categories that the list after the "
300        "change doesn't:\n[dynamic_library]\n"
301    )
302    error_only_second = (
303        "* List after change contains entries for the "
304        "following categories that the list before the "
305        "change doesn't:\n"
306        "[\n"
307        "\tinterface_library\n"
308        "\tstatic_library\n"
309        "]\n"
310    )
311    error_extension_differs = (
312        "* Value for category 'object_file' differs "
313        "before and after the change:\n"
314        "Value before change:"
315        "\tprefix:''"
316        "\textension:'.obj1'\n"
317        "Value after change:"
318        "\tprefix:''"
319        "\textension:'.obj2'\n"
320    )
321    error_prefix_differs = (
322        "* Value for category 'executable' differs "
323        "before and after the change:\n"
324        "Value before change:"
325        "\tprefix:'first'"
326        "\textension:'.exe'\n"
327        "Value after change:"
328        "\tprefix:'second'"
329        "\textension:'.exe'\n"
330    )
331    mock_stdout = StringIO()
332    with mock.patch("sys.stdout", mock_stdout):
333      compare_ctoolchains(first, second)
334      self.assertIn(error_only_first, mock_stdout.getvalue())
335      self.assertIn(error_only_second, mock_stdout.getvalue())
336      self.assertIn(error_extension_differs, mock_stdout.getvalue())
337      self.assertIn(error_prefix_differs, mock_stdout.getvalue())
338
339  def test_features_not_ordered(self):
340    first = make_toolchain("""
341        feature {
342          name: 'feature1'
343        }
344        feature {
345          name: 'feature2'
346        }
347    """)
348    second = make_toolchain("""
349        feature {
350          name: 'feature2'
351        }
352        feature {
353          name: 'feature1'
354        }
355    """)
356    mock_stdout = StringIO()
357    with mock.patch("sys.stdout", mock_stdout):
358      compare_ctoolchains(first, second)
359      self.assertIn("Features not in right order", mock_stdout.getvalue())
360
361  def test_features_missing(self):
362    first = make_toolchain("""
363        feature {
364          name: 'feature1'
365        }
366    """)
367    second = make_toolchain("""
368        feature {
369          name: 'feature2'
370        }
371    """)
372    error_only_first = (
373        "* List before change contains entries for the "
374        "following features that the list after the "
375        "change doesn't:\n[feature1]\n"
376    )
377    error_only_second = (
378        "* List after change contains entries for the "
379        "following features that the list before the "
380        "change doesn't:\n[feature2]\n"
381    )
382    mock_stdout = StringIO()
383    with mock.patch("sys.stdout", mock_stdout):
384      compare_ctoolchains(first, second)
385      self.assertIn(error_only_first, mock_stdout.getvalue())
386      self.assertIn(error_only_second, mock_stdout.getvalue())
387
388  def test_feature_enabled(self):
389    first = make_toolchain("""
390        feature {
391          name: 'feature'
392          enabled: true
393        }
394    """)
395    second = make_toolchain("""
396        feature {
397          name: 'feature'
398          enabled: false
399        }
400    """)
401    mock_stdout = StringIO()
402    with mock.patch("sys.stdout", mock_stdout):
403      compare_ctoolchains(first, second)
404      self.assertIn(
405          "* Feature 'feature' differs before and after", mock_stdout.getvalue()
406      )
407
408  def test_feature_provides(self):
409    first = make_toolchain("""
410        feature {
411          name: 'feature'
412          provides: 'a'
413        }
414    """)
415    second = make_toolchain("""
416        feature {
417          name: 'feature'
418          provides: 'b'
419        }
420    """)
421    mock_stdout = StringIO()
422    with mock.patch("sys.stdout", mock_stdout):
423      compare_ctoolchains(first, second)
424      self.assertIn(
425          "* Feature 'feature' differs before and after the change:",
426          mock_stdout.getvalue(),
427      )
428
429  def test_feature_provides_preserves_order(self):
430    first = make_toolchain("""
431        feature {
432          name: 'feature'
433          provides: 'a'
434          provides: 'b'
435        }
436    """)
437    second = make_toolchain("""
438        feature {
439          name: 'feature'
440          provides: 'b'
441          provides: 'a'
442        }
443    """)
444    mock_stdout = StringIO()
445    with mock.patch("sys.stdout", mock_stdout):
446      compare_ctoolchains(first, second)
447      self.assertIn(
448          "* Feature 'feature' differs before and after the change:",
449          mock_stdout.getvalue(),
450      )
451
452  def test_feature_implies(self):
453    first = make_toolchain("""
454        feature {
455          name: 'feature'
456          implies: 'a'
457        }
458    """)
459    second = make_toolchain("""
460        feature {
461          name: 'feature'
462        }
463    """)
464    mock_stdout = StringIO()
465    with mock.patch("sys.stdout", mock_stdout):
466      compare_ctoolchains(first, second)
467      self.assertIn(
468          "* Feature 'feature' differs before and after the change:",
469          mock_stdout.getvalue(),
470      )
471
472  def test_feature_implies_preserves_order(self):
473    first = make_toolchain("""
474        feature {
475          name: 'feature'
476          implies: 'a'
477          implies: 'b'
478        }
479    """)
480    second = make_toolchain("""
481        feature {
482          name: 'feature'
483          implies: 'b'
484          implies: 'a'
485        }
486    """)
487    mock_stdout = StringIO()
488    with mock.patch("sys.stdout", mock_stdout):
489      compare_ctoolchains(first, second)
490      self.assertIn(
491          "* Feature 'feature' differs before and after the change:",
492          mock_stdout.getvalue(),
493      )
494
495  def test_feature_requires_preserves_list_order(self):
496    first = make_toolchain("""
497        feature {
498          name: 'feature'
499          requires: {
500            feature: 'feature1'
501          }
502          requires: {
503            feature: 'feature2'
504          }
505        }
506    """)
507    second = make_toolchain("""
508        feature {
509          name: 'feature'
510          requires: {
511            feature: 'feature2'
512          }
513          requires: {
514            feature: 'feature1'
515          }
516        }
517    """)
518    mock_stdout = StringIO()
519    with mock.patch("sys.stdout", mock_stdout):
520      compare_ctoolchains(first, second)
521      self.assertIn(
522          "* Feature 'feature' differs before and after the change:",
523          mock_stdout.getvalue(),
524      )
525
526  def test_feature_requires_ignores_required_features_order(self):
527    first = make_toolchain("""
528        feature {
529          name: 'feature'
530          requires: {
531            feature: 'feature1'
532            feature: 'feature2'
533          }
534        }
535    """)
536    second = make_toolchain("""
537        feature {
538          name: 'feature'
539          requires: {
540            feature: 'feature2'
541            feature: 'feature1'
542          }
543        }
544    """)
545    mock_stdout = StringIO()
546    with mock.patch("sys.stdout", mock_stdout):
547      compare_ctoolchains(first, second)
548      self.assertIn("No difference", mock_stdout.getvalue())
549
550  def test_feature_requires_differs(self):
551    first = make_toolchain("""
552        feature {
553          name: 'feature'
554          requires: {
555            feature: 'feature1'
556          }
557        }
558    """)
559    second = make_toolchain("""
560        feature {
561          name: 'feature'
562          requires: {
563            feature: 'feature2'
564          }
565        }
566    """)
567    mock_stdout = StringIO()
568    with mock.patch("sys.stdout", mock_stdout):
569      compare_ctoolchains(first, second)
570      self.assertIn(
571          "* Feature 'feature' differs before and after the change:",
572          mock_stdout.getvalue(),
573      )
574
575  def test_action_config_ignores_requires(self):
576    first = make_toolchain("""
577        action_config {
578          config_name: 'config'
579          requires: {
580            feature: 'feature1'
581          }
582        }
583    """)
584    second = make_toolchain("""
585        action_config {
586          config_name: 'config'
587          requires: {
588            feature: 'feature2'
589          }
590        }
591    """)
592    mock_stdout = StringIO()
593    with mock.patch("sys.stdout", mock_stdout):
594      compare_ctoolchains(first, second)
595      self.assertIn("No difference", mock_stdout.getvalue())
596
597  def test_env_set_actions_differ(self):
598    first = make_toolchain("""
599        feature {
600          name: 'feature'
601          env_set {
602            action: 'a1'
603          }
604        }
605    """)
606    second = make_toolchain("""
607        feature {
608          name: 'feature'
609          env_set: {
610            action: 'a1'
611            action: 'a2'
612          }
613        }
614    """)
615    mock_stdout = StringIO()
616    with mock.patch("sys.stdout", mock_stdout):
617      compare_ctoolchains(first, second)
618      self.assertIn(
619          "* Feature 'feature' differs before and after the change:",
620          mock_stdout.getvalue(),
621      )
622
623  def test_env_set_ignores_actions_order(self):
624    first = make_toolchain("""
625        feature {
626          name: 'feature'
627          env_set {
628            action: 'a2'
629            action: 'a1'
630          }
631        }
632    """)
633    second = make_toolchain("""
634        feature {
635          name: 'feature'
636          env_set: {
637            action: 'a1'
638            action: 'a2'
639          }
640        }
641    """)
642    mock_stdout = StringIO()
643    with mock.patch("sys.stdout", mock_stdout):
644      compare_ctoolchains(first, second)
645      self.assertIn("No difference", mock_stdout.getvalue())
646
647  def test_env_set_env_entries_not_ordered(self):
648    first = make_toolchain("""
649        feature {
650          name: 'feature'
651          env_set {
652            env_entry {
653              key: 'k1'
654              value: 'v1'
655            }
656            env_entry {
657              key: 'k2'
658              value: 'v2'
659            }
660          }
661        }
662    """)
663    second = make_toolchain("""
664        feature {
665          name: 'feature'
666          env_set {
667            env_entry {
668              key: 'k2'
669              value: 'v2'
670            }
671            env_entry {
672              key: 'k1'
673              value: 'v1'
674            }
675          }
676        }
677    """)
678    mock_stdout = StringIO()
679    with mock.patch("sys.stdout", mock_stdout):
680      compare_ctoolchains(first, second)
681      self.assertIn(
682          "* Feature 'feature' differs before and after the change:",
683          mock_stdout.getvalue(),
684      )
685
686  def test_env_set_env_entries_differ(self):
687    first = make_toolchain("""
688        feature {
689          name: 'feature'
690          env_set {
691            env_entry {
692              key: 'k1'
693              value: 'value_first'
694            }
695          }
696        }
697    """)
698    second = make_toolchain("""
699        feature {
700          name: 'feature'
701          env_set {
702            env_entry {
703              key: 'k1'
704              value: 'value_second'
705            }
706          }
707        }
708    """)
709    mock_stdout = StringIO()
710    with mock.patch("sys.stdout", mock_stdout):
711      compare_ctoolchains(first, second)
712      self.assertIn(
713          "* Feature 'feature' differs before and after the change:",
714          mock_stdout.getvalue(),
715      )
716
717  def test_feature_preserves_env_set_order(self):
718    first = make_toolchain("""
719        feature {
720          name: 'feature'
721          env_set {
722            env_entry {
723              key: 'first'
724              value: 'first'
725            }
726          }
727          env_set {
728            env_entry {
729              key: 'second'
730              value: 'second'
731            }
732          }
733        }
734    """)
735    second = make_toolchain("""
736        feature {
737          name: 'feature'
738          env_set {
739            env_entry {
740              key: 'second'
741              value: 'second'
742            }
743          }
744          env_set {
745            env_entry {
746              key: 'first'
747              value: 'first'
748            }
749          }
750        }
751    """)
752    mock_stdout = StringIO()
753    with mock.patch("sys.stdout", mock_stdout):
754      compare_ctoolchains(first, second)
755      self.assertIn(
756          "* Feature 'feature' differs before and after the change:",
757          mock_stdout.getvalue(),
758      )
759
760  def test_action_config_ignores_env_set(self):
761    first = make_toolchain("""
762        action_config {
763          config_name: 'config'
764          env_set {
765            env_entry {
766              key: 'k1'
767              value: 'value_first'
768            }
769          }
770        }
771    """)
772    second = make_toolchain("""
773        action_config {
774          config_name: 'config'
775          env_set {
776            env_entry {
777              key: 'k1'
778              value: 'value_second'
779            }
780          }
781        }
782    """)
783    mock_stdout = StringIO()
784    with mock.patch("sys.stdout", mock_stdout):
785      compare_ctoolchains(first, second)
786      self.assertIn("No difference", mock_stdout.getvalue())
787
788  def test_env_set_ignores_with_feature_set_order(self):
789    first = make_toolchain("""
790        feature {
791          name: 'feature'
792          env_set{
793            with_feature {
794              feature: 'feature1'
795            }
796            with_feature {
797              not_feature: 'feature2'
798            }
799          }
800        }
801    """)
802    second = make_toolchain("""
803        feature {
804          name: 'feature'
805          env_set {
806            with_feature {
807              not_feature: 'feature2'
808            }
809            with_feature {
810              feature: 'feature1'
811            }
812          }
813        }
814    """)
815    mock_stdout = StringIO()
816    with mock.patch("sys.stdout", mock_stdout):
817      compare_ctoolchains(first, second)
818      self.assertIn("No difference", mock_stdout.getvalue())
819
820  def test_env_set_ignores_with_feature_set_lists_order(self):
821    first = make_toolchain("""
822        feature {
823          name: 'feature'
824          env_set{
825            with_feature {
826              feature: 'feature1'
827              feature: 'feature2'
828              not_feature: 'not_feature1'
829              not_feature: 'not_feature2'
830            }
831          }
832        }
833    """)
834    second = make_toolchain("""
835        feature {
836          name: 'feature'
837          env_set{
838            with_feature {
839              feature: 'feature2'
840              feature: 'feature1'
841              not_feature: 'not_feature2'
842              not_feature: 'not_feature1'
843            }
844          }
845        }
846    """)
847    mock_stdout = StringIO()
848    with mock.patch("sys.stdout", mock_stdout):
849      compare_ctoolchains(first, second)
850      self.assertIn("No difference", mock_stdout.getvalue())
851
852  def test_flag_set_ignores_actions_order(self):
853    first = make_toolchain("""
854        feature {
855          name: 'feature'
856          flag_set {
857             action: 'a1'
858             action: 'a2'
859          }
860        }
861    """)
862    second = make_toolchain("""
863       feature {
864          name: 'feature'
865          flag_set {
866             action: 'a2'
867             action: 'a1'
868          }
869        }
870    """)
871    mock_stdout = StringIO()
872    with mock.patch("sys.stdout", mock_stdout):
873      compare_ctoolchains(first, second)
874      self.assertIn("No difference", mock_stdout.getvalue())
875
876  def test_action_config_flag_set_actions_ignored(self):
877    first = make_toolchain("""
878      action_config {
879          config_name: 'config'
880          flag_set {
881            action: 'a1'
882          }
883        }
884    """)
885    second = make_toolchain("""
886      action_config {
887          config_name: 'config'
888          flag_set {
889            action: 'a2'
890          }
891        }
892    """)
893    mock_stdout = StringIO()
894    with mock.patch("sys.stdout", mock_stdout):
895      compare_ctoolchains(first, second)
896      self.assertIn("No difference", mock_stdout.getvalue())
897
898  def test_flag_set_ignores_with_feature_set_order(self):
899    first = make_toolchain("""
900        feature {
901          name: 'feature'
902          flag_set {
903            with_feature {
904              feature: 'feature1'
905            }
906            with_feature {
907              not_feature: 'feature2'
908            }
909          }
910        }
911        action_config {
912          config_name: 'config'
913          flag_set {
914            with_feature {
915              feature: 'feature1'
916            }
917            with_feature {
918              not_feature: 'feature2'
919            }
920          }
921        }
922    """)
923    second = make_toolchain("""
924        feature {
925          name: 'feature'
926          flag_set {
927            with_feature {
928              not_feature: 'feature2'
929            }
930            with_feature {
931              feature: 'feature1'
932            }
933          }
934        }
935        action_config {
936          config_name: 'config'
937          flag_set {
938            with_feature {
939              not_feature: 'feature2'
940            }
941            with_feature {
942              feature: 'feature1'
943            }
944          }
945        }
946    """)
947    mock_stdout = StringIO()
948    with mock.patch("sys.stdout", mock_stdout):
949      compare_ctoolchains(first, second)
950      self.assertIn("No difference", mock_stdout.getvalue())
951
952  def test_flag_set_ignores_with_feature_set_lists_order(self):
953    first = make_toolchain("""
954        feature {
955          name: 'feature'
956          flag_set{
957            with_feature {
958              feature: 'feature1'
959              feature: 'feature2'
960              not_feature: 'not_feature1'
961              not_feature: 'not_feature2'
962            }
963          }
964        }
965        action_config {
966          config_name: 'config'
967          flag_set{
968            with_feature {
969              feature: 'feature1'
970              feature: 'feature2'
971              not_feature: 'not_feature1'
972              not_feature: 'not_feature2'
973            }
974          }
975        }
976    """)
977    second = make_toolchain("""
978        feature {
979          name: 'feature'
980          flag_set{
981            with_feature {
982              feature: 'feature2'
983              feature: 'feature1'
984              not_feature: 'not_feature2'
985              not_feature: 'not_feature1'
986            }
987          }
988        }
989        action_config {
990          config_name: 'config'
991          flag_set{
992            with_feature {
993              feature: 'feature2'
994              feature: 'feature1'
995              not_feature: 'not_feature2'
996              not_feature: 'not_feature1'
997            }
998          }
999        }
1000    """)
1001    mock_stdout = StringIO()
1002    with mock.patch("sys.stdout", mock_stdout):
1003      compare_ctoolchains(first, second)
1004      self.assertIn("No difference", mock_stdout.getvalue())
1005
1006  def test_flag_set_preserves_flag_group_order(self):
1007    first = make_toolchain("""
1008        feature {
1009          name: 'feature'
1010          flag_set {
1011            flag_group {
1012              flag: 'a'
1013            }
1014            flag_group {
1015              flag: 'b'
1016            }
1017          }
1018        }
1019        action_config {
1020          config_name: 'config'
1021          flag_set {
1022             flag_group {
1023               flag: 'a'
1024             }
1025             flag_group {
1026               flag: 'b'
1027             }
1028          }
1029        }
1030    """)
1031    second = make_toolchain("""
1032       feature {
1033          name: 'feature'
1034          flag_set {
1035            flag_group {
1036              flag: 'b'
1037            }
1038            flag_group {
1039              flag: 'a'
1040            }
1041          }
1042        }
1043        action_config {
1044          config_name: 'config'
1045          flag_set {
1046            flag_group {
1047              flag: 'b'
1048            }
1049            flag_group {
1050              flag: 'a'
1051            }
1052          }
1053        }
1054    """)
1055    mock_stdout = StringIO()
1056    with mock.patch("sys.stdout", mock_stdout):
1057      compare_ctoolchains(first, second)
1058      self.assertIn(
1059          "* Feature 'feature' differs before and after", mock_stdout.getvalue()
1060      )
1061      self.assertIn(
1062          "* Action config 'config' differs before and after",
1063          mock_stdout.getvalue(),
1064      )
1065
1066  def test_flag_group_preserves_flags_order(self):
1067    first = make_toolchain("""
1068        feature {
1069          name: 'feature'
1070          flag_set{
1071            flag_group {
1072              flag: 'flag1'
1073              flag: 'flag2'
1074            }
1075          }
1076        }
1077        action_config {
1078          config_name: 'config'
1079          flag_set{
1080            flag_group {
1081              flag: 'flag1'
1082              flag: 'flag2'
1083            }
1084          }
1085        }
1086    """)
1087    second = make_toolchain("""
1088        feature {
1089          name: 'feature'
1090          flag_set{
1091            flag_group {
1092              flag: 'flag2'
1093              flag: 'flag1'
1094            }
1095          }
1096        }
1097        action_config {
1098          config_name: 'config'
1099          flag_set{
1100            flag_group {
1101              flag: 'flag2'
1102              flag: 'flag1'
1103            }
1104          }
1105        }
1106    """)
1107    mock_stdout = StringIO()
1108    with mock.patch("sys.stdout", mock_stdout):
1109      compare_ctoolchains(first, second)
1110      self.assertIn(
1111          "* Feature 'feature' differs before and after", mock_stdout.getvalue()
1112      )
1113      self.assertIn(
1114          "* Action config 'config' differs before and after",
1115          mock_stdout.getvalue(),
1116      )
1117
1118  def test_flag_group_iterate_over_differs(self):
1119    first = make_toolchain("""
1120        feature {
1121          name: 'feature'
1122          flag_set{
1123            flag_group {
1124              iterate_over: 'a'
1125            }
1126          }
1127        }
1128        action_config {
1129          config_name: 'config'
1130          flag_set{
1131            flag_group {
1132              iterate_over: 'a'
1133            }
1134          }
1135        }
1136    """)
1137    second = make_toolchain("""
1138        feature {
1139          name: 'feature'
1140          flag_set{
1141            flag_group {
1142              iterate_over: 'b'
1143            }
1144          }
1145        }
1146        action_config {
1147          config_name: 'config'
1148          flag_set{
1149            flag_group {
1150              iterate_over: 'b'
1151            }
1152          }
1153        }
1154    """)
1155    mock_stdout = StringIO()
1156    with mock.patch("sys.stdout", mock_stdout):
1157      compare_ctoolchains(first, second)
1158      self.assertIn(
1159          "* Feature 'feature' differs before and after", mock_stdout.getvalue()
1160      )
1161      self.assertIn(
1162          "* Action config 'config' differs before and after",
1163          mock_stdout.getvalue(),
1164      )
1165
1166  def test_flag_group_expand_if_true_differs(self):
1167    first = make_toolchain("""
1168        feature {
1169          name: 'feature'
1170          flag_set{
1171            flag_group {
1172              expand_if_true: 'a'
1173            }
1174          }
1175        }
1176        action_config {
1177          config_name: 'config'
1178          flag_set{
1179            flag_group {
1180              expand_if_true: 'a'
1181            }
1182          }
1183        }
1184    """)
1185    second = make_toolchain("""
1186        feature {
1187          name: 'feature'
1188          flag_set{
1189            flag_group {
1190              expand_if_true: 'b'
1191            }
1192          }
1193        }
1194        action_config {
1195          config_name: 'config'
1196          flag_set{
1197            flag_group {
1198              expand_if_true: 'b'
1199            }
1200          }
1201        }
1202    """)
1203    mock_stdout = StringIO()
1204    with mock.patch("sys.stdout", mock_stdout):
1205      compare_ctoolchains(first, second)
1206      self.assertIn(
1207          "* Feature 'feature' differs before and after", mock_stdout.getvalue()
1208      )
1209      self.assertIn(
1210          "* Action config 'config' differs before and after",
1211          mock_stdout.getvalue(),
1212      )
1213
1214  def test_flag_group_expand_if_false_differs(self):
1215    first = make_toolchain("""
1216        feature {
1217          name: 'feature'
1218          flag_set{
1219            flag_group {
1220              expand_if_false: 'a'
1221            }
1222          }
1223        }
1224        action_config {
1225          config_name: 'config'
1226          flag_set{
1227            flag_group {
1228              expand_if_false: 'a'
1229            }
1230          }
1231        }
1232    """)
1233    second = make_toolchain("""
1234        feature {
1235          name: 'feature'
1236          flag_set{
1237            flag_group {
1238              expand_if_false: 'b'
1239            }
1240          }
1241        }
1242        action_config {
1243          config_name: 'config'
1244          flag_set{
1245            flag_group {
1246              expand_if_false: 'b'
1247            }
1248          }
1249        }
1250    """)
1251    mock_stdout = StringIO()
1252    with mock.patch("sys.stdout", mock_stdout):
1253      compare_ctoolchains(first, second)
1254      self.assertIn(
1255          "* Feature 'feature' differs before and after", mock_stdout.getvalue()
1256      )
1257      self.assertIn(
1258          "* Action config 'config' differs before and after",
1259          mock_stdout.getvalue(),
1260      )
1261
1262  def test_flag_group_expand_if_all_available_differs(self):
1263    first = make_toolchain("""
1264        feature {
1265          name: 'feature'
1266          flag_set{
1267            flag_group {
1268              expand_if_all_available: 'a'
1269            }
1270          }
1271        }
1272        action_config {
1273          config_name: 'config'
1274          flag_set{
1275            flag_group {
1276              expand_if_all_available: 'a'
1277            }
1278          }
1279        }
1280    """)
1281    second = make_toolchain("""
1282        feature {
1283          name: 'feature'
1284          flag_set{
1285            flag_group {
1286              expand_if_all_available: 'b'
1287            }
1288          }
1289        }
1290        action_config {
1291          config_name: 'config'
1292          flag_set{
1293            flag_group {
1294              expand_if_all_available: 'b'
1295            }
1296          }
1297        }
1298    """)
1299    mock_stdout = StringIO()
1300    with mock.patch("sys.stdout", mock_stdout):
1301      compare_ctoolchains(first, second)
1302      self.assertIn(
1303          "* Feature 'feature' differs before and after", mock_stdout.getvalue()
1304      )
1305      self.assertIn(
1306          "* Action config 'config' differs before and after",
1307          mock_stdout.getvalue(),
1308      )
1309
1310  def test_flag_group_expand_if_none_available_differs(self):
1311    first = make_toolchain("""
1312        feature {
1313          name: 'feature'
1314          flag_set{
1315            flag_group {
1316              expand_if_none_available: 'a'
1317            }
1318          }
1319        }
1320        action_config {
1321          config_name: 'config'
1322          flag_set{
1323            flag_group {
1324              expand_if_none_available: 'a'
1325            }
1326          }
1327        }
1328    """)
1329    second = make_toolchain("""
1330        feature {
1331          name: 'feature'
1332          flag_set{
1333            flag_group {
1334              expand_if_none_available: 'b'
1335            }
1336          }
1337        }
1338        action_config {
1339          config_name: 'config'
1340          flag_set{
1341            flag_group {
1342              expand_if_none_available: 'b'
1343            }
1344          }
1345        }
1346    """)
1347    mock_stdout = StringIO()
1348    with mock.patch("sys.stdout", mock_stdout):
1349      compare_ctoolchains(first, second)
1350      self.assertIn(
1351          "* Feature 'feature' differs before and after", mock_stdout.getvalue()
1352      )
1353      self.assertIn(
1354          "* Action config 'config' differs before and after",
1355          mock_stdout.getvalue(),
1356      )
1357
1358  def test_flag_group_expand_if_all_available_ignores_order(self):
1359    first = make_toolchain("""
1360        feature {
1361          name: 'feature'
1362          flag_set{
1363            flag_group {
1364              expand_if_all_available: 'a'
1365              expand_if_all_available: 'b'
1366            }
1367          }
1368        }
1369        action_config {
1370          config_name: 'config'
1371          flag_set{
1372            flag_group {
1373              expand_if_all_available: 'a'
1374              expand_if_all_available: 'b'
1375            }
1376          }
1377        }
1378    """)
1379    second = make_toolchain("""
1380        feature {
1381          name: 'feature'
1382          flag_set{
1383            flag_group {
1384              expand_if_all_available: 'b'
1385              expand_if_all_available: 'a'
1386            }
1387          }
1388        }
1389        action_config {
1390          config_name: 'config'
1391          flag_set{
1392            flag_group {
1393              expand_if_all_available: 'b'
1394              expand_if_all_available: 'a'
1395            }
1396          }
1397        }
1398    """)
1399    mock_stdout = StringIO()
1400    with mock.patch("sys.stdout", mock_stdout):
1401      compare_ctoolchains(first, second)
1402      self.assertIn("No difference", mock_stdout.getvalue())
1403
1404  def test_flag_group_expand_if_none_available_ignores_order(self):
1405    first = make_toolchain("""
1406        feature {
1407          name: 'feature'
1408          flag_set{
1409            flag_group {
1410              expand_if_none_available: 'a'
1411              expand_if_none_available: 'b'
1412            }
1413          }
1414        }
1415        action_config {
1416          config_name: 'config'
1417          flag_set{
1418            flag_group {
1419              expand_if_none_available: 'a'
1420              expand_if_none_available: 'b'
1421            }
1422          }
1423        }
1424    """)
1425    second = make_toolchain("""
1426        feature {
1427          name: 'feature'
1428          flag_set{
1429            flag_group {
1430              expand_if_none_available: 'b'
1431              expand_if_none_available: 'a'
1432            }
1433          }
1434        }
1435        action_config {
1436          config_name: 'config'
1437          flag_set{
1438            flag_group {
1439              expand_if_none_available: 'b'
1440              expand_if_none_available: 'a'
1441            }
1442          }
1443        }
1444    """)
1445    mock_stdout = StringIO()
1446    with mock.patch("sys.stdout", mock_stdout):
1447      compare_ctoolchains(first, second)
1448      self.assertIn("No difference", mock_stdout.getvalue())
1449
1450  def test_flag_group_expand_if_equal_differs(self):
1451    first = make_toolchain("""
1452        feature {
1453          name: 'feature'
1454          flag_set{
1455            flag_group {
1456              expand_if_equal {
1457                variable: 'first'
1458                value: 'val'
1459              }
1460            }
1461          }
1462        }
1463        action_config {
1464          config_name: 'config'
1465          flag_set{
1466            flag_group {
1467              expand_if_equal {
1468                variable: 'first'
1469                value: 'val'
1470              }
1471            }
1472          }
1473        }
1474    """)
1475    second = make_toolchain("""
1476        feature {
1477          name: 'feature'
1478          flag_set{
1479            flag_group {
1480              expand_if_equal {
1481                variable: 'second'
1482                value: 'val'
1483              }
1484            }
1485          }
1486        }
1487        action_config {
1488          config_name: 'config'
1489          flag_set{
1490            flag_group {
1491              expand_if_equal {
1492                variable: 'second'
1493                value: 'val'
1494              }
1495            }
1496          }
1497        }
1498    """)
1499    mock_stdout = StringIO()
1500    with mock.patch("sys.stdout", mock_stdout):
1501      compare_ctoolchains(first, second)
1502      self.assertIn(
1503          "* Feature 'feature' differs before and after", mock_stdout.getvalue()
1504      )
1505      self.assertIn(
1506          "* Action config 'config' differs before and after",
1507          mock_stdout.getvalue(),
1508      )
1509
1510  def test_flag_group_flag_groups_differ(self):
1511    first = make_toolchain("""
1512        feature {
1513          name: 'feature'
1514          flag_set{
1515            flag_group {
1516              flag_group {
1517                flag: 'a'
1518                flag: 'b'
1519              }
1520            }
1521          }
1522        }
1523        action_config {
1524          config_name: 'config'
1525          flag_set{
1526            flag_group {
1527              flag_group {
1528                flag: 'a'
1529                flag: 'b'
1530              }
1531            }
1532          }
1533        }
1534    """)
1535    second = make_toolchain("""
1536        feature {
1537          name: 'feature'
1538          flag_set{
1539            flag_group {
1540              flag_group {
1541                flag: 'b'
1542                flag: 'a'
1543              }
1544            }
1545          }
1546        }
1547        action_config {
1548          config_name: 'config'
1549          flag_set{
1550            flag_group {
1551              flag_group {
1552                flag: 'b'
1553                flag: 'a'
1554              }
1555            }
1556          }
1557        }
1558    """)
1559    mock_stdout = StringIO()
1560    with mock.patch("sys.stdout", mock_stdout):
1561      compare_ctoolchains(first, second)
1562      self.assertIn(
1563          "* Feature 'feature' differs before and after", mock_stdout.getvalue()
1564      )
1565      self.assertIn(
1566          "* Action config 'config' differs before and after",
1567          mock_stdout.getvalue(),
1568      )
1569
1570  def test_action_configs_not_ordered(self):
1571    first = make_toolchain("""
1572        action_config {
1573          config_name: 'action1'
1574        }
1575        action_config {
1576          config_name: 'action2'
1577        }
1578    """)
1579    second = make_toolchain("""
1580        action_config {
1581          config_name: 'action2'
1582        }
1583        action_config {
1584          config_name: 'action1'
1585        }
1586    """)
1587    mock_stdout = StringIO()
1588    with mock.patch("sys.stdout", mock_stdout):
1589      compare_ctoolchains(first, second)
1590      self.assertIn("Action configs not in right order", mock_stdout.getvalue())
1591
1592  def test_action_configs_missing(self):
1593    first = make_toolchain("""
1594        action_config {
1595          config_name: 'action1'
1596        }
1597    """)
1598    second = make_toolchain("""
1599        action_config {
1600          config_name: 'action2'
1601        }
1602    """)
1603    error_only_first = (
1604        "* List before change contains entries for the "
1605        "following action_configs that the list after the "
1606        "change doesn't:\n[action1]\n"
1607    )
1608    error_only_second = (
1609        "* List after change contains entries for the "
1610        "following action_configs that the list before the "
1611        "change doesn't:\n[action2]\n"
1612    )
1613    mock_stdout = StringIO()
1614    with mock.patch("sys.stdout", mock_stdout):
1615      compare_ctoolchains(first, second)
1616      self.assertIn(error_only_first, mock_stdout.getvalue())
1617      self.assertIn(error_only_second, mock_stdout.getvalue())
1618
1619  def test_action_config_enabled(self):
1620    first = make_toolchain("""
1621        action_config {
1622          config_name: 'config'
1623          enabled: true
1624        }
1625    """)
1626    second = make_toolchain("""
1627        action_config {
1628          config_name: 'config'
1629          enabled: false
1630        }
1631    """)
1632    mock_stdout = StringIO()
1633    with mock.patch("sys.stdout", mock_stdout):
1634      compare_ctoolchains(first, second)
1635      self.assertIn(
1636          "* Action config 'config' differs before and after",
1637          mock_stdout.getvalue(),
1638      )
1639
1640  def test_action_config_action_name(self):
1641    first = make_toolchain("""
1642        action_config {
1643          config_name: 'config'
1644          action_name: 'config1'
1645        }
1646    """)
1647    second = make_toolchain("""
1648        action_config {
1649          config_name: 'config'
1650          action_name: 'config2'
1651        }
1652    """)
1653    mock_stdout = StringIO()
1654    with mock.patch("sys.stdout", mock_stdout):
1655      compare_ctoolchains(first, second)
1656      self.assertIn(
1657          "* Action config 'config' differs before and after",
1658          mock_stdout.getvalue(),
1659      )
1660
1661  def test_action_config_tool_tool_path_differs(self):
1662    first = make_toolchain("""
1663        action_config {
1664          config_name: 'config'
1665          tool {
1666            tool_path: 'path1'
1667          }
1668        }
1669    """)
1670    second = make_toolchain("""
1671        action_config {
1672          config_name: 'config'
1673          tool {
1674            tool_path: 'path2'
1675          }
1676        }
1677    """)
1678    mock_stdout = StringIO()
1679    with mock.patch("sys.stdout", mock_stdout):
1680      compare_ctoolchains(first, second)
1681      self.assertIn(
1682          "* Action config 'config' differs before and after",
1683          mock_stdout.getvalue(),
1684      )
1685
1686  def test_action_config_tool_execution_requirements_differ(self):
1687    first = make_toolchain("""
1688        action_config {
1689          config_name: 'config'
1690          tool {
1691            execution_requirement: 'a'
1692          }
1693        }
1694    """)
1695    second = make_toolchain("""
1696        action_config {
1697          config_name: 'config'
1698          tool {
1699            execution_requirement: 'b'
1700          }
1701        }
1702    """)
1703    mock_stdout = StringIO()
1704    with mock.patch("sys.stdout", mock_stdout):
1705      compare_ctoolchains(first, second)
1706      self.assertIn(
1707          "* Action config 'config' differs before and after",
1708          mock_stdout.getvalue(),
1709      )
1710
1711  def test_action_config_tool_execution_requirements_ignores_order(self):
1712    first = make_toolchain("""
1713        action_config {
1714          config_name: 'config'
1715          tool {
1716            execution_requirement: 'a'
1717            execution_requirement: 'b'
1718          }
1719        }
1720    """)
1721    second = make_toolchain("""
1722        action_config {
1723          config_name: 'config'
1724          tool {
1725            execution_requirement: 'b'
1726            execution_requirement: 'a'
1727          }
1728        }
1729    """)
1730    mock_stdout = StringIO()
1731    with mock.patch("sys.stdout", mock_stdout):
1732      compare_ctoolchains(first, second)
1733      self.assertIn("No difference", mock_stdout.getvalue())
1734
1735  def test_action_config_implies_differs(self):
1736    first = make_toolchain("""
1737        action_config {
1738          config_name: 'config'
1739          implies: 'a'
1740        }
1741    """)
1742    second = make_toolchain("""
1743        action_config {
1744          config_name: 'config'
1745          implies: 'b'
1746        }
1747    """)
1748    mock_stdout = StringIO()
1749    with mock.patch("sys.stdout", mock_stdout):
1750      compare_ctoolchains(first, second)
1751      self.assertIn(
1752          "* Action config 'config' differs before and after",
1753          mock_stdout.getvalue(),
1754      )
1755
1756  def test_action_config_implies_preserves_order(self):
1757    first = make_toolchain("""
1758        action_config {
1759          config_name: 'config'
1760          implies: 'a'
1761          implies: 'b'
1762        }
1763    """)
1764    second = make_toolchain("""
1765        action_config {
1766          config_name: 'config'
1767          implies: 'b'
1768          implies: 'a'
1769        }
1770    """)
1771    mock_stdout = StringIO()
1772    with mock.patch("sys.stdout", mock_stdout):
1773      compare_ctoolchains(first, second)
1774      self.assertIn(
1775          "* Action config 'config' differs before and after",
1776          mock_stdout.getvalue(),
1777      )
1778
1779  def test_unused_tool_path(self):
1780    first = make_toolchain("""
1781        tool_path {
1782          name: "empty"
1783          path: ""
1784        }
1785    """)
1786    second = make_toolchain("""
1787        tool_path {
1788          name: "empty"
1789          path: "NOT_USED"
1790        }
1791    """)
1792    mock_stdout = StringIO()
1793    with mock.patch("sys.stdout", mock_stdout):
1794      compare_ctoolchains(first, second)
1795      self.assertIn("No difference", mock_stdout.getvalue())
1796
1797  def test_unused_tool_path_in_tool(self):
1798    first = make_toolchain("""
1799        action_config {
1800          config_name: 'config'
1801          tool {
1802            tool_path: ''
1803          }
1804        }
1805    """)
1806    second = make_toolchain("""
1807        action_config {
1808          config_name: 'config'
1809          tool {
1810            tool_path: 'NOT_USED'
1811          }
1812        }
1813    """)
1814    mock_stdout = StringIO()
1815    with mock.patch("sys.stdout", mock_stdout):
1816      compare_ctoolchains(first, second)
1817      self.assertIn("No difference", mock_stdout.getvalue())
1818
1819
1820if __name__ == "__main__":
1821  unittest.main()
1822