<!---
  Licensed to the Apache Software Foundation (ASF) under one
  or more contributor license agreements.  See the NOTICE file
  distributed with this work for additional information
  regarding copyright ownership.  The ASF licenses this file
  to you under the Apache License, Version 2.0 (the
  "License"); you may not use this file except in compliance
  with the License.  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing,
  software distributed under the License is distributed on an
  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  KIND, either express or implied.  See the License for the
  specific language governing permissions and limitations
  under the License.
-->

<h1 id="test-patch">test-patch</h1>

<ul>
  <li><a href="#purpose">Purpose</a></li>
  <li><a href="#pre-requisites">Pre-requisites</a></li>
  <li><a href="#basic-usage">Basic Usage</a></li>
  <li><a href="#automation">Automation</a></li>
  <li><a href="#build-tool">Build Tool</a></li>
  <li><a href="#providing-patch-files">Providing Patch Files</a></li>
  <li><a href="#project-specific-capabilities">Project-Specific Capabilities</a></li>
  <li><a href="#multijdk">MultiJDK</a></li>
  <li><a href="#docker">Docker</a></li>
  <li><a href="#in-closing">In Closing</a></li>
</ul>

<h1 id="purpose">Purpose</h1>

<p>As part of Apache Hadoop's commit process, all patches to the source base go through a precommit test that does some (relatively) light checking to make sure the proposed change does not break unit tests and/or passes some other prerequisites such as code formatting guidelines.  This is meant as a preliminary check for committers so that the basic patch is in a known state and for contributors to know if they have followed the project's guidelines.  This check, called test-patch, along with a helper program, called smart-apply-patch, may also be used by individual developers to verify a patch prior to sending to the Apache Hadoop QA systems.</p>

<p>Other projects have adopted a similar methodology after seeing great success in the Apache Hadoop model.  Some have even gone as far as forking Apache Hadoop's precommit code and modifying it to meet their project's needs.</p>

<p>One of the key facets of Apache Yetus is to bring together all of these forks under a common code base to help software development<br />
as a whole.</p>

<h1 id="pre-requisites">Pre-requisites</h1>

<p>test-patch and smart-apply-patch are written in bash for maximum portability.  As such, it mostly assumes the locations of commands to be in the file path. However, in many cases, this assumption may be overridden via command line options.</p>

<p>For Solaris and Solaris-like operating systems, the default location for the POSIX binaries is in /usr/xpg4/bin and the default location for the GNU binaries is /usr/gnu/bin.</p>

<h2 id="base-requirements">Base Requirements</h2>

<p>test-patch requires these installed components to execute:</p>

<ul>
  <li>git-based project (and git 1.7.3 or higher installed)</li>
  <li>bash v3.2 or higher (bash v4.0 or higher is recommended)</li>
  <li>GNU diff</li>
  <li>GNU patch</li>
  <li>POSIX awk</li>
  <li>POSIX grep</li>
  <li>POSIX sed</li>
  <li><a href="http://curl.haxx.se/">curl</a> command</li>
  <li>file command</li>
</ul>

<h2 id="optional-requirements">Optional Requirements</h2>

<p>Features are plug-in based and enabled either individually or collectively on the command line. From there, these are activated based upon tool availability, the languages being tested, etc.  The external dependencies of plug-ins may have different licensing requirements than Apache Yetus.</p>

<p>Bug Systems:</p>

<ul>
  <li><a href="https://github.com/">GitHub</a>-based issue tracking</li>
  <li><a href="https://www.atlassian.com/software/jira">JIRA</a>-based issue tracking</li>
  <li><a href="https://www.bugzilla.org/">Bugzilla</a>-based issue tracking (Read Only)</li>
</ul>

<p>Build Tools:</p>

<ul>
  <li><a href="https://ant.apache.org">ant</a></li>
  <li><a href="https://www.gnu.org/software/autoconf/autoconf.html">autoconf</a></li>
  <li><a href="https://www.cmake.org">cmake</a></li>
  <li><a href="https://www.gradle.org">gradle</a></li>
  <li>make</li>
  <li><a href="https://maven.apache.org">maven</a></li>
