@@ -628,6 +628,122 @@ def test_compare(config_tmpdir, west_init_tmpdir):
628628 assert 'mybranch' in cmd ('compare --no-ignore-branches' )
629629
630630
631+ def test_compare_format_manifest (config_tmpdir , west_init_tmpdir ):
632+ # ManifestProject special-casing: empty output, single-line
633+ # machine-readable output without any banner, and the path/sha/url
634+ # keys that are distinct from regular projects -- same conventions
635+ # as 'west list'.
636+
637+ # No dirty projects -> no output at all.
638+ assert cmd ('compare --format {name}' ) == ''
639+
640+ # Dirty the manifest repo.
641+ foo = west_init_tmpdir / 'zephyr' / 'foo'
642+ with open (foo , 'w' ):
643+ pass
644+
645+ # The whole point of --format is stable machine-readable output:
646+ # no banner ('=== ...'), exactly one line per dirty project, and
647+ # the ManifestProject is named 'manifest' just like 'west list'.
648+ out = cmd ('compare --format {name}' )
649+ assert '===' not in out
650+ assert out .splitlines () == ['manifest' ]
651+
652+ # Path-style keys for the ManifestProject come from manifest.repo_*,
653+ # not project.*. {sha}/{url} are 'N/A' and {revision} is 'HEAD'.
654+ # {groups} is empty. These all match 'west list' behavior.
655+ assert cmd ('compare -f {path}' ).strip () == 'zephyr'
656+ assert cmd ('compare -f {abspath}' ).strip () == str (west_init_tmpdir / 'zephyr' )
657+ assert cmd ('compare -f {posixpath}' ).strip () == (Path (west_init_tmpdir / 'zephyr' ).as_posix ())
658+ assert cmd ('compare -f {sha}' ).strip () == 'N/A'
659+ assert cmd ('compare -f {url}' ).strip () == 'N/A'
660+ assert cmd ('compare -f {revision}' ).strip () == 'HEAD'
661+ assert cmd (['compare' , '-f' , '[{groups}]' ]).strip () == '[]'
662+
663+ # --exit-code still fires when --format produced any output.
664+ with pytest .raises (SystemExit ):
665+ cmd ('compare --exit-code --format {name}' )
666+
667+ # Cleanup -> empty output again.
668+ os .unlink (foo )
669+ assert cmd ('compare --format {name}' ) == ''
670+
671+
672+ def test_compare_format_projects (config_tmpdir , west_init_tmpdir ):
673+ # Regular (non-manifest) projects: every format key, multi-project
674+ # ordering, and interactions with --all / named projects / the
675+ # manifest.group-filter configuration option.
676+
677+ cmd ('update' )
678+ kconfiglib = west_init_tmpdir / 'subdir' / 'Kconfiglib'
679+ bar = kconfiglib / 'bar'
680+ with open (bar , 'w' ):
681+ pass
682+
683+ # {sha} must resolve to a real 40-char hex SHA for a cloned project
684+ # (not 'N/A'): this verifies DelayFormat actually gets invoked when
685+ # the format string references {sha}.
686+ line = cmd (['compare' , '-f' , '{name}|{sha}' ]).strip ()
687+ name , sha = line .split ('|' )
688+ assert name == 'Kconfiglib'
689+ assert re .match (r'^[0-9a-f]{40}$' , sha )
690+
691+ # All remaining keys.
692+ out = cmd ([
693+ 'compare' ,
694+ '-f' ,
695+ '{name}|{url}|{path}|{abspath}|{posixpath}|{revision}|{groups}' ,
696+ ]).strip ()
697+ fields = out .split ('|' )
698+ assert fields [0 ] == 'Kconfiglib'
699+ assert fields [1 ].endswith ('/Kconfiglib' ) # url from test fixture's url-base
700+ # {path} preserves the manifest YAML value verbatim (always forward
701+ # slashes), regardless of OS -- same behavior as 'west list -f {path}'.
702+ assert fields [2 ] == 'subdir/Kconfiglib'
703+ assert fields [3 ] == str (kconfiglib )
704+ assert fields [4 ] == Path (kconfiglib ).as_posix ()
705+ assert fields [5 ] == 'zephyr'
706+ assert fields [6 ] == 'Kconfiglib-group' # set in test fixture's manifest
707+
708+ # Multiple dirty projects produce one line per project, in manifest
709+ # order (Kconfiglib before tagged_repo in this fixture's manifest).
710+ tagged = west_init_tmpdir / 'tagged_repo' / 'baz'
711+ with open (tagged , 'w' ):
712+ pass
713+ assert cmd ('compare -f {name}' ).splitlines () == ['Kconfiglib' , 'tagged_repo' ]
714+ os .unlink (tagged )
715+
716+ # An inactive project is skipped by default but appears with --all
717+ # or when named explicitly (same active-project semantics as plain
718+ # 'west compare').
719+ cmd ('config manifest.group-filter -- -Kconfiglib-group' )
720+ assert cmd ('compare -f {name}' ) == ''
721+ assert cmd ('compare --all -f {name}' ).strip () == 'Kconfiglib'
722+ assert cmd ('compare -f {name} Kconfiglib' ).strip () == 'Kconfiglib'
723+ cmd ('config -d manifest.group-filter' )
724+
725+ os .unlink (bar )
726+
727+
728+ def test_compare_format_errors (config_tmpdir , west_init_tmpdir ):
729+ # Malformed format strings must fail cleanly via self.die() ->
730+ # SystemExit, not with an uncaught KeyError/IndexError traceback.
731+ # Dirty the manifest repo so the format path is actually reached.
732+ foo = west_init_tmpdir / 'zephyr' / 'foo'
733+ with open (foo , 'w' ):
734+ pass
735+
736+ # Unknown named key.
737+ with pytest .raises (SystemExit ):
738+ cmd ('compare -f {bogus}' )
739+
740+ # Positional argument (fmt.format() has no positional args).
741+ with pytest .raises (SystemExit ):
742+ cmd ('compare -f {0}' )
743+
744+ os .unlink (foo )
745+
746+
631747def test_diff (west_init_tmpdir ):
632748 # FIXME: Check output
633749
0 commit comments