def zephyr_manifest(self): # load the upstream manifest. the west.manifest APIs guarantee # in this case that its project hierarchy is rooted in the NCS # directory. z_project = self.manifest.get_projects(['zephyr'], allow_paths=False, only_cloned=True)[0] cp = z_project.git(f'show {self.zephyr_rev}:west.yml', capture_stdout=True, check=True) z_west_yml = cp.stdout.decode('utf-8') try: # The topdir kwarg was added in a pre-release west, which # is required to use this file. The latest stable (0.6.3) # doesn't have it, so pylint is failing with a false error # report here. Turn it off for now; this can be removed # when west 0.7 is out. # # pylint: disable=unexpected-keyword-arg return Manifest.from_data(source_data=yaml.safe_load(z_west_yml), topdir=self.topdir) except MalformedManifest: log.die(f"can't load zephyr manifest; file {z_west_yml} " "is malformed")
def test_sections(): # We no longer validate the west section, so things that would # once have been schema errors shouldn't matter. content_wrong_west = '''\ west: url: https://example.com revision: abranch wrongfield: avalue manifest: remotes: - name: testremote url-base: https://example.com projects: - name: testproject remote: testremote path: sub/directory ''' with patch('west.util.west_topdir', return_value=os.path.realpath('/west_top')): # Parsing manifest only, no exception raised manifest = Manifest.from_data(yaml.safe_load(content_wrong_west)) assert manifest.projects[1].path == 'sub' + os.path.sep + 'directory' assert manifest.projects[1].abspath == \ os.path.realpath('/west_top/sub/directory')
def test_ignore_west_section(): # We no longer validate the west section, so things that would # once have been schema errors shouldn't be anymore. Projects # should still work as expected regardless of what's in there. content_wrong_west = '''\ west: url: https://example.com revision: abranch wrongfield: avalue manifest: remotes: - name: testremote url-base: https://example.com projects: - name: testproject remote: testremote path: sub/directory ''' # Parsing manifest only, no exception raised manifest = Manifest.from_data(yaml.safe_load(content_wrong_west), topdir='/west_top') p1 = manifest.projects[1] assert PurePath(p1.path) == PurePath('sub', 'directory') assert PurePath(p1.abspath) == PurePath('/west_top/sub/directory')
def test_sections(): # Projects must be able to override their default paths. content_wrong_west = '''\ west: url: https://example.com revision: abranch wrongfield: avalue manifest: remotes: - name: testremote url-base: https://example.com projects: - name: testproject remote: testremote path: sub/directory ''' with patch('west.util.west_topdir', return_value=os.path.realpath('/west_top')): # Parsing manifest only, no exception raised manifest = Manifest.from_data(yaml.safe_load(content_wrong_west), sections=['manifest']) assert manifest.projects[1].path == 'sub' + os.path.sep + 'directory' assert manifest.projects[1].abspath == \ os.path.realpath('/west_top/sub/directory') content_wrong_manifest = '''\ west: url: https://example.com revision: abranch manifest: remotes: - name: testremote url-base: https://example.com projects: - name: testproject remote: testremote path: sub/directory ''' with patch('west.util.west_topdir', return_value=os.path.realpath('/west_top')): # Parsing west section only, no exception raised manifest = Manifest.from_data(yaml.safe_load(content_wrong_manifest), sections=['west']) assert manifest.west_project.url == 'https://example.com' assert manifest.west_project.revision == 'abranch'
def test_default_clone_depth(config_file_project_setup): # Defaults and clone depth should work as in this example. content = '''\ manifest: defaults: remote: testremote1 revision: defaultrev remotes: - name: testremote1 url-base: https://example1.com - name: testremote2 url-base: https://example2.com projects: - name: testproject1 - name: testproject2 remote: testremote2 revision: rev clone-depth: 1 ''' r1 = Remote('testremote1', 'https://example1.com') r2 = Remote('testremote2', 'https://example2.com') d = Defaults(remote=r1, revision='defaultrev') with patch('west.util.west_topdir', return_value='/west_top'): manifest = Manifest.from_data(yaml.safe_load(content)) expected = [ ManifestProject(path='manifestproject'), Project('testproject1', d, path='testproject1', clone_depth=None, revision=d.revision, remote=r1), Project('testproject2', d, path='testproject2', clone_depth=1, revision='rev', remote=r2) ] # Check that default attributes match. assert manifest.defaults.remote == d.remote assert manifest.defaults.revision == d.revision # Check the remotes are as expected. assert list(manifest.remotes) == [r1, r2] # Check that the projects are as expected. for p, e in zip(manifest.projects, expected): deep_eq_check(p, e) assert all(p.abspath == os.path.realpath(os.path.join('/west_top', p.path)) for p in manifest.projects)
def test_project_named_west(): # A project named west is allowed now, even though it was once an error. content = '''\ manifest: projects: - name: west url: https://foo.com ''' manifest = Manifest.from_data(yaml.safe_load(content)) assert manifest.projects[1].name == 'west'
def test_no_remote_ok(west_topdir): # remotes isn't required if projects are specified by URL. content = '''\ manifest: projects: - name: testproject url: https://example.com/my-project ''' manifest = Manifest.from_data(yaml.safe_load(content)) assert manifest.projects[1].url == 'https://example.com/my-project'
def test_get_projects_unknown(): content = '''\ manifest: projects: - name: foo url: https://foo.com ''' with patch('west.util.west_topdir', return_value='/west_top'): manifest = Manifest.from_data(yaml.safe_load(content)) with pytest.raises(ValueError): manifest.get_projects(['unknown'])
def test_default_clone_depth(): # Defaults and clone depth should work as in this example. content = '''\ manifest: defaults: remote: testremote1 revision: defaultrev remotes: - name: testremote1 url-base: https://example1.com - name: testremote2 url-base: https://example2.com projects: - name: testproject1 - name: testproject2 remote: testremote2 revision: rev clone-depth: 1 ''' r1 = Remote('testremote1', 'https://example1.com') r2 = Remote('testremote2', 'https://example2.com') d = Defaults(remote=r1, revision='defaultrev') manifest = Manifest.from_data(yaml.safe_load(content)) expected = [ Project('testproject1', d, path='testproject1', clone_depth=None, revision=d.revision, remote=r1), Project('testproject2', d, path='testproject2', clone_depth=1, revision='rev', remote=r2) ] # Check that default attributes match. assert manifest.defaults.remote == d.remote assert manifest.defaults.revision == d.revision # Check the remotes are as expected. assert list(manifest.remotes) == [r1, r2] # Check that the projects are as expected. for p, e in zip(manifest.projects[1:], expected): check_proj_consistency(p, e)
def test_project_west_commands(): # Projects may specify subdirectories containing west commands. content = '''\ manifest: projects: - name: zephyr url: https://foo.com west-commands: some-path/west-commands.yml ''' manifest = Manifest.from_data(yaml.safe_load(content)) assert len(manifest.projects) == 2 assert manifest.projects[1].west_commands == 'some-path/west-commands.yml'
def test_get_projects_unknown(): # Attempting to get an unknown project is an error. # TODO: add more testing for get_projects(). content = '''\ manifest: projects: - name: foo url: https://foo.com ''' manifest = Manifest.from_data(yaml.safe_load(content)) with pytest.raises(ValueError): manifest.get_projects(['unknown'])
def test_self_tag(project_setup): # Manifests with self tag reference. content = '''\ manifest: remotes: - name: testremote1 url-base: https://example1.com - name: testremote2 url-base: https://example2.com projects: - name: testproject1 remote: testremote1 revision: rev1 - name: testproject2 remote: testremote2 self: path: mainproject ''' r1 = Remote('testremote1', 'https://example1.com') r2 = Remote('testremote2', 'https://example2.com') with patch('west.util.west_topdir', return_value='/west_top'): manifest = Manifest.from_data(yaml.safe_load(content)) expected = [ ManifestProject(path='mainproject'), Project('testproject1', None, path='testproject1', clone_depth=None, revision='rev1', remote=r1), Project('testproject2', None, path='testproject2', clone_depth=None, revision='master', remote=r2) ] # Check the remotes are as expected. assert list(manifest.remotes) == [r1, r2] # Check the projects are as expected. for p, e in zip(manifest.projects, expected): deep_eq_check(p, e) assert all(p.abspath == os.path.realpath(os.path.join('/west_top', p.path)) for p in manifest.projects)
def manifest_from_url(token, url): log(f'Creating manifest from {url}') # Download manifest file header = {'Authorization': f'token {token}'} req = requests.get(url=url, headers=header) try: manifest = Manifest.from_data(req.content.decode(), import_flags=ImportFlag.IGNORE_PROJECTS) except MalformedManifest as e: die(f'Failed to parse manifest from {url}: {e}') return manifest
def test_self_tag(): # Manifests may contain a self section describing their behavior. # It should work with multiple projects and remotes as expected. content = '''\ manifest: remotes: - name: testremote1 url-base: https://example1.com - name: testremote2 url-base: https://example2.com projects: - name: testproject1 remote: testremote1 revision: rev1 - name: testproject2 remote: testremote2 self: path: the-manifest-path ''' r1 = Remote('testremote1', 'https://example1.com') r2 = Remote('testremote2', 'https://example2.com') manifest = Manifest.from_data(yaml.safe_load(content)) expected = [ ManifestProject(path='the-manifest-path'), Project('testproject1', None, path='testproject1', clone_depth=None, revision='rev1', remote=r1), Project('testproject2', None, path='testproject2', clone_depth=None, revision='master', remote=r2) ] # Check the remotes are as expected. assert list(manifest.remotes) == [r1, r2] # Check the projects are as expected. for p, e in zip(manifest.projects, expected): check_proj_consistency(p, e)
def test_defaults_and_url(west_topdir): # an explicit URL overrides the defaults attribute. content = '''\ manifest: defaults: remote: remote1 remotes: - name: remote1 url-base: https://url1.com/ projects: - name: testproject url: https://url2.com/testproject ''' manifest = Manifest.from_data(yaml.safe_load(content)) assert manifest.projects[1].url == 'https://url2.com/testproject'
def test_west_is_ok(): # Projects named west are allowed now. content = '''\ manifest: remotes: - name: testremote url-base: https://example.com projects: - name: west remote: testremote ''' with patch('west.util.west_topdir', return_value=os.path.realpath('/west_top')): manifest = Manifest.from_data(yaml.safe_load(content)) assert manifest.projects[1].name == 'west'
def test_path(): # Projects must be able to override their default paths. content = '''\ manifest: remotes: - name: testremote url: https://example.com projects: - name: testproject remote: testremote path: sub/directory ''' with patch('west.util.west_topdir', return_value='/west_top'): manifest = Manifest.from_data(yaml.safe_load(content)) assert manifest.projects[0].path == 'sub/directory' assert manifest.projects[0].abspath == '/west_top/sub/directory'
def test_no_defaults(config_file_project_setup): # Manifests with no defaults should work. content = '''\ manifest: remotes: - name: testremote1 url-base: https://example1.com - name: testremote2 url-base: https://example2.com projects: - name: testproject1 remote: testremote1 revision: rev1 - name: testproject2 remote: testremote2 ''' r1 = Remote('testremote1', 'https://example1.com') r2 = Remote('testremote2', 'https://example2.com') with patch('west.util.west_topdir', return_value='/west_top'): manifest = Manifest.from_data(yaml.safe_load(content)) expected = [ SpecialProject('manifestproject', path='manifestproject'), Project('testproject1', r1, None, path='testproject1', clone_depth=None, revision='rev1'), Project('testproject2', r2, None, path='testproject2', clone_depth=None, revision='master') ] # Check the remotes are as expected. assert list(manifest.remotes) == [r1, r2] # Check the projects are as expected. for p, e in zip(manifest.projects, expected): deep_eq_check(p, e) assert all(p.abspath == os.path.realpath(os.path.join('/west_top', p.path)) for p in manifest.projects)
def test_path(): # Projects must be able to override their default paths. # Absolute paths should reflect this setting. content = '''\ manifest: remotes: - name: testremote url-base: https://example.com projects: - name: testproject remote: testremote path: sub/directory ''' manifest = Manifest.from_data(yaml.safe_load(content), topdir='/west_top') assert manifest.projects[1].path == 'sub' + os.path.sep + 'directory' assert manifest.projects[1].posixpath == '/west_top/sub/directory'
def zephyr_manifest(self): # load the upstream manifest. the west.manifest APIs guarantee # in this case that its project hierarchy is rooted in the NCS # directory. z_project = self.manifest.get_projects(['zephyr'], allow_paths=False, only_cloned=True)[0] cp = z_project.git(f'show {self.zephyr_rev}:west.yml', capture_stdout=True, check=True) z_west_yml = cp.stdout.decode('utf-8') try: return Manifest.from_data(source_data=yaml.safe_load(z_west_yml), topdir=self.topdir) except MalformedManifest: log.die(f"can't load zephyr manifest; file {z_west_yml} " "is malformed")
def test_west_commands(): # Projects may specify subdirectories containing west commands. content = '''\ manifest: remotes: - name: testremote url-base: https://example.com projects: - name: zephyr remote: testremote west-commands: some-path/west-commands.yml ''' with patch('west.util.west_topdir', return_value=os.path.realpath('/west_top')): manifest = Manifest.from_data(yaml.safe_load(content)) assert len(manifest.projects) == 2 assert manifest.projects[-1].west_commands == 'some-path/west-commands.yml'
def test_multiple_remotes(): # More than one remote may be used, and one of them may be used as # the default. content = '''\ manifest: defaults: remote: testremote2 remotes: - name: testremote1 url-base: https://example1.com - name: testremote2 url-base: https://example2.com projects: - name: testproject1 remote: testremote1 revision: rev1 - name: testproject2 remote: testremote2 - name: testproject3 ''' r1 = Remote('testremote1', 'https://example1.com') r2 = Remote('testremote2', 'https://example2.com') manifest = Manifest.from_data(yaml.safe_load(content)) expected = [ Project('testproject1', revision='rev1', remote=r1), Project('testproject2', remote=r2), Project('testproject3', remote=r2) ] # Check the remotes are as expected. assert list(manifest.remotes) == [r1, r2] # Check the projects are as expected. for p, e in zip(manifest.projects[1:], expected): check_proj_consistency(p, e) # Throw in an extra check that absolute paths are not available, # just for fun. assert all(p.abspath is None for p in manifest.projects)
def zephyr_manifest(self): # Load the upstream manifest. Since west v0.13, the resulting # projects have no absolute paths, so we'll fix those up so # they're relative to our own topdir. (We can't use # Manifest.from_file() in this case because self.zephyr_rev is # not what's checked out on the file system). z_project = self.manifest.get_projects(['zephyr'], allow_paths=False, only_cloned=True)[0] cp = z_project.git(f'show {self.zephyr_rev}:west.yml', capture_stdout=True, check=True) z_west_yml = cp.stdout.decode('utf-8') try: ret = Manifest.from_data(source_data=yaml.safe_load(z_west_yml), import_flags=ImportFlag.IGNORE) if WEST_V0_13_0_OR_LATER: for project in ret.projects: project.topdir = self.manifest.topdir return ret except MalformedManifest: log.die(f"can't load zephyr manifest; file {z_west_yml} " "is malformed")
def test_manifest_attrs(): # test that the manifest repository, when represented as a project, # has attributes which make sense. # Case 1: everything at defaults content = '''\ manifest: projects: - name: name url: url ''' manifest = Manifest.from_data(yaml.safe_load(content)) mp = manifest.projects[0] assert mp.name == 'manifest' assert mp.path is None assert mp.topdir is None assert mp.abspath is None assert mp.posixpath is None assert mp.url is None assert mp.remote is None assert mp.revision == 'HEAD' assert mp.clone_depth is None # Case 2: path etc. are specified, but not topdir content = '''\ manifest: projects: - name: name url: url self: path: my-path west-commands: cmds.yml ''' manifest = Manifest.from_data(yaml.safe_load(content)) mp = manifest.projects[0] assert mp.name == 'manifest' assert mp.path == 'my-path' assert mp.west_commands == 'cmds.yml' assert mp.topdir is None assert mp.abspath is None assert mp.posixpath is None assert mp.url is None assert mp.remote is None assert mp.revision == 'HEAD' assert mp.clone_depth is None # Case 3: path etc. and topdir are all specified content = '''\ manifest: projects: - name: name url: url self: path: my-path west-commands: cmds.yml ''' manifest = Manifest.from_data(yaml.safe_load(content), manifest_path='should-be-ignored', topdir='/west_top') mp = manifest.projects[0] assert mp.name == 'manifest' assert mp.path == 'my-path' assert mp.topdir is not None assert PurePath(mp.abspath) == PurePath('/west_top/my-path') assert mp.posixpath is not None assert mp.west_commands == 'cmds.yml' assert mp.url is None assert mp.remote is None assert mp.revision == 'HEAD' assert mp.clone_depth is None
def test_invalid(invalid): with open(invalid, 'r') as f: data = yaml.safe_load(f.read()) with pytest.raises(MalformedManifest): Manifest.from_data(source_data=data)
def test_data_and_topdir(tmpdir): # If you specify the topdir along with some source data, you will # get absolute paths, even if it doesn't exist. topdir = str(tmpdir) # Case 1: manifest has no path (projects always have paths) content = '''\ manifest: projects: - name: my-cool-project url: from-manifest-dir ''' manifest = Manifest.from_data(source_data=yaml.safe_load(content), topdir=topdir) assert manifest.topdir == topdir mproj = manifest.projects[0] assert mproj.topdir == topdir assert mproj.path is None p1 = manifest.projects[1] assert PurePath(p1.topdir) == PurePath(topdir) assert PurePath(p1.abspath) == PurePath(str(tmpdir / 'my-cool-project')) # Case 2: manifest path is provided programmatically content = '''\ manifest: projects: - name: my-cool-project url: from-manifest-dir ''' manifest = Manifest.from_data(source_data=yaml.safe_load(content), manifest_path='from-api', topdir=topdir) assert manifest.topdir == topdir mproj = manifest.projects[0] assert PurePath(mproj.topdir).is_absolute() assert PurePath(mproj.topdir) == PurePath(topdir) assert mproj.path == 'from-api' assert PurePath(mproj.abspath).is_absolute() assert PurePath(mproj.abspath) == PurePath(str(tmpdir / 'from-api')) p1 = manifest.projects[1] assert PurePath(p1.topdir) == PurePath(topdir) assert PurePath(p1.abspath) == PurePath(str(tmpdir / 'my-cool-project')) # Case 3: manifest has a self path. This must override the # manifest_path kwarg. content = '''\ manifest: projects: - name: my-cool-project url: from-manifest-dir self: path: from-content ''' manifest = Manifest.from_data(source_data=yaml.safe_load(content), manifest_path='should-be-ignored', topdir=topdir) assert manifest.topdir == topdir mproj = manifest.projects[0] assert mproj.path == 'from-content' assert PurePath(mproj.abspath) == PurePath(str(tmpdir / 'from-content')) p1 = manifest.projects[1] assert p1.path == 'my-cool-project' assert PurePath(p1.abspath) == PurePath(str(tmpdir / 'my-cool-project')) # Case 4: project has a path. content = '''\ manifest: projects: - name: my-cool-project url: from-manifest-dir path: project-path self: path: manifest-path ''' manifest = Manifest.from_data(source_data=yaml.safe_load(content), manifest_path='should-be-ignored', topdir=topdir) assert manifest.topdir == topdir mproj = manifest.projects[0] assert mproj.path == 'manifest-path' assert PurePath(mproj.abspath) == PurePath(str(tmpdir / 'manifest-path')) p1 = manifest.projects[1] assert p1.path == 'project-path' assert PurePath(p1.abspath) == PurePath(str(tmpdir / 'project-path'))
def test_repo_path(west_topdir): # a project's fetch URL may be specified by combining a remote and # repo-path. this overrides the default use of the project's name # as the repo-path. # default remote + repo-path content = '''\ manifest: defaults: remote: remote1 remotes: - name: remote1 url-base: https://example.com projects: - name: testproject repo-path: some/path ''' manifest = Manifest.from_data(yaml.safe_load(content)) assert manifest.projects[1].url == 'https://example.com/some/path' # non-default remote + repo-path content = '''\ manifest: defaults: remote: remote1 remotes: - name: remote1 url-base: https://url1.com - name: remote2 url-base: https://url2.com projects: - name: testproject remote: remote2 repo-path: path ''' manifest = Manifest.from_data(yaml.safe_load(content)) assert manifest.projects[1].url == 'https://url2.com/path' # same project checked out under two different names content = '''\ manifest: defaults: remote: remote1 remotes: - name: remote1 url-base: https://url1.com projects: - name: testproject_v1 revision: v1.0 repo-path: testproject - name: testproject_v2 revision: v2.0 repo-path: testproject ''' manifest = Manifest.from_data(yaml.safe_load(content)) p1, p2 = manifest.projects[1:] r = Remote('remote1', 'https://url1.com') assert p1.url == 'https://url1.com/testproject' assert p1.url == p2.url expected1 = Project('testproject_v1', defaults=None, path='testproject_v1', clone_depth=None, revision='v1.0', west_commands=None, remote=r, repo_path='testproject', url=None) expected2 = Project('testproject_v2', defaults=None, path='testproject_v2', clone_depth=None, revision='v2.0', west_commands=None, remote=r, repo_path='testproject', url=None) deep_eq_check(p1, expected1) deep_eq_check(p2, expected2)