</ul>

<p>Automation and Isolation:</p>

<ul>
  <li><a href="https://www.docker.com">Docker</a> version 1.6.0+</li>
  <li><a href="https://www.jenkins-ci.org">Jenkins</a></li>
</ul>

<p>Unit Test Formats:</p>

<ul>
  <li><a href="https://cmake.org/Wiki/CMake/Testing_With_CTest">ctest</a></li>
  <li><a href="http://junit.org/">JUnit</a></li>
  <li><a href="https://testanything.org/">TAP</a></li>
</ul>

<p>Language Support, Licensing, and more:</p>

<ul>
  <li><a href="http://creadur.apache.org/rat/">Apache Creadur Rat</a> entries in build system</li>
  <li><a href="http://checkstyle.sourceforge.net/">checkstyle</a> entries in build system (ant and maven only)</li>
  <li><a href="http://findbugs.sourceforge.net/">FindBugs</a> entries in build system and 3.x executables</li>
  <li><a href="http://perlcritic.com/">Perl::Critic</a> installed</li>
  <li><a href="http://www.pylint.org/">pylint</a> installed</li>
  <li><a href="http://batsov.com/rubocop/">rubocop</a> installed</li>
  <li><a href="https://github.com/YorickPeterse/ruby-lint">ruby-lint</a> installed</li>
  <li><a href="https://github.com/koalaman/shellcheck">shellcheck</a> installed, preferably 0.3.6 or higher</li>
</ul>

<h1 id="basic-usage">Basic Usage</h1>

<p>The first step for a successful deployment is determining which features/plug-ins to enable:</p>

<div class="highlight"><pre class="highlight shell"><code><span class="nv">$ </span>test-patch.sh <span class="nt">--list-plugins</span>
</code></pre></div>
<p>This option will list all of the available plug-ins that are installed in the default location.  From this list, the specific plug-ins can be enabled:</p>

<div class="highlight"><pre class="highlight shell"><code><span class="nv">$ </span>test-patch.sh <span class="nt">--plugins</span><span class="o">=</span><span class="s2">"ant,maven,shellcheck,xml"</span> &lt;other options&gt;
</code></pre></div>
<p>As a short-cut, every plug-in may be enabled via the special 'all' type:</p>

<div class="highlight"><pre class="highlight shell"><code><span class="nv">$ </span>test-patch.sh <span class="nt">--plugins</span><span class="o">=</span><span class="s2">"all"</span> &lt;other options&gt;
</code></pre></div>
<p><code>--plugins</code> also allows some basic "arithmetic":</p>

<div class="highlight"><pre class="highlight shell"><code><span class="nv">$ </span>test-patch.sh <span class="nt">--plugins</span><span class="o">=</span><span class="s2">"all,-checkstyle,-findbugs"</span> &lt;other options&gt;
</code></pre></div>
<p>This will enable all plug-ins for potential usage, except for checkstyle and findbugs.</p>

<p><strong>NOTE: The examples in this section will assume that the necessary <code>--plugins</code> option has been set on the command line as appropriate for your particular installation.</strong></p>

<p>This command will execute basic patch testing against a patch file stored in "filename":</p>

<div class="highlight"><pre class="highlight shell"><code><span class="nv">$ </span><span class="nb">cd</span> &lt;your repo&gt;
<span class="nv">$ </span>test-patch.sh <span class="nt">--dirty-workspace</span> <span class="nt">--project</span><span class="o">=</span>projectname &lt;filename&gt;
</code></pre></div>
<p>The <code>--dirty-workspace</code> flag tells test-patch that the repository is not clean and it is ok to continue.  By default, unit tests are not run since they may take a significant amount of time.</p>

<p>To do turn them on, we need to provide the –run-tests option:</p>

