[Buildroot] [next v2 6/7] testing/tests/download: add test for sha1 as git ref
Ricardo Martincoski
ricardo.martincoski at gmail.com
Sat Aug 26 22:20:55 UTC 2017
From: Ricardo Martincoski <ricardo.martincoski at datacom.ind.br>
Add a test to download the package with git method and its version set
to a sha1 reachable by a branch name, but not pointed by it.
This is the most common use case for git refs in the tree.
Besides the test case Python script, add:
- a br2-external (git-refs) with a fake package (foo) as a fixture for
this and upcoming tests;
- support in GitTestBase for repos created on the fly;
- a new class GitRepo to create and manipulate repos. It is a class and
not a simple module because it will become very useful when testing
submodules.
After the download, compare the tarball to a checkout of the original
remote repo.
Cc: Arnout Vandecappelle <arnout at mind.be>
Signed-off-by: Ricardo Martincoski <ricardo.martincoski at datacom.ind.br>
---
Changes v1 -> v2:
- rename the main test file to testgit (Arnout). Actually I broke it
down to a common gitbase and the two test_git_refs and test_git_hash
(this last one is new, in the previous patch);
- remove some weird/wrong TODO I had in v1 code (Arnout);
- use the same structure for test cases as used in the test infra;
- raise an exception when the download fails (Arnout). I did not add
code for this since I let the builder class to raise it;
- I reimplemented git_util as gitrepo. Arnout suggested to use it as
module and I first implemented that way locally, but then I created
test cases for submodules and I realized now I have a reason to use
a class (see next patch);
- move package (now called foo) to a BR2_EXTERNAL (Arnout);
- override BR2_DL_DIR when calling make (Arnout);
- the test infra uses O= so I don't need to;
- instead of removing the files in the dl/ folder, use a different dir
as dl/ dir since now each repo has a different name;
- open the tarball to check its contents (Arnout). I create a fresh
clone of the repo before the test to compare against;
- this patch is not checking for the actual sha1 anymore, it can be
done later. But the check for the contents should cover most cases;
- my old argument to not test only the support/download/dl-wrapper is
not valid anymore since I mimic the logic from the scripts to know
the name of the tarball. But I still think calling make ...-source
has better maintenance. And also the previous patch (test for hash
of packages with git method) can use the same base class;
- this patch is part of series 1/3 of a new version of
http://patchwork.ozlabs.org/patch/690097/
---
.gitlab-ci.yml | 1 +
.../tests/download/br2-external/git-refs/Config.in | 0
.../download/br2-external/git-refs/external.desc | 1 +
.../download/br2-external/git-refs/external.mk | 1 +
.../br2-external/git-refs/package/foo/foo.mk | 13 +++
support/testing/tests/download/gitbase.py | 13 +++
support/testing/tests/download/gitrepo.py | 82 ++++++++++++++++++
support/testing/tests/download/test_git_refs.py | 96 ++++++++++++++++++++++
8 files changed, 207 insertions(+)
create mode 100644 support/testing/tests/download/br2-external/git-refs/Config.in
create mode 100644 support/testing/tests/download/br2-external/git-refs/external.desc
create mode 100644 support/testing/tests/download/br2-external/git-refs/external.mk
create mode 100644 support/testing/tests/download/br2-external/git-refs/package/foo/foo.mk
create mode 100644 support/testing/tests/download/gitrepo.py
create mode 100644 support/testing/tests/download/test_git_refs.py
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 98022b6d7f..6d64a2c0b6 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -222,6 +222,7 @@ tests.core.test_timezone.TestNoTimezone: *runtime_test
tests.download.test_git_hash.TestGitBadHash: *runtime_test
tests.download.test_git_hash.TestGitGoodHash: *runtime_test
tests.download.test_git_hash.TestGitNoHash: *runtime_test
+tests.download.test_git_refs.TestGitSha1InsideBranch: *runtime_test
tests.fs.test_ext.TestExt2: *runtime_test
tests.fs.test_ext.TestExt2r1: *runtime_test
tests.fs.test_ext.TestExt3: *runtime_test
diff --git a/support/testing/tests/download/br2-external/git-refs/Config.in b/support/testing/tests/download/br2-external/git-refs/Config.in
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/support/testing/tests/download/br2-external/git-refs/external.desc b/support/testing/tests/download/br2-external/git-refs/external.desc
new file mode 100644
index 0000000000..69f40d46c6
--- /dev/null
+++ b/support/testing/tests/download/br2-external/git-refs/external.desc
@@ -0,0 +1 @@
+name: GIT_REFS
diff --git a/support/testing/tests/download/br2-external/git-refs/external.mk b/support/testing/tests/download/br2-external/git-refs/external.mk
new file mode 100644
index 0000000000..de1aad635f
--- /dev/null
+++ b/support/testing/tests/download/br2-external/git-refs/external.mk
@@ -0,0 +1 @@
+include $(sort $(wildcard $(BR2_EXTERNAL_GIT_REFS_PATH)/package/*/*.mk))
diff --git a/support/testing/tests/download/br2-external/git-refs/package/foo/foo.mk b/support/testing/tests/download/br2-external/git-refs/package/foo/foo.mk
new file mode 100644
index 0000000000..01c926e177
--- /dev/null
+++ b/support/testing/tests/download/br2-external/git-refs/package/foo/foo.mk
@@ -0,0 +1,13 @@
+################################################################################
+#
+# foo
+#
+################################################################################
+
+# Get all the data from the test infra
+FOO_VERSION ?= 0
+GITREMOTE_PORT_NUMBER ?= 9418
+GITREMOTE_REPO ?= repo.git
+FOO_SITE = git://localhost:$(GITREMOTE_PORT_NUMBER)/$(GITREMOTE_REPO)
+
+$(eval $(generic-package))
diff --git a/support/testing/tests/download/gitbase.py b/support/testing/tests/download/gitbase.py
index b259914c77..b70e6400bc 100644
--- a/support/testing/tests/download/gitbase.py
+++ b/support/testing/tests/download/gitbase.py
@@ -1,6 +1,9 @@
+import os
+
import infra.basetest
from infra.builder import Builder
from gitremote import GitRemote
+from gitrepo import GitRepo
class GitTestBase(infra.basetest.BRTest):
@@ -9,6 +12,7 @@ class GitTestBase(infra.basetest.BRTest):
BR2_BACKUP_SITE=""
"""
br2_external = None
+ repo = None
serveddir = None
gitremote = None
logfile = None
@@ -27,6 +31,15 @@ class GitTestBase(infra.basetest.BRTest):
# time a test runs
self.b.build(["dependencies"])
+ # for the case we are dinamically creating repos
+ if self.serveddir is None:
+ # place the repo in the images/ dir so it appears as an artifact
+ # when the test runs in gitlab infra
+ self.serveddir = os.path.join(self.builddir, "images")
+ if not os.path.exists(self.serveddir): # for run-tests -k
+ os.mkdir(self.serveddir)
+ self.repo = GitRepo(self.builddir, self.serveddir, self.logtofile)
+
self.gitremote = GitRemote(self.builddir, self.serveddir,
self.logtofile)
# send output from the test to the logfile created by GitRemote
diff --git a/support/testing/tests/download/gitrepo.py b/support/testing/tests/download/gitrepo.py
new file mode 100644
index 0000000000..47bdba1b7c
--- /dev/null
+++ b/support/testing/tests/download/gitrepo.py
@@ -0,0 +1,82 @@
+import os
+import subprocess
+import tempfile
+
+import infra
+
+
+class GitRepo(object):
+
+ def __init__(self, builddir, serveddir, logtofile):
+ self.logfile = infra.open_log_file(builddir, "run", logtofile)
+ self.remotedir = tempfile.mkdtemp(dir=serveddir)
+
+ # Run a git command in the emulated remote repo
+ def _git(self, git_cmd):
+ cmd = ["git"] + git_cmd
+ self.logfile.write("> Running command '{}' @ '{}'\n"
+ .format(" ".join(cmd), self.remotedir))
+ self.logfile.flush()
+ r = subprocess.call(cmd, stdout=self.logfile, stderr=self.logfile,
+ cwd=self.remotedir)
+ if r != 0:
+ raise SystemError("'{}' returned {}".format(" ".join(cmd), r))
+
+ def _sha1(self, ref="HEAD"):
+ cmd = ["git", "rev-parse", ref]
+ out = subprocess.check_output(cmd, stderr=self.logfile,
+ cwd=self.remotedir)
+ sha1 = out.strip()
+ self.logfile.write("> {}\n".format(sha1))
+ return sha1
+
+ # Initialize a repo with an initial commit. Git by default gives us the
+ # master branch pointing to this commit. Leave it there by detaching the
+ # HEAD before creating any more commits. It will become handy before we
+ # start the actual download test, since we can checkout this branch to move
+ # HEAD away from the ref to be tested, thus avoiding false OK in the case
+ # the HEAD can be checked out but the ref doesn't.
+ def git_init(self):
+ self._git(["init"])
+ # Ensure git know us before commit (needed to run in the docker image)
+ self._git(["config", "user.email", "'you at example.com'"])
+ self._git(["config", "user.name", "'Your Name'"])
+ self.git_commit()
+ self.git_checkout("HEAD~0")
+ return os.path.basename(self.remotedir)
+
+ # Create a commit with random data (actually a randomly named empty file).
+ # This way the tarball can be checked by comparing the contents of a clone
+ # of the repo against the contents of the tarball.
+ # Use the name of the new file as the commit message so when the git tree
+ # is dumped to the logfile, it displays useful info.
+ def git_commit(self):
+ _, f = tempfile.mkstemp(dir=self.remotedir)
+ self._git(["add", os.path.basename(f)])
+ self._git(["commit", "-m", os.path.basename(f)])
+ return self._sha1()
+
+ def git_branch(self, branch):
+ self._git(["branch", branch])
+ return self._sha1()
+
+ def git_checkout(self, ref):
+ self._git(["checkout", "-f", ref])
+ return self._sha1()
+
+ # Create a fresh clone of the repo to be compared to the tarball.
+ # For simplicity, create this copy inside the repo itself, so all go away
+ # when the test ends (when run-tests is called without -k).
+ def create_a_copy_to_compare(self):
+ self._git(["clone", self.remotedir, "copy"])
+ return os.path.join(self.remotedir, "copy")
+
+ # Move HEAD of emulated remote away from desired commit to avoid false OK
+ # when the reference under test cannot be fetched but HEAD can.
+ def move_head_away_from_ref_under_test(self):
+ self.git_checkout("master")
+
+ # For debug purposes, dump to the log a nice ascii art of all commits in
+ # the repo, including the short sha1, commit title and the refs.
+ def save_git_tree_to_log(self):
+ self._git(["log", "--all", "--oneline", "--graph", "--decorate"])
diff --git a/support/testing/tests/download/test_git_refs.py b/support/testing/tests/download/test_git_refs.py
new file mode 100644
index 0000000000..2f0709d703
--- /dev/null
+++ b/support/testing/tests/download/test_git_refs.py
@@ -0,0 +1,96 @@
+import os
+import tarfile
+
+import infra
+from gitbase import GitTestBase
+
+
+def files_from_tarball(tarball):
+ filelist = None
+ with tarfile.open(tarball) as tf:
+ filelist = tf.getnames()
+ path_inside_tarball = os.path.basename(tarball).split('.')[0]
+ files = [os.path.relpath(f, start=path_inside_tarball) for f in filelist]
+ return sorted(files)
+
+
+def files_from_checkout(checkout):
+ filelist = []
+ for dirpath, dirnames, filenames in os.walk(checkout):
+ if '.git' in dirnames:
+ dirnames.remove('.git')
+ filelist += [os.path.join(dirpath, f) for f in filenames]
+ files = [os.path.relpath(f, start=checkout) for f in filelist]
+ return sorted(files)
+
+
+def compare_tarball_with_checkout(tarball, checkout, logfile):
+ t_files = files_from_tarball(tarball)
+ c_files = files_from_checkout(checkout)
+ only_in_t = [f for f in t_files if f not in c_files]
+ only_in_c = [f for f in c_files if f not in t_files]
+ common = [f for f in t_files if f in c_files]
+ logfile.write("> Comparing resulting tarball with a checkout of the repo\n"
+ "> only in tarball: " + " ".join(only_in_t) + "\n"
+ "> missing from tarball: " + " ".join(only_in_c) + "\n"
+ "> common files: " + " ".join(common) + "\n")
+ return len(only_in_t + only_in_c)
+
+
+class TestGitRefs(GitTestBase):
+ br2_external = infra.filepath("tests/download/br2-external/git-refs")
+
+ # Common preparations of the repo before the actual test
+ def _prepare_the_repo(self, ref):
+ # create a checkout from current commit to compare the generated
+ # tarball against it later
+ self.repo.git_checkout(ref)
+ self.checkoutdir = self.repo.create_a_copy_to_compare()
+ self.repo.move_head_away_from_ref_under_test()
+ # save debug info about the git repos
+ self.repo.save_git_tree_to_log()
+
+ # Download the sources from the emulated remote
+ def _download_ref(self, ref, remotedir):
+ self.dldir = os.path.join(self.builddir, "dl",
+ os.path.basename(remotedir))
+ env = {"BR2_DL_DIR": self.dldir,
+ "FOO_VERSION": ref,
+ "GITREMOTE_REPO": remotedir,
+ "GITREMOTE_PORT_NUMBER": str(self.gitremote.port)}
+ # download the sources
+ self.b.build(["foo-dirclean", "foo-source"], env, self.logfile)
+
+ # Check the tarball files against a clone of the emulated remote repo
+ def _check_tarball(self, ref):
+ tarball = os.path.join(self.dldir,
+ "foo-{}.tar.gz".format(ref.replace('/', '_')))
+ ret = compare_tarball_with_checkout(tarball, self.checkoutdir,
+ self.logfile)
+ self.assertEqual(ret, 0, "downloaded source does not match")
+
+ # Download the sources and check
+ #
+ # ref: the git reference to be tested (tag, branch, sha1, special ref).
+ # e.g. "mybranch"
+ #
+ # remotedir: local path to the repo exported by git server.
+ # usually self.repo.git_init()
+ #
+ def check_download(self, ref, remotedir):
+ self._prepare_the_repo(ref)
+ self._download_ref(ref, remotedir)
+ self._check_tarball(ref)
+
+
+class TestGitSha1InsideBranch(TestGitRefs):
+ # Repo layout under test:
+ # * sha1_3 (mybranch)
+ # * sha1_2<<<
+ # * sha1_1 (HEAD -> master)
+ def test_run(self):
+ remotedir = self.repo.git_init()
+ ref = self.repo.git_commit()
+ self.repo.git_commit()
+ self.repo.git_branch("mybranch")
+ self.check_download(ref, remotedir)
--
2.13.0
More information about the buildroot
mailing list