Files
meson/unittests/subprojectscommandtests.py
Dylan Baker e991c4d454 Use SPDX-License-Identifier consistently
This replaces all of the Apache blurbs at the start of each file with an
`# SPDX-License-Identifier: Apache-2.0` string. It also fixes existing
uses to be consistent in capitalization, and to be placed above any
copyright notices.

This removes nearly 3000 lines of boilerplate from the project (only
python files), which no developer cares to look at.

SPDX is in common use, particularly in the Linux kernel, and is the
recommended format for Meson's own `project(license: )` field
2023-12-13 15:19:21 -05:00

301 lines
14 KiB
Python

# SPDX-License-Identifier: Apache-2.0
# Copyright 2016-2021 The Meson development team
import subprocess
import tempfile
import textwrap
import os
from pathlib import Path
import typing as T
from mesonbuild.mesonlib import (
version_compare, git, search_version
)
from .baseplatformtests import BasePlatformTests
from .helpers import *
class SubprojectsCommandTests(BasePlatformTests):
def setUp(self):
super().setUp()
self.root_dir = Path(self.builddir)
self.project_dir = self.root_dir / 'src'
self._create_project(self.project_dir)
self.subprojects_dir = self.project_dir / 'subprojects'
os.makedirs(str(self.subprojects_dir))
self.packagecache_dir = self.subprojects_dir / 'packagecache'
os.makedirs(str(self.packagecache_dir))
def _create_project(self, path, project_name='dummy'):
os.makedirs(str(path), exist_ok=True)
with open(str(path / 'meson.build'), 'w', encoding='utf-8') as f:
f.write(f"project('{project_name}')")
def _git(self, cmd, workdir):
return git(cmd, str(workdir), check=True)[1].strip()
def _git_config(self, workdir):
self._git(['config', 'user.name', 'Meson Test'], workdir)
self._git(['config', 'user.email', 'meson.test@example.com'], workdir)
def _git_remote(self, cmd, name):
return self._git(cmd, self.root_dir / name)
def _git_local(self, cmd, name):
return self._git(cmd, self.subprojects_dir / name)
def _git_local_branch(self, name):
# Same as `git branch --show-current` but compatible with older git version
branch = self._git_local(['rev-parse', '--abbrev-ref', 'HEAD'], name)
return branch if branch != 'HEAD' else ''
def _git_local_commit(self, name, ref='HEAD'):
return self._git_local(['rev-parse', ref], name)
def _git_remote_commit(self, name, ref='HEAD'):
return self._git_remote(['rev-parse', ref], name)
def _git_create_repo(self, path):
# If a user has git configuration init.defaultBranch set we want to override that
with tempfile.TemporaryDirectory() as d:
out = git(['--version'], str(d))[1]
if version_compare(search_version(out), '>= 2.28'):
extra_cmd = ['--initial-branch', 'master']
else:
extra_cmd = []
self._create_project(path)
self._git(['init'] + extra_cmd, path)
self._git_config(path)
self._git(['add', '.'], path)
self._git(['commit', '--no-gpg-sign', '-m', 'Initial commit'], path)
def _git_create_remote_repo(self, name):
self._git_create_repo(self.root_dir / name)
def _git_create_local_repo(self, name):
self._git_create_repo(self.subprojects_dir / name)
def _git_create_remote_commit(self, name, branch):
self._git_remote(['checkout', branch], name)
self._git_remote(['commit', '--no-gpg-sign', '--allow-empty', '-m', f'initial {branch} commit'], name)
def _git_create_remote_branch(self, name, branch):
self._git_remote(['checkout', '-b', branch], name)
self._git_remote(['commit', '--no-gpg-sign', '--allow-empty', '-m', f'initial {branch} commit'], name)
def _git_create_remote_tag(self, name, tag):
self._git_remote(['commit', '--no-gpg-sign', '--allow-empty', '-m', f'tag {tag} commit'], name)
self._git_remote(['tag', '--no-sign', tag], name)
def _wrap_create_git(self, name, revision='master', depth=None):
path = self.root_dir / name
with open(str((self.subprojects_dir / name).with_suffix('.wrap')), 'w', encoding='utf-8') as f:
if depth is None:
depth_line = ''
else:
depth_line = 'depth = {}'.format(depth)
f.write(textwrap.dedent(
'''
[wrap-git]
url={}
revision={}
{}
'''.format(os.path.abspath(str(path)), revision, depth_line)))
def _wrap_create_file(self, name, tarball='dummy.tar.gz'):
path = self.root_dir / tarball
with open(str((self.subprojects_dir / name).with_suffix('.wrap')), 'w', encoding='utf-8') as f:
f.write(textwrap.dedent(
f'''
[wrap-file]
source_url={os.path.abspath(str(path))}
source_filename={tarball}
'''))
Path(self.packagecache_dir / tarball).touch()
def _subprojects_cmd(self, args):
return self._run(self.meson_command + ['subprojects'] + args, workdir=str(self.project_dir))
def test_git_update(self):
subp_name = 'sub1'
# Create a fake remote git repository and a wrap file. Checks that
# "meson subprojects download" works.
self._git_create_remote_repo(subp_name)
self._wrap_create_git(subp_name)
self._subprojects_cmd(['download'])
self.assertPathExists(str(self.subprojects_dir / subp_name))
self._git_config(self.subprojects_dir / subp_name)
# Create a new remote branch and update the wrap file. Checks that
# "meson subprojects update --reset" checkout the new branch.
self._git_create_remote_branch(subp_name, 'newbranch')
self._wrap_create_git(subp_name, 'newbranch')
self._subprojects_cmd(['update', '--reset'])
self.assertEqual(self._git_local_branch(subp_name), 'newbranch')
self.assertEqual(self._git_local_commit(subp_name), self._git_remote_commit(subp_name, 'newbranch'))
# Update remote newbranch. Checks the new commit is pulled into existing
# local newbranch. Make sure it does not print spurious 'git stash' message.
self._git_create_remote_commit(subp_name, 'newbranch')
out = self._subprojects_cmd(['update', '--reset'])
self.assertNotIn('No local changes to save', out)
self.assertEqual(self._git_local_branch(subp_name), 'newbranch')
self.assertEqual(self._git_local_commit(subp_name), self._git_remote_commit(subp_name, 'newbranch'))
# Update remote newbranch and switch to another branch. Checks that it
# switch current branch to newbranch and pull latest commit.
self._git_local(['checkout', 'master'], subp_name)
self._git_create_remote_commit(subp_name, 'newbranch')
self._subprojects_cmd(['update', '--reset'])
self.assertEqual(self._git_local_branch(subp_name), 'newbranch')
self.assertEqual(self._git_local_commit(subp_name), self._git_remote_commit(subp_name, 'newbranch'))
# Stage some local changes then update. Checks that local changes got
# stashed.
self._create_project(self.subprojects_dir / subp_name, 'new_project_name')
self._git_local(['add', '.'], subp_name)
self._git_create_remote_commit(subp_name, 'newbranch')
self._subprojects_cmd(['update', '--reset'])
self.assertEqual(self._git_local_branch(subp_name), 'newbranch')
self.assertEqual(self._git_local_commit(subp_name), self._git_remote_commit(subp_name, 'newbranch'))
self.assertTrue(self._git_local(['stash', 'list'], subp_name))
# Untracked files need to be stashed too, or (re-)applying a patch
# creating one of those untracked files will fail.
untracked = self.subprojects_dir / subp_name / 'untracked.c'
untracked.write_bytes(b'int main(void) { return 0; }')
self._subprojects_cmd(['update', '--reset'])
self.assertTrue(self._git_local(['stash', 'list'], subp_name))
assert not untracked.exists()
# Ensure it was indeed stashed, and we can get it back.
self.assertTrue(self._git_local(['stash', 'pop'], subp_name))
assert untracked.exists()
# Create a new remote tag and update the wrap file. Checks that
# "meson subprojects update --reset" checkout the new tag in detached mode.
self._git_create_remote_tag(subp_name, 'newtag')
self._wrap_create_git(subp_name, 'newtag')
self._subprojects_cmd(['update', '--reset'])
self.assertEqual(self._git_local_branch(subp_name), '')
self.assertEqual(self._git_local_commit(subp_name), self._git_remote_commit(subp_name, 'newtag'))
# Create a new remote commit and update the wrap file with the commit id.
# Checks that "meson subprojects update --reset" checkout the new commit
# in detached mode.
self._git_local(['checkout', 'master'], subp_name)
self._git_create_remote_commit(subp_name, 'newbranch')
new_commit = self._git_remote(['rev-parse', 'HEAD'], subp_name)
self._wrap_create_git(subp_name, new_commit)
self._subprojects_cmd(['update', '--reset'])
self.assertEqual(self._git_local_branch(subp_name), '')
self.assertEqual(self._git_local_commit(subp_name), new_commit)
# Create a local project not in a git repository, then update it with
# a git wrap. Without --reset it should print error message and return
# failure. With --reset it should delete existing project and clone the
# new project.
subp_name = 'sub2'
self._create_project(self.subprojects_dir / subp_name)
self._git_create_remote_repo(subp_name)
self._wrap_create_git(subp_name)
with self.assertRaises(subprocess.CalledProcessError) as cm:
self._subprojects_cmd(['update'])
self.assertIn('Not a git repository', cm.exception.output)
self._subprojects_cmd(['update', '--reset'])
self.assertEqual(self._git_local_commit(subp_name), self._git_remote_commit(subp_name))
# Create a fake remote git repository and a wrap file targeting
# HEAD and depth = 1. Checks that "meson subprojects download" works.
subp_name = 'sub3'
self._git_create_remote_repo(subp_name)
self._wrap_create_git(subp_name, revision='head', depth='1')
self._subprojects_cmd(['download'])
self.assertPathExists(str(self.subprojects_dir / subp_name))
self._git_config(self.subprojects_dir / subp_name)
@skipIfNoExecutable('true')
def test_foreach(self):
self._create_project(self.subprojects_dir / 'sub_file')
self._wrap_create_file('sub_file')
self._git_create_local_repo('sub_git')
self._wrap_create_git('sub_git')
self._git_create_local_repo('sub_git_no_wrap')
def ran_in(s):
ret = []
prefix = 'Executing command in '
for l in s.splitlines():
if l.startswith(prefix):
ret.append(l[len(prefix):])
return sorted(ret)
dummy_cmd = ['true']
out = self._subprojects_cmd(['foreach'] + dummy_cmd)
self.assertEqual(ran_in(out), sorted(['subprojects/sub_file', 'subprojects/sub_git', 'subprojects/sub_git_no_wrap']))
out = self._subprojects_cmd(['foreach', '--types', 'git,file'] + dummy_cmd)
self.assertEqual(ran_in(out), sorted(['subprojects/sub_file', 'subprojects/sub_git']))
out = self._subprojects_cmd(['foreach', '--types', 'file'] + dummy_cmd)
self.assertEqual(ran_in(out), ['subprojects/sub_file'])
out = self._subprojects_cmd(['foreach', '--types', 'git'] + dummy_cmd)
self.assertEqual(ran_in(out), ['subprojects/sub_git'])
def test_purge(self):
self._create_project(self.subprojects_dir / 'sub_file')
self._wrap_create_file('sub_file')
self._git_create_local_repo('sub_git')
self._wrap_create_git('sub_git')
sub_file_subprojects_dir = self.subprojects_dir / 'sub_file' / 'subprojects'
sub_file_subprojects_dir.mkdir(exist_ok=True, parents=True)
real_dir = Path('sub_file') / 'subprojects' / 'real'
self._wrap_create_file(real_dir, tarball='dummy2.tar.gz')
with open(str((self.subprojects_dir / 'redirect').with_suffix('.wrap')), 'w', encoding='utf-8') as f:
f.write(textwrap.dedent(
f'''
[wrap-redirect]
filename = {real_dir}.wrap
'''))
def deleting(s: str) -> T.List[str]:
ret = []
prefix = 'Deleting '
for l in s.splitlines():
if l.startswith(prefix):
ret.append(l[len(prefix):])
return sorted(ret)
out = self._subprojects_cmd(['purge'])
self.assertEqual(deleting(out), sorted([
str(self.subprojects_dir / 'redirect.wrap'),
str(self.subprojects_dir / 'sub_file'),
str(self.subprojects_dir / 'sub_git'),
]))
out = self._subprojects_cmd(['purge', '--include-cache'])
self.assertEqual(deleting(out), sorted([
str(self.subprojects_dir / 'sub_git'),
str(self.subprojects_dir / 'redirect.wrap'),
str(self.subprojects_dir / 'packagecache' / 'dummy.tar.gz'),
str(self.subprojects_dir / 'packagecache' / 'dummy2.tar.gz'),
str(self.subprojects_dir / 'sub_file'),
]))
out = self._subprojects_cmd(['purge', '--include-cache', '--confirm'])
self.assertEqual(deleting(out), sorted([
str(self.subprojects_dir / 'sub_git'),
str(self.subprojects_dir / 'redirect.wrap'),
str(self.subprojects_dir / 'packagecache' / 'dummy.tar.gz'),
str(self.subprojects_dir / 'packagecache' / 'dummy2.tar.gz'),
str(self.subprojects_dir / 'sub_file'),
]))
self.assertFalse(Path(self.subprojects_dir / 'packagecache' / 'dummy.tar.gz').exists())
self.assertFalse(Path(self.subprojects_dir / 'sub_file').exists())
self.assertFalse(Path(self.subprojects_dir / 'sub_git').exists())
self.assertFalse(Path(self.subprojects_dir / 'redirect.wrap').exists())