<div class="highlight"><pre class="highlight shell"><code><span class="nv">$ </span><span class="nb">cd</span> &lt;your repo&gt;
<span class="nv">$ </span>test-patch.sh <span class="nt">--dirty-workspace</span> <span class="nt">--run-tests</span> &lt;filename&gt;
</code></pre></div>
<p>This is the same command, but now runs the unit tests.</p>

<p>A typical configuration is to have two repositories.  One with the code you are working on and another, clean repository.  This means you can:</p>

<div class="highlight"><pre class="highlight shell"><code><span class="nv">$ </span><span class="nb">cd</span> &lt;workrepo&gt;
<span class="nv">$ </span>git diff master <span class="o">&gt;</span> /tmp/patchfile
<span class="nv">$ </span><span class="nb">cd</span> ../&lt;testrepo&gt;
<span class="nv">$ </span>test-patch.sh <span class="nt">--basedir</span><span class="o">=</span>&lt;testrepo&gt; <span class="nt">--resetrepo</span> /tmp/patchfile
</code></pre></div>
<p>We used two new options here.  –basedir sets the location of the repository to use for testing.  –resetrepo tells test patch that it can go into <strong>destructive</strong> mode.  Destructive mode will wipe out any changes made to that repository, so use it with care!</p>

<h1 id="fork-bomb-protection">Fork Bomb Protection</h1>

<p>By default, test-patch.sh will set the user soft limit (ulimit -Su) to a relatively low 1,000 processes (and, on some operating systems with some languages such as Java, threads!). This is to prevent errant processes from eating up all system resources.  If this limit is too low, it may be necessary to use the <code>--proclimit</code> option.  For example:</p>

<div class="highlight"><pre class="highlight shell"><code><span class="nv">$ </span>test-patch <span class="nt">--proclimit</span><span class="o">=</span>10000
</code></pre></div>
<p>… will set it to be 10,000 processes.</p>

<p>NOTE: The actual implementation of this feature is dependent upon the version of Bash.  For bash v4 and higher (most operating systems), the fork bomb protection is generally only used for the build and QA tools.  This means Apache Yetus should continue to function. For earlier versions of bash (e.g., OS X), the limit is applied to all of test-patch. If the limit is hit, Apache Yetus will itself likely crash.</p>

<h1 id="automation">Automation</h1>

<p>After the tests have run, there is a directory that contains all of the test-patch related artifacts.  This is generally referred to as the patchprocess directory.  By default, test-patch tries to make something off of /tmp to contain this content.  Using the <code>--patch-dir</code> option, one can specify exactly which directory to use.  This is helpful for automated precommit testing so that Jenkins or other automated workflow system knows where to look to gather up the output.</p>

<p>For example:</p>

<div class="highlight"><pre class="highlight shell"><code><span class="nv">$ </span>test-patch.sh <span class="nt">--robot</span> <span class="nt">--patch-dir</span><span class="o">=</span><span class="k">${</span><span class="nv">WORKSPACE</span><span class="k">}</span>/patchprocess <span class="nt">--basedir</span><span class="o">=</span><span class="k">${</span><span class="nv">WORKSPACE</span><span class="k">}</span>/source <span class="k">${</span><span class="nv">WORKSPACE</span><span class="k">}</span>/patchfile
</code></pre></div>
<p>… will trigger test-patch to run in fully automated mode, using ${WORKSPACE}/patchprocess as its scratch space, ${WORKSPACE}/source as the source repository, and ${WORKSPACE}/patchfile as the name of the patch to test against.  This will always run the unit tests, write answers back to bug systems, remove old, stopped/exited Docker containers after 24 hours and images after 1 week, forcibly use –resetrepo, and more.</p>

<p><strong>NOTE: Make sure to add the patch directory to <code>.gitignore</code> if the directory is inside the source tree to avoid deleting it, as <code>test-patch</code> does a <code>git clean</code> to remove untracked files from previous runs.</strong></p>

