diff options
author | Trevor Gamblin <tgamblin@baylibre.com> | 2023-10-16 15:44:57 -0400 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2023-10-17 11:06:15 +0100 |
commit | cf318c3c05fc050b8c838c04f28797325c569c5c (patch) | |
tree | eae2cb9c15ee1715af721f0256965aacf548dc16 /scripts | |
parent | 499cdad7a16f6cc256837069c7add294132127a4 (diff) | |
download | openembedded-core-contrib-cf318c3c05fc050b8c838c04f28797325c569c5c.tar.gz |
patchtest: add scripts to oe-core
Add the following from the patchtest repo:
- patchtest: core patch testing tool
- patchtest-get-branch: determine the target branch of a patch
- patchtest-get-series: pull patch series from Patchwork
- patchtest-send-results: send test results to selected mailing list
- patchtest-setup-sharedir: create sharedir for use with patchtest guest
mode
- patchtest.README: instructions for using patchtest based on the README
in the original repository
Note that the patchtest script was modified slightly from the repo
version to retain compatibility with the oe-core changes.
patchtest-send-results and patchtest-setup-sharedir are also primarily
intended for automated testing in guest mode, but are added for
consistency.
Signed-off-by: Trevor Gamblin <tgamblin@baylibre.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'scripts')
-rwxr-xr-x | scripts/patchtest | 233 | ||||
-rwxr-xr-x | scripts/patchtest-get-branch | 92 | ||||
-rwxr-xr-x | scripts/patchtest-get-series | 125 | ||||
-rwxr-xr-x | scripts/patchtest-send-results | 93 | ||||
-rwxr-xr-x | scripts/patchtest-setup-sharedir | 95 | ||||
-rw-r--r-- | scripts/patchtest.README | 152 |
6 files changed, 790 insertions, 0 deletions
diff --git a/scripts/patchtest b/scripts/patchtest new file mode 100755 index 0000000000..9525a2be17 --- /dev/null +++ b/scripts/patchtest @@ -0,0 +1,233 @@ +#!/usr/bin/env python3 +# ex:ts=4:sw=4:sts=4:et +# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- +# +# patchtest: execute all unittest test cases discovered for a single patch +# +# Copyright (C) 2016 Intel Corporation +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Author: Leo Sandoval <leonardo.sandoval.gonzalez@linux.intel.com> +# + +import sys +import os +import unittest +import fileinput +import logging +import traceback +import json + +# Include current path so test cases can see it +sys.path.insert(0, os.path.dirname(os.path.realpath(__file__))) + +# Include patchtest library +sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)), '../meta/lib/patchtest')) + +from data import PatchTestInput +from repo import PatchTestRepo + +import utils +logger = utils.logger_create('patchtest') +info = logger.info +error = logger.error + +import repo + +def getResult(patch, mergepatch, logfile=None): + + class PatchTestResult(unittest.TextTestResult): + """ Patchtest TextTestResult """ + shouldStop = True + longMessage = False + + success = 'PASS' + fail = 'FAIL' + skip = 'SKIP' + + def startTestRun(self): + # let's create the repo already, it can be used later on + repoargs = { + 'repodir': PatchTestInput.repodir, + 'commit' : PatchTestInput.basecommit, + 'branch' : PatchTestInput.basebranch, + 'patch' : patch, + } + + self.repo_error = False + self.test_error = False + self.test_failure = False + + try: + self.repo = PatchTestInput.repo = PatchTestRepo(**repoargs) + except: + logger.error(traceback.print_exc()) + self.repo_error = True + self.stop() + return + + if mergepatch: + self.repo.merge() + + def addError(self, test, err): + self.test_error = True + (ty, va, trace) = err + logger.error(traceback.print_exc()) + + def addFailure(self, test, err): + test_description = test.id().split('.')[-1].replace('_', ' ').replace("cve", "CVE").replace("signed off by", + "Signed-off-by").replace("upstream status", + "Upstream-Status").replace("non auh", + "non-AUH").replace("presence format", "presence") + self.test_failure = True + fail_str = '{}: {}: {} ({})'.format(self.fail, + test_description, json.loads(str(err[1]))["issue"], + test.id()) + print(fail_str) + if logfile: + with open(logfile, "a") as f: + f.write(fail_str + "\n") + + def addSuccess(self, test): + test_description = test.id().split('.')[-1].replace('_', ' ').replace("cve", "CVE").replace("signed off by", + "Signed-off-by").replace("upstream status", + "Upstream-Status").replace("non auh", + "non-AUH").replace("presence format", "presence") + success_str = '{}: {} ({})'.format(self.success, + test_description, test.id()) + print(success_str) + if logfile: + with open(logfile, "a") as f: + f.write(success_str + "\n") + + def addSkip(self, test, reason): + test_description = test.id().split('.')[-1].replace('_', ' ').replace("cve", "CVE").replace("signed off by", + "Signed-off-by").replace("upstream status", + "Upstream-Status").replace("non auh", + "non-AUH").replace("presence format", "presence") + skip_str = '{}: {}: {} ({})'.format(self.skip, + test_description, json.loads(str(reason))["issue"], + test.id()) + print(skip_str) + if logfile: + with open(logfile, "a") as f: + f.write(skip_str + "\n") + + def stopTestRun(self): + + # in case there was an error on repo object creation, just return + if self.repo_error: + return + + self.repo.clean() + + return PatchTestResult + +def _runner(resultklass, prefix=None): + # load test with the corresponding prefix + loader = unittest.TestLoader() + if prefix: + loader.testMethodPrefix = prefix + + # create the suite with discovered tests and the corresponding runner + suite = loader.discover(start_dir=PatchTestInput.startdir, pattern=PatchTestInput.pattern, top_level_dir=PatchTestInput.topdir) + ntc = suite.countTestCases() + + # if there are no test cases, just quit + if not ntc: + return 2 + runner = unittest.TextTestRunner(resultclass=resultklass, verbosity=0) + + try: + result = runner.run(suite) + except: + logger.error(traceback.print_exc()) + logger.error('patchtest: something went wrong') + return 1 + + return 0 + +def run(patch, logfile=None): + """ Load, setup and run pre and post-merge tests """ + # Get the result class and install the control-c handler + unittest.installHandler() + + # run pre-merge tests, meaning those methods with 'pretest' as prefix + premerge_resultklass = getResult(patch, False, logfile) + premerge_result = _runner(premerge_resultklass, 'pretest') + + # run post-merge tests, meaning those methods with 'test' as prefix + postmerge_resultklass = getResult(patch, True, logfile) + postmerge_result = _runner(postmerge_resultklass, 'test') + + if premerge_result == 2 and postmerge_result == 2: + logger.error('patchtest: any test cases found - did you specify the correct suite directory?') + + return premerge_result or postmerge_result + +def main(): + tmp_patch = False + patch_path = PatchTestInput.patch_path + log_results = PatchTestInput.log_results + log_path = None + patch_list = None + + if os.path.isdir(patch_path): + patch_list = [os.path.join(patch_path, filename) for filename in os.listdir(patch_path)] + else: + patch_list = [patch_path] + + for patch in patch_list: + if os.path.getsize(patch) == 0: + logger.error('patchtest: patch is empty') + return 1 + + logger.info('Testing patch %s' % patch) + + if log_results: + log_path = patch + ".testresult" + with open(log_path, "a") as f: + f.write("Patchtest results for patch '%s':\n\n" % patch) + + try: + if log_path: + run(patch, log_path) + else: + run(patch) + finally: + if tmp_patch: + os.remove(patch) + +if __name__ == '__main__': + ret = 1 + + # Parse the command line arguments and store it on the PatchTestInput namespace + PatchTestInput.set_namespace() + + # set debugging level + if PatchTestInput.debug: + logger.setLevel(logging.DEBUG) + + # if topdir not define, default it to startdir + if not PatchTestInput.topdir: + PatchTestInput.topdir = PatchTestInput.startdir + + try: + ret = main() + except Exception: + import traceback + traceback.print_exc(5) + + sys.exit(ret) diff --git a/scripts/patchtest-get-branch b/scripts/patchtest-get-branch new file mode 100755 index 0000000000..9415de98ef --- /dev/null +++ b/scripts/patchtest-get-branch @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 + +# Get target branch from the corresponding mbox +# +# NOTE: this script was based on patches coming to the openembedded-core +# where target branch is defined inside brackets as subject prefix +# i.e. [master], [rocko], etc. +# +# Copyright (C) 2016 Intel Corporation +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +import mailbox +import argparse +import re +import git +import sys + +re_prefix = re.compile("(\[.*\])", re.DOTALL) + +def get_branch(filepath_repo, filepath_mbox, default_branch): + branch = None + + # get all remotes branches + gitbranches = git.Git(filepath_repo).branch('-a').splitlines() + + # from gitbranches, just get the names + branches = [b.split('/')[-1] for b in gitbranches] + + subject = ' '.join(mailbox.mbox(filepath_mbox)[0]['subject'].splitlines()) + + # we expect that patches will have somewhere between one and three + # consecutive sets of square brackets with tokens inside, e.g.: + # 1. [PATCH] + # 2. [OE-core][PATCH] + # 3. [OE-core][kirkstone][PATCH] + # Some of them may also be part of a series, in which case the PATCH + # token will be formatted like: + # [PATCH 1/4] + # or they will be revisions to previous patches, where it will be: + # [PATCH v2] + # Or they may contain both: + # [PATCH v2 3/4] + # In any case, we want mprefix to contain all of these tokens so + # that we can search for branch names within them. + mprefix = re.findall(r'\[.*?\]', subject) + found_branch = None + if mprefix: + # Iterate over the tokens and compare against the branch list to + # figure out which one the patch is targeting + for token in mprefix: + stripped = token.lower().strip('[]') + if default_branch in stripped: + found_branch = default_branch + break + else: + for branch in branches: + # ignore branches named "core" + if branch != "core" and stripped.rfind(branch) != -1: + found_branch = token.split(' ')[0].strip('[]') + break + + # if there's no mprefix content or no known branches were found in + # the tokens, assume the target is master + if found_branch is None: + found_branch = "master" + + return (subject, found_branch) + +if __name__ == '__main__': + + parser = argparse.ArgumentParser() + parser.add_argument('repo', metavar='REPO', help='Main repository') + parser.add_argument('mbox', metavar='MBOX', help='mbox filename') + parser.add_argument('--default-branch', metavar='DEFAULT_BRANCH', default='master', help='Use this branch if no one is found') + parser.add_argument('--separator', '-s', metavar='SEPARATOR', default=' ', help='Char separator for output data') + args = parser.parse_args() + + subject, branch = get_branch(args.repo, args.mbox, args.default_branch) + print("branch: %s" % branch) + diff --git a/scripts/patchtest-get-series b/scripts/patchtest-get-series new file mode 100755 index 0000000000..773701f80b --- /dev/null +++ b/scripts/patchtest-get-series @@ -0,0 +1,125 @@ +#!/bin/bash -e +# +# get-latest-series: Download latest patch series from Patchwork +# +# Copyright (C) 2023 BayLibre Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +# the interval into the past which we want to check for new series, in minutes +INTERVAL_MINUTES=30 + +# Maximum number of series to retrieve. the Patchwork API can support up to 250 +# at once +SERIES_LIMIT=250 + +# Location to save patches +DOWNLOAD_PATH="." + +# Name of the file to use/check as a log of previously-tested series IDs +SERIES_TEST_LOG=".series_test.log" + +# Patchwork project to pull series patches from +PROJECT="oe-core" + +# The Patchwork server to pull from +SERVER="https://patchwork.yoctoproject.org/api/1.2/" + +help() +{ + echo "Usage: get-latest-series [ -i | --interval MINUTES ] + [ -d | --directory DIRECTORY ] + [ -l | --limit COUNT ] + [ -h | --help ] + [ -t | --tested-series LOGFILE] + [ -p | --project PROJECT ] + [ -s | --server SERVER ]" + exit 2 +} + +while [ "$1" != "" ]; do + case $1 in + -i|--interval) + INTERVAL_MINUTES=$2 + shift 2 + ;; + -l|--limit) + SERIES_LIMIT=$2 + shift 2 + ;; + -d|--directory) + DOWNLOAD_PATH=$2 + shift 2 + ;; + -p|--project) + PROJECT=$2 + shift 2 + ;; + -s|--server) + SERVER=$2 + shift 2 + ;; + -t|--tested-series) + SERIES_TEST_LOG=$2 + shift 2 + ;; + -h|--help) + help + ;; + *) + echo "Unknown option $1" + help + ;; + esac +done + +# The time this script is running at +START_TIME=$(date --date "now" +"%Y-%m-%dT%H:%M:%S") + +# the corresponding timestamp we want to check against for new patch series +SERIES_CHECK_LIMIT=$(date --date "now - ${INTERVAL_MINUTES} minutes" +"%Y-%m-%dT%H:%M:%S") + +echo "Start time is $START_TIME" +echo "Series check limit is $SERIES_CHECK_LIMIT" + +# Create DOWNLOAD_PATH if it doesn't exist +if [ ! -d "$DOWNLOAD_PATH" ]; then + mkdir "${DOWNLOAD_PATH}" +fi + +# Create SERIES_TEST_LOG if it doesn't exist +if [ ! -f "$SERIES_TEST_LOG" ]; then + touch "${SERIES_TEST_LOG}" +fi + +# Retrieve a list of series IDs from the 'git-pw series list' output. The API +# supports a maximum of 250 results, so make sure we allow that when required +SERIES_LIST=$(git-pw --project "${PROJECT}" --server "${SERVER}" series list --since "${SERIES_CHECK_LIMIT}" --limit "${SERIES_LIMIT}" | awk '{print $2}' | xargs | sed -e 's/[^0-9 ]//g') + +if [ -z "$SERIES_LIST" ]; then + echo "No new series for project ${PROJECT} since ${SERIES_CHECK_LIMIT}" + exit 0 +fi + +# Check each series ID +for SERIES in $SERIES_LIST; do + # Download the series only if it's not found in the SERIES_TEST_LOG + if ! grep -w --quiet "${SERIES}" "${SERIES_TEST_LOG}"; then + echo "Downloading $SERIES..." + git-pw series download --separate "${SERIES}" "${DOWNLOAD_PATH}" + echo "${SERIES}" >> "${SERIES_TEST_LOG}" + else + echo "Already tested ${SERIES}. Skipping..." + fi +done diff --git a/scripts/patchtest-send-results b/scripts/patchtest-send-results new file mode 100755 index 0000000000..2a2c57a10e --- /dev/null +++ b/scripts/patchtest-send-results @@ -0,0 +1,93 @@ +#!/usr/bin/env python3 +# ex:ts=4:sw=4:sts=4:et +# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- +# +# patchtest: execute all unittest test cases discovered for a single patch +# Note that this script is currently under development and has been +# hard-coded with default values for testing purposes. This script +# should not be used without changing the default recipient, at minimum. +# +# Copyright (C) 2023 BayLibre Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Author: Trevor Gamblin <tgamblin@baylibre.com> +# + +import argparse +import boto3 +import configparser +import mailbox +import os +import sys + +greeting = """Thank you for your submission. Patchtest identified one +or more issues with the patch. Please see the log below for +more information:\n\n---\n""" + +suggestions = """\n---\n\nPlease address the issues identified and +submit a new revision of the patch, or alternatively, reply to this +email with an explanation of why the patch format should be accepted. +Note that patchtest may report failures in the merge-on-head test for +patches that are part of a series if they rely on changes from +preceeding entries. + +If you believe these results are due to an error in patchtest, please +submit a bug at https://bugzilla.yoctoproject.org/ (use the 'Patchtest' +category under 'Yocto Project Subprojects'). Thank you!""" + +parser = argparse.ArgumentParser(description="Send patchtest results to a submitter for a given patch") +parser.add_argument("-p", "--patch", dest="patch", required=True, help="The patch file to summarize") +args = parser.parse_args() + +if not os.path.exists(args.patch): + print(f"Patch '{args.patch}' not found - did you provide the right path?") + sys.exit(1) +elif not os.path.exists(args.patch + ".testresult"): + print(f"Found patch '{args.patch}' but '{args.patch}.testresult' was not present. Have you run patchtest on the patch?") + sys.exit(1) + +result_file = args.patch + ".testresult" +result_basename = os.path.basename(args.patch) +testresult = None + +with open(result_file, "r") as f: + testresult = f.read() + +reply_contents = greeting + testresult + suggestions +subject_line = f"Patchtest results for {result_basename}" + +if "FAIL" in testresult: + ses_client = boto3.client('ses', region_name='us-west-2') + response = ses_client.send_email( + Source='patchtest@automation.yoctoproject.org', + Destination={ + 'ToAddresses': ['test-list@lists.yoctoproject.org'], + }, + ReplyToAddresses=['test-list@lists.yoctoproject.org'], + Message={ + 'Subject': { + 'Data': subject_line, + 'Charset': 'utf-8' + }, + 'Body': { + 'Text': { + 'Data': reply_contents, + 'Charset': 'utf-8' + } + } + } + ) +else: + print(f"No failures identified for {args.patch}.") diff --git a/scripts/patchtest-setup-sharedir b/scripts/patchtest-setup-sharedir new file mode 100755 index 0000000000..a1497987cb --- /dev/null +++ b/scripts/patchtest-setup-sharedir @@ -0,0 +1,95 @@ +#!/bin/bash -e +# +# patchtest-setup-sharedir: Setup a directory for storing mboxes and +# repositories to be shared with the guest machine, including updates to +# the repos if the directory already exists +# +# Copyright (C) 2023 BayLibre Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Author: Trevor Gamblin <tgamblin@baylibre.com> + +# poky repository +POKY_REPO="https://git.yoctoproject.org/poky" + +# patchtest repository +PATCHTEST_REPO="https://git.yoctoproject.org/patchtest" + +# the name of the directory +SHAREDIR="patchtest_share" + +help() +{ + echo "Usage: patchtest-setup-sharedir [ -d | --directory SHAREDIR ] + [ -p | --patchtest PATCHTEST_REPO ] + [ -y | --poky POKY_REPO ]" + exit 2 +} + +while [ "$1" != "" ]; do + case $1 in + -d|--directory) + SHAREDIR=$2 + shift 2 + ;; + -p|--patchtest) + PATCHTEST_REPO=$2 + shift 2 + ;; + -y|--poky) + POKY_REPO=$2 + shift 2 + ;; + -h|--help) + help + ;; + *) + echo "Unknown option $1" + help + ;; + esac +done + +# define MBOX_DIR where the patch series will be stored by +# get-latest-series +MBOX_DIR="${SHAREDIR}/mboxes" + +# Create SHAREDIR if it doesn't exist +if [ ! -d "$SHAREDIR" ]; then + mkdir -p "${SHAREDIR}" + echo "Created ${SHAREDIR}" +fi + +# Create the mboxes directory if it doesn't exist +if [ ! -d "$MBOX_DIR" ]; then + mkdir -p "${MBOX_DIR}" + echo "Created ${MBOX_DIR}" +fi + +# clone poky if it's not already present; otherwise, update it +if [ ! -d "$POKY_REPO" ]; then + BASENAME=$(basename ${POKY_REPO}) + git clone "${POKY_REPO}" "${SHAREDIR}/${BASENAME}" +else + (cd "${SHAREDIR}/$BASENAME" && git pull) +fi + +# clone patchtest if it's not already present; otherwise, update it +if [ ! -d "$PATCHTEST_REPO" ]; then + BASENAME=$(basename ${PATCHTEST_REPO}) + git clone "${PATCHTEST_REPO}" "${SHAREDIR}/${BASENAME}" +else + (cd "${SHAREDIR}/$BASENAME" && git pull) +fi diff --git a/scripts/patchtest.README b/scripts/patchtest.README new file mode 100644 index 0000000000..689d513df5 --- /dev/null +++ b/scripts/patchtest.README @@ -0,0 +1,152 @@ +# Patchtest + +## Introduction + +Patchtest is a test framework for community patches based on the standard +unittest python module. As input, it needs tree elements to work properly: +a patch in mbox format (either created with `git format-patch` or fetched +from 'patchwork'), a test suite and a target repository. + +The first test suite intended to be used with patchtest is found in the +openembedded-core repository [1] targeted for patches that get into the +openembedded-core mailing list [2]. This suite is also intended as a +baseline for development of similar suites for other layers as needed. + +Patchtest can either run on a host or a guest machine, depending on which +environment the execution needs to be done. If you plan to test your own patches +(a good practice before these are sent to the mailing list), the easiest way is +to install and execute on your local host; in the other hand, if automatic +testing is intended, the guest method is strongly recommended. The guest +method requires the use of the patchtest layer, in addition to the tools +available in oe-core: https://git.yoctoproject.org/patchtest/ + +## Installation + +As a tool for use with the Yocto Project, the [quick start guide](https://docs.yoctoproject.org/brief-yoctoprojectqs/index.html) +contains the necessary prerequisites for a basic project. In addition, +patchtest relies on the following Python modules: + +- boto3 (for sending automated results emails only) +- git-pw>=2.5.0 +- jinja2 +- pylint +- pyparsing>=3.0.9 +- unidiff + +These can be installed by running `pip install -r +meta/lib/patchtest/requirements.txt`. Note that git-pw is not +automatically added to the user's PATH; by default, it is installed at +~/.local/bin/git-pw. + +For git-pw (and therefore scripts such as patchtest-get--series) to work, you need +to provide a Patchwork instance in your user's .gitconfig, like so (the project +can be specified using the --project argument): + + git config --global pw.server "https://patchwork.yoctoproject.org/api/1.2/" + +To work with patchtest, you should have the following repositories cloned: + +1. https://git.openembedded.org/openembedded-core/ (or https://git.yoctoproject.org/poky/) +2. https://git.openembedded.org/bitbake/ (if not using poky) +3. https://git.yoctoproject.org/patchtest (if using guest mode) + +## Usage + +### Obtaining Patches + +Patch files can be obtained directly from cloned repositories using `git +format-patch -N` (where N is the number of patches starting from HEAD to +generate). git-pw can also be used with filters for users, patch/series IDs, +and timeboxes if specific patches are desired. For more information, see the +git-pw [documentation](https://patchwork.readthedocs.io/projects/git-pw/en/latest/). + +Alternatively, `scripts/patchtest-get-series` can be used to pull mbox files from +the Patchwork instance configured previously in .gitconfig. It uses a log file +called ".series_test.log" to store and compare series IDs so that the same +versions of a patch are not tested multiple times unintentionally. By default, +it will pull up to five patch series from the last 30 minutes using oe-core as +the target project, but these parameters can be configured using the `--limit`, +`--interval`, and `--project` arguments respectively. For more information, run +`patchtest-get-series -h`. + +### Host Mode + +To run patchtest on the host, do the following: + +1. In openembedded-core/poky, do `source oe-init-build-env` +2. Generate patch files from the target repository by doing `git-format patch -N`, + where N is the number of patches starting at HEAD, or by using git-pw + or patchtest-get-series +3. Run patchtest on a patch file by doing the following: + + patchtest --patch /path/to/patch/file /path/to/target/repo /path/to/tests/directory + + or, if you have stored the patch files in a directory, do: + + patchtest --directory /path/to/patch/directory /path/to/target/repo /path/to/tests/directory + + For example, to test `master-gcc-Fix--fstack-protector-issue-on-aarch64.patch` against the oe-core test suite: + + patchtest --patch master-gcc-Fix--fstack-protector-issue-on-aarch64.patch /path/to/openembedded-core /path/to/openembedded-core/meta/lib/patchtest/tests + +### Guest Mode + +Patchtest's guest mode has been refactored to more closely mirror the +typical Yocto Project image build workflow, but there are still some key +differences to keep in mind. The primary objective is to provide a level +of isolation from the host when testing patches pulled automatically +from the mailing lists. When executed this way, the test process is +essentially running random code from the internet and could be +catastrophic if malicious bits or even poorly-handled edge cases aren't +protected against. In order to use this mode, the +https://git.yoctoproject.org/patchtest/ repository must be cloned and +the meta-patchtest layer added to bblayers.conf. + +The general flow of guest mode is: + +1. Run patchtest-setup-sharedir --directory <dirname> to create a + directory for mounting +2. Collect patches via patchtest-get-series (or other manual step) into the + <dirname>/mboxes path +3. Ensure that a user with ID 1200 has appropriate read/write + permissions to <dirname> and <dirname>/mboxes, so that the + "patchtest" user in the core-image-patchtest image can function +4. Build the core-image-patchtest image +5. Run the core-image-patchtest image with the mounted sharedir, like + so: + `runqemu kvm nographic qemuparams="-snapshot -fsdev + local,id=test_mount,path=/workspace/yocto/poky/build/patchtestdir,security_model=mapped + -device virtio-9p-pci,fsdev=test_mount,mount_tag=test_mount -smp 4 -m + 2048"` + +Patchtest runs as an initscript for the core-image-patchtest image and +shuts down after completion, so there is no input required from a user +during operation. Unlike in host mode, the guest is designed to +automatically generate test result files, in the same directory as the +targeted patch files but with .testresult as an extension. These contain +the entire output of the patchtest run for each respective pass, +including the PASS, FAIL, and SKIP indicators for each test run. + +## Contributing + +The yocto mailing list (yocto@lists.yoctoproject.org) is used for questions, +comments and patch review. It is subscriber only, so please register before +posting. + +Send pull requests to yocto@lists.yoctoproject.org with '[patchtest]' in the +subject. + +When sending single patches, please use something like: + + git send-email -M -1 --to=yocto@lists.yoctoproject.org --subject-prefix=patchtest][PATCH + +## Maintenance +----------- + +Maintainers: + Trevor Gamblin <tgamblin@baylibre.com> + +## Links +----- +[1] https://git.openembedded.org/openembedded-core/ +[2] https://www.yoctoproject.org/community/mailing-lists/ |