<p>The –build-url option is also useful when running in –robot mode so that emails and such<br />
have a location to look at the output artifacts:</p>

<div class="highlight"><pre class="highlight shell"><code><span class="nv">$ </span>test-patch.sh <span class="nt">--robot</span> <span class="nt">--build-url</span><span class="o">=</span>http://server.example.name:80/<span class="k">${</span><span class="nv">buildnumber</span><span class="k">}</span>/
</code></pre></div>
<p>Some plug-ins such as Maven have special handling if there are multiple executions of test-patch happening at once.  It is very common when using automation systems to have multiple runs on the same host. In order to assist these plug-ins, an instance identifier may be provided:</p>

<div class="highlight"><pre class="highlight shell"><code><span class="nv">$ </span>test-patch.sh <span class="nt">--robot</span> <span class="nt">--instance</span><span class="o">=</span>1
</code></pre></div>
<p>If –robot is specified without an instance, a random number is generated and used.</p>

<p>There is some special handling if Jenkins is actually your automation tool.  Instead of using –robot, use –jenkins:</p>

<div class="highlight"><pre class="highlight shell"><code><span class="nv">$ </span>test-patch.sh <span class="nt">--jenkins</span> <span class="nt">--patch-dir</span><span class="o">=</span><span class="k">${</span><span class="nv">WORKSPACE</span><span class="k">}</span>/patchprocess <span class="nt">--basedir</span><span class="o">=</span><span class="k">${</span><span class="nv">WORKSPACE</span><span class="k">}</span>/source <span class="k">${</span><span class="nv">WORKSPACE</span><span class="k">}</span>/patchfile
</code></pre></div>
<p>This will enable –robot, set the –build-url option from the ${BUILD_URL} environment variable, and the instance identifier is set to the ${EXECUTOR_NUMBER}.</p>

<p>If stuck containers are a problem, a more aggressive robot may be enabled with the –sentinel option.  This option enables killing containers that have been running for over 24 hours as well.</p>

<h1 id="build-tool">Build Tool</h1>

<p>Out of the box, test-patch is built to use maven.  But what if the project is built using something else, such as ant?</p>

<div class="highlight"><pre class="highlight shell"><code><span class="nv">$ </span>test-patch.sh <span class="o">(</span>other options<span class="o">)</span> <span class="nt">--build-tool</span><span class="o">=</span>ant
</code></pre></div>
<p>will tell test-patch to use ant instead of maven to drive the project.</p>

<h1 id="providing-patch-files">Providing Patch Files</h1>

<h2 id="jira">JIRA</h2>

<p>It is a fairly common practice within the Apache community to use Apache's JIRA instance to store potential patches.  As a result, test-patch supports providing just a JIRA issue number.  test-patch will find the <em>last</em> attachment, download it, then process it.</p>

<p><strong>NOTE: test-patch expects the patch files to follow a particular naming convention. For complete details<br />
 on the naming convention please refer to <a href="../precommit-patchnames/">patch-naming-conventions</a></strong></p>

<p>For example:</p>

<div class="highlight"><pre class="highlight shell"><code><span class="nv">$ </span>test-patch.sh <span class="o">(</span>other options<span class="o">)</span> HADOOP-9905
</code></pre></div>
<p>… will process the patch file associated with this JIRA issue.</p>

<p>If the Apache JIRA system is not in use, then override options may be provided on the command line to point to a different JIRA instance.</p>

<div class="highlight"><pre class="highlight shell"><code><span class="nv">$ </span>test-patch.sh <span class="nt">--jira-issue-re</span><span class="o">=</span><span class="s1">'^PROJECT-[0-9]+$'</span> <span class="nt">--jira-base-url</span><span class="o">=</span><span class="s1">'https://example.com/jira'</span> PROJECT-90
</code></pre></div>
<p>… will process the patch file attached to PROJECT-90 on the JIRA instance located on the example.com server.</p>

<h2 id="github">GITHUB</h2>

<p>test-patch has some basic support for Github.  test-patch supports many forms of providing pull requests to work on:</p>

<div class="highlight"><pre class="highlight shell"><code><span class="nv">$ </span>test-patch.sh <span class="nt">--github-repo</span><span class="o">=</span>apache/pig GH:99
</code></pre></div>
<p>or</p>

<div class="highlight"><pre class="highlight shell"><code><span class="nv">$ </span>test-patch.sh https://github.com/apache/pig/pulls/99
</code></pre></div>
<p>or</p>

<div class="highlight"><pre class="highlight shell"><code><span class="nv">$ </span>test-patch.sh https://github.com/apache/pig/pulls/99.patch
</code></pre></div>
<p>… will process PR #99 on the apache/pig repo.</p>

<h2 id="generic-urls">Generic URLs</h2>

<p>Luckily, test-patch supports ways to provide unified diffs via URLs.</p>

<p>For example:</p>

<div class="highlight"><pre class="highlight shell"><code><span class="nv">$ </span>test-patch.sh <span class="o">(</span>other options<span class="o">)</span> https://example.com/webserver/file.patch
</code></pre></div>
<p>… will download and process the file.patch from the example.com webserver.</p>

<h1 id="project-specific-capabilities">Project-specific Capabilities</h1>

<p>Due to the extensible nature of the system, test-patch allows for projects to define project-specific rules which we call personalities.  (How to build those rules is covered elsewhere.) There are two ways to specify which personality to use:</p>

<h2 id="direct-method">Direct Method</h2>

<div class="highlight"><pre class="highlight shell"><code><span class="nv">$ </span>test-patch.sh <span class="o">(</span>other options<span class="o">)</span> <span class="nt">--personality</span><span class="o">=(</span>filename<span class="o">)</span>
</code></pre></div>
<p>This tells test-patch to use the personality in the given file.</p>

<h2 id="project-method">Project Method</h2>

<p>However, test-patch can detect if it is a personality that is in its "personality" directory based upon the project name:</p>

<div class="highlight"><pre class="highlight shell"><code><span class="nv">$ </span>test-patch.sh <span class="o">(</span>other options<span class="o">)</span> <span class="nt">--project</span><span class="o">=(</span>project<span class="o">)</span>
</code></pre></div>
<h1 id="multijdk">MultiJDK</h1>

<p>For many projects, it is useful to test Java code against multiple versions of JDKs at the same time.  test-patch can do this with the –multijdkdirs option:</p>

<div class="highlight"><pre class="highlight shell"><code><span class="nv">$ </span>test-patch.sh <span class="o">(</span>other options<span class="o">)</span> <span class="nt">--multijdkdirs</span><span class="o">=</span><span class="s2">"/j/d/k/1,/j/d/k/2"</span>
</code></pre></div>
<p>Not all Java tests support this mode, but those that do will now run their tests with all of the given versions of Java consecutively (e.g., javac–the Java compliation test).  Tests that do not support MultiJDK mode (e.g., checkstyle, mvn install) will use JAVA_HOME.</p>

<p>NOTE: JAVA_HOME is always appended to the list of JDKs in MultiJDK mode.  If JAVA_HOME is in the list, it will be moved to the end.</p>

<h1 id="docker">Docker</h1>

<p>test-patch also has a mode to utilize Docker:</p>

<div class="highlight"><pre class="highlight shell"><code><span class="nv">$ </span>test-patch.sh <span class="o">(</span>other options<span class="o">)</span> <span class="nt">--docker</span>
</code></pre></div>
<p>This will do some preliminary setup and then re-execute itself inside a Docker container.  For more information on how to provide a custom Dockerfile and other Docker-specific features, see the advanced guide.</p>

<h1 id="in-closing">In Closing</h1>

<p>test-patch has many other features and command line options for the basic user.  Many of these are self-explanatory.  To see the list of options, run test-patch.sh without any options or with –help.</p>
