1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2024-12-12 10:04:14 +02:00

Document how to contribute to pgBackRest.

There's a lot more to be done here, but this is a good start.
This commit is contained in:
Cynthia Shang 2019-10-08 15:27:17 -04:00 committed by David Steele
parent a4152a0ea1
commit 38b72eded4
8 changed files with 522 additions and 5 deletions

View File

@ -6,6 +6,42 @@
Indentation is four spaces -- no tabs. Only file types that absolutely require tabs (e.g. `Makefile`) may use them.
### Line Length
With the exception of documentation code, no line of any code or test file shall exceed 132 characters. If a line break is required, then it shall be after the first function parenthesis:
```
// CORRECT - location of line break after first function parenthesis if line length is greater than 132
StringList *removeList = infoBackupDataLabelList(
infoBackup, strNewFmt("^%s.*", strPtr(strLstGet(currentBackupList, fullIdx))));
// INCORRECT
StringList *removeList = infoBackupDataLabelList(infoBackup, strNewFmt("^%s.*", strPtr(strLstGet(currentBackupList,
fullIdx))));
```
If a conditional, then after a completed conditional, for example:
```
// CORRECT - location of line break after a completed conditional if line length is greater than 132
if (archiveInfoPgHistory.id != backupInfoPgHistory.id ||
archiveInfoPgHistory.systemId != backupInfoPgHistory.systemId ||
archiveInfoPgHistory.version != backupInfoPgHistory.version)
// INCORRECT
if (archiveInfoPgHistory.id != backupInfoPgHistory.id || archiveInfoPgHistory.systemId !=
backupInfoPgHistory.systemId || archiveInfoPgHistory.version != backupInfoPgHistory.version)
```
### Inline Comment
Inline comments shall start at character 69 and must not exceed the line length of 132. For example:
```
typedef struct InlineCommentExample
{
const String *comment; // Inline comment example
const String *longComment; // Inline comment example that exceeds 132 characters should
// then go to next line but this should be avoided
} InlineCommentExample;
```
### Naming
#### Variables

194
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,194 @@
# pgBackRest <br/> Contributing to pgBackRest
## Introduction
This documentation is intended to assist contributors to pgBackRest by outlining some basic steps and guidelines for contributing to the project. Coding standards to follow are defined in [CODING.md](https://github.com/pgbackrest/pgbackrest/blob/master/CODING.md). At a minimum, unit tests must be written and run and the documentation generated before submitting a Pull Request; see the [Testing](#testing) section below for details.
## Adding an Option
Options can be added to a command or multiple commands. Options can be configuration file only, command-line only or valid for both. Once an option is added, `config.auto.*`, `define.auto.*` and `parse.auto.*` files will automatically be generated by the build system.
To add an option, two files need be to be modified:
- `build/lib/pgBackRestBuild/Config/Data.pm`
- `doc/xml/reference.xml`
These files are discussed in the following sections.
### Data.pm
There is a detailed comment at the top of this file on the configuration definitions which one can refer to in determining how to define the rules for the option.
#### Command Line Only Options
Command-line only options are options where `CFGDEF_SECTION` rule is not defined. There are two sections to be updated when adding a command-line only option, each of which is marked by the comment `Command-line only options`.
- **Section 1:** Find the first section with the `Command-line only options` comment. This section defines and exports the constant for the actual option.
- **Section 2:** Find the second section with the `Command-line only options` comment. This is where the rules for the option are defined.
The steps for how to update these sections are detailed below.
**Section 1**
Copy the two lines ("use constant"/"push") of an existing option and paste them where the option would be in alphabetical order and rename it to the same name as the new option name. For example CFGOPT_DRY_RUN, defined as "dry-run".
**Section 2**
To better explain this section, `CFGOPT_ONLINE` will be used as an example:
```
&CFGOPT_ONLINE =>
{
&CFGDEF_TYPE => CFGDEF_TYPE_BOOLEAN,
&CFGDEF_NEGATE => true,
&CFGDEF_DEFAULT => true,
&CFGDEF_COMMAND =>
{
&CFGCMD_BACKUP => {},
&CFGCMD_STANZA_CREATE => {},
&CFGCMD_STANZA_UPGRADE => {},
}
},
```
Note that `CFGDEF_SECTION` is not present thereby making this a command-line only option. Each line is explained below:
- `CFGOPT_ONLINE` - the name of the option as defined in **Section 1**
- `CFGDEF_TYPE` - the type of the option. Valid types are: `CFGDEF_TYPE_BOOLEAN`, `CFGDEF_TYPE_FLOAT`, `CFGDEF_TYPE_HASH`, `CFGDEF_TYPE_INTEGER`, `CFGDEF_TYPE_LIST`, `CFGDEF_TYPE_PATH`, `CFGDEF_TYPE_SIZE`, and `CFGDEF_TYPE_STRING`
- `CFGDEF_NEGATE` - being a command-line only boolean option, this rule would automatically default to false so it must be defined if the option is negatable. Ask yourself if negation makes sense, for example, would a --dry-run option make sense as --no-dry-run? If the answer is no, then this rule can be omitted as it would automatically default to false. Any boolean option that cannot be negatable, must be a command-line only and not a configuration file option as all configuration boolean options must be negatable.
- `CFGDEF_DEFAULT` - sets a default for the option if the option is not provided when the command is run. The default can be global or it can be specified for a specific command in the `CFGDEF_COMMAND` section. For example, if it was desirable for the default to be false for the `CFGCMD_STANZA_CREATE` then CFGDEF_NEGATE => would be set to `true` in each command listed except for `CFGCMD_STANZA_CREATE` where it would be `false` and it would not be specified (as it is here) in the global section (meaning global for all commands listed).
- `CFGDEF_COMMAND` - list each command for which the option is valid. If a command is not listed, then the option is not valid for the command and an error will be thrown if it attempted to be used for that command.
### reference.xml
All options must be documented or the system will error during the build. To add an option, find the command section identified by `command id="COMMAND"` section where `COMMAND` is the name of the command (e.g. `expire`) or, if the option is used by more than one command and the definition for the option is the same for all of the commands, the `operation-general title="General Options"` section.
To add an option, add the following to the `<option-list>` section; if it does not exist, then wrap the following in `<option-list>` `</option-list>`. This example uses the boolean option `force` of the `restore` command. Simply replace that with your new option and the appropriate `summary`, `text` and `example`.
```
<option id="force" name="Force">
<summary>Force a restore.</summary>
<text>By itself this option forces the <postgres/> data and tablespace paths to be completely overwritten. In combination with <br-option>--delta</br-option> a timestamp/size delta will be performed instead of using checksums.</text>
<example>y</example>
</option>
```
> **IMPORTANT:** currently a period (.) is required to end the `summary` section.
## Testing
For testing, it is recommended that Vagrant and Docker be used; instructions are provided in the `README.md` file of the pgBackRest [test](https://github.com/pgbackrest/pgbackrest/blob/master/test) directory. A list of all possible test combinations can be viewed by running:
```
/backrest/test/test.pl --dry-run
```
> **WARNING:** currently the `BACKREST_USER` in `ContainerTest.pm` must exists, or the test suite will fail with a string concatenation error.
If using a RHEL system, the CPAN XML parser is required for running `test.pl` and `doc.pl`. Instructions for installing Docker and the XML parse can be found in the `README.md` file of the pgBackRest [doc](https://github.com/pgbackrest/pgbackrest/blob/master/doc) directory in the section "The following is a sample CentOS/RHEL 7 configuration that can be used for building the documentation". NOTE that the `Install latex (for building PDF)` is not required since testing of the docs need only be run for HTML output.
While some files are automatically generated during `make`, others are generated by running the test harness as follows:
```
/backrest/test/test.pl --gen-only
```
Prior to any submission, the html version of the documentation should also be run.
```
/backrest/doc/doc.pl --out=html
```
> **NOTE:** `ERROR: [028]` regarding cache is invalid is OK; it just means there have been changes and the documentation will be built from scratch. In this case, be patient as the build could take 20 minutes or more depending on your system.
### Writing a Unit Test
The goal of unit testing is to have 100 percent coverage. Two files will usually be involved in this process:
- **define.yaml** - defines the number of tests to be run for each module and test file. There is a comment at the top of the file that provides more information about this file.
- **src/module/somefileTest.c** - where "somefile" is the path and name of the test file where the unit tests are located for the code being updated (e.g. `src/module/command/expireTest.c`).
#### define.yaml
Each module is separated by a line of asterisks (*) and each test within is separated by a line of dashes (-). In the example below, the module is `command` and the unit test is `check`. The number of calls to `testBegin()` in a unit test file will dictate the number following `total:`, in this case 2. Under `coverage:`, the list of files that will be tested must be listed followed by the coverage level, which should always be `full`.
```
# ********************************************************************************************************************************
- name: command
test:
# ----------------------------------------------------------------------------------------------------------------------------
- name: check
total: 2
coverage:
command/check/common: full
command/check/check: full
```
#### somefileTest.c
Assuming that a test file already exists, new unit tests will either go in a new `testBegin()` section or be added to an existing section.
Unit test files are organized in the test/src/module directory with the same directory structure as the source code being tested. For example, if new code is added to src/**command/expire**.c then test/src/module/**command/expire**Test.c will need to be updated.
```
// *****************************************************************************************************************************
if (testBegin("expireBackup()"))
```
**Setting up the command to be run**
If configuration options are required then a string list with the command and options must be defined and passed to the function `harnessCfgLoad()`. For example, the following will set up a test to run `pgbackrest --repo-path=test/test-0/repo info` command:
```
String *repoPath = strNewFmt("%s/repo", testPath()); // create a string defining the repo path on the test system
StringList *argList = strLstNew(); // create an empty string list
strLstAdd(argList, strNewFmt("--repo-path=%s/", strPtr(repoPath))); // add the --repo-path option as a formatted string
strLstAddZ(argList, "info"); // add the command
harnessCfgLoad(cfgCmdExpire, argList); // load the command and option list into the test harness
TEST_RESULT_STR(strPtr(infoRender()), "No stanzas exist in the repository.\n", "text - no stanzas"); // run the test
```
Tests are run via macros. All test macros expect the first parameter to be the function to call that is being tested. With the exception of TEST_RESULT_VOID, the second parameter is the expected result, and with the exception of TEST_ERROR, the third parameter is a short description of the test. The most common macros are:
- `TEST_RESULT_STR` - Test the actual value of the string returned by the function.
- `TEST_RESULT_UINT` / `TEST_RESULT_INT` - Test for an unsigned integer / integer.
- `TEST_RESULT_BOOL` - Test a boolean return value.
- `TEST_RESULT_PTR` / `TEST_RESULT_PTR_NE` - Test a pointer: useful for testing if the pointer is `NULL` or not equal (`NE`) to `NULL`.
- `TEST_RESULT_VOID` - The function being tested returns a `void`. This is then usually followed by tests that ensure other actions occurred (e.g. a file was written to disk).
- `TEST_ERROR` / `TEST_ERROR_FMT` - Test for that a specific error code was raised with specific wording.
**Storing a file**
Sometimes it is necessary to store a file to the test directory. The following demonstrates that. It is not necessary to wrap the storagePutNP in TEST_RESULT_VOID, but doing so allows a short description to be displayed when running the tests (in this case "store a corrupt backup.info file").
```
String *content = strNew("bad content");
TEST_RESULT_VOID(
storagePutNP(storageNewWriteNP(storageTest, strNewFmt("%s/backup/demo/backup.info", strPtr(repoPath))),
harnessInfoChecksum(content)), "store a corrupt backup.info file");
```
**Testing a log message**
If a function being tested logs something with `LOG_WARN`, `LOG_INFO` or other `LOG_` macro, then the logged message must be cleared before the end of the test by using the `harnessLogResult()` function.
```
harnessLogResult(
"P00 WARN: WAL segment '000000010000000100000001' was not pushed due to error [25] and was manually skipped: error");
```
### Running a Unit Test
Unit tests are run, and coverage of the code being tested is provided, by running the following. This example would run the test set from the **define.yaml** section detailed above.
```
/backrest/test/test.pl --vm-out --dev --module=command --test=check --coverage-only
```
> **NOTE:** If you have changed branches, it is recommended the above be run with `--dev-test` instead of `--dev` to rebuild the code from scratch.
Because no test run is specified and `--coverage-only` has been requested, a coverage report will be generated and written to the local file system under `backrest/test/coverage/c-coverage.html` and will highlight code that has not been tested.
Sometimes it is useful to look at files that were generated during the test. The default for running any test is that, at the start/end of the test, the test harness will clean up all files and directories created. To override this behavior, a single test run must be specified and the option `--no-cleanup` provided. Again, continuing with the check command, we see in **define.yaml** above that there are two tests. Below, test one will be run and nothing will be cleaned up so that the files and directories in test/test-0 can be inspected.
```
/backrest/test/test.pl --vm-out --dev --module=command --test=check --coverage-only --run=1 --no-cleanup
```
For more details on running tests, again, please refer to the `README.md` file of the pgBackRest [test](https://github.com/pgbackrest/pgbackrest/blob/master/test) directory.

View File

@ -31,11 +31,12 @@
#
# CFGDEF_DEFAULT:
# Sets a default for the option for all commands if listed in the global section, or for specific commands if listed in the
# CFGDEF_COMMAND section.
# CFGDEF_COMMAND section. All boolean types require a default.
#
# CFGDEF_NEGATE:
# The option can be negated with "no" e.g. --no-compress. This applies tp options that are only valid on the command line (i.e.
# no config section defined). All config options are automatically negatable.
# The option can be negated with "no" e.g. --no-compress. This applies to options that are only valid on the command line (i.e.
# no config section defined) and if not specifically defined, the default is false. All config file boolean options are
# automatically negatable.
#
# CFGDEF_RESET:
# The option can be reset to default even if the default is undefined.

View File

@ -88,6 +88,7 @@
<source key="faq"/>
<source key="metric"/>
<source key="coding"/>
<source key="contributing"/>
<source key="documentation"/>
<source key="test"/>
</source-list>
@ -111,6 +112,7 @@
<render type="markdown">
<render-source key="index" file="../../../README.md"/>
<render-source key="coding" file="../../../CODING.md"/>
<render-source key="contributing" file="../../../CONTRIBUTING.md"/>
<render-source key="test" file="../../../test/README.md"/>
<render-source key="documentation" file="../../README.md"/>
</render>

View File

@ -12,6 +12,50 @@
<p>Indentation is four spaces -- no tabs. Only file types that absolutely require tabs (e.g. `Makefile`) may use them.</p>
</section>
<section id="line-length">
<title>Line Length</title>
<p>With the exception of documentation code, no line of any code or test file shall exceed 132 characters. If a line break is required, then it shall be after the first function parenthesis:</p>
<code-block>
// CORRECT - location of line break after first function parenthesis if line length is greater than 132
StringList *removeList = infoBackupDataLabelList(
infoBackup, strNewFmt("^%s.*", strPtr(strLstGet(currentBackupList, fullIdx))));
// INCORRECT
StringList *removeList = infoBackupDataLabelList(infoBackup, strNewFmt("^%s.*", strPtr(strLstGet(currentBackupList,
fullIdx))));
</code-block>
<p>If a conditional, then after a completed conditional, for example:</p>
<code-block>
// CORRECT - location of line break after a completed conditional if line length is greater than 132
if (archiveInfoPgHistory.id != backupInfoPgHistory.id ||
archiveInfoPgHistory.systemId != backupInfoPgHistory.systemId ||
archiveInfoPgHistory.version != backupInfoPgHistory.version)
// INCORRECT
if (archiveInfoPgHistory.id != backupInfoPgHistory.id || archiveInfoPgHistory.systemId !=
backupInfoPgHistory.systemId || archiveInfoPgHistory.version != backupInfoPgHistory.version)
</code-block>
</section>
<section id="inline-comment">
<title>Inline Comment</title>
<p>Inline comments shall start at character 69 and must not exceed the line length of 132. For example:</p>
<code-block>
typedef struct InlineCommentExample
{
const String *comment; // Inline comment example
const String *longComment; // Inline comment example that exceeds 132 characters should
// then go to next line but this should be avoided
} InlineCommentExample;
</code-block>
</section>
<section id="naming">
<title>Naming</title>

232
doc/xml/contributing.xml Normal file
View File

@ -0,0 +1,232 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE doc SYSTEM "doc.dtd">
<doc title="{[project]}" subtitle="Contributing to {[project]}" toc="y">
<description>{[project]} Contributing Guidelines.</description>
<section id="introduction">
<title>Introduction</title>
<p>This documentation is intended to assist contributors to <backrest/> by outlining some basic steps and guidelines for contributing to the project. Coding standards to follow are defined in <link url="{[github-url-master]}/CODING.md">CODING.md</link>. At a minimum, unit tests must be written and run and the documentation generated before submitting a Pull Request; see the <link section="/testing">Testing</link> section below for details.</p>
</section>
<section id="option">
<title>Adding an Option</title>
<p>Options can be added to a command or multiple commands. Options can be configuration file only, command-line only or valid for both. Once an option is added, <file>config.auto.*</file>, <file>define.auto.*</file> and <file>parse.auto.*</file> files will automatically be generated by the build system.</p>
<p> To add an option, two files need be to be modified:</p>
<list>
<list-item><file>build/lib/pgBackRestBuild/Config/Data.pm</file></list-item>
<list-item><file>doc/xml/reference.xml</file></list-item>
</list>
<p>These files are discussed in the following sections.</p>
<section id="data-file">
<title>Data.pm</title>
<p>There is a detailed comment at the top of this file on the configuration definitions which one can refer to in determining how to define the rules for the option.</p>
<section id="command-line-only">
<title>Command Line Only Options</title>
<p>Command-line only options are options where <id>CFGDEF_SECTION</id> rule is not defined. There are two sections to be updated when adding a command-line only option, each of which is marked by the comment <code>Command-line only options</code>.</p>
<list>
<list-item><b>Section 1:</b> Find the first section with the <code>Command-line only options</code> comment. This section defines and exports the constant for the actual option.</list-item>
<list-item><b>Section 2:</b> Find the second section with the <code>Command-line only options</code> comment. This is where the rules for the option are defined.</list-item>
</list>
<p>The steps for how to update these sections are detailed below.</p>
<p><b>Section 1</b></p>
<p>Copy the two lines (<quote>use constant</quote>/<quote>push</quote>) of an existing option and paste them where the option would be in alphabetical order and rename it to the same name as the new option name. For example CFGOPT_DRY_RUN, defined as <quote>dry-run</quote>.</p>
<p><b>Section 2</b></p>
<p>To better explain this section, <id>CFGOPT_ONLINE</id> will be used as an example:</p>
<code-block>
&amp;CFGOPT_ONLINE =>
{
&amp;CFGDEF_TYPE => CFGDEF_TYPE_BOOLEAN,
&amp;CFGDEF_NEGATE => true,
&amp;CFGDEF_DEFAULT => true,
&amp;CFGDEF_COMMAND =>
{
&amp;CFGCMD_BACKUP => {},
&amp;CFGCMD_STANZA_CREATE => {},
&amp;CFGCMD_STANZA_UPGRADE => {},
}
},
</code-block>
<p>Note that <id>CFGDEF_SECTION</id> is not present thereby making this a command-line only option. Each line is explained below:</p>
<list>
<list-item><id>CFGOPT_ONLINE</id> - the name of the option as defined in <b>Section 1</b></list-item>
<list-item><id>CFGDEF_TYPE</id> - the type of the option. Valid types are: <id>CFGDEF_TYPE_BOOLEAN</id>, <id>CFGDEF_TYPE_FLOAT</id>, <id>CFGDEF_TYPE_HASH</id>, <id>CFGDEF_TYPE_INTEGER</id>, <id>CFGDEF_TYPE_LIST</id>, <id>CFGDEF_TYPE_PATH</id>, <id>CFGDEF_TYPE_SIZE</id>, and <id>CFGDEF_TYPE_STRING</id>
</list-item>
<list-item><id>CFGDEF_NEGATE</id> - being a command-line only boolean option, this rule would automatically default to false so it must be defined if the option is negatable. Ask yourself if negation makes sense, for example, would a --dry-run option make sense as --no-dry-run? If the answer is no, then this rule can be omitted as it would automatically default to false. Any boolean option that cannot be negatable, must be a command-line only and not a configuration file option as all configuration boolean options must be negatable.</list-item>
<list-item><id>CFGDEF_DEFAULT</id> - sets a default for the option if the option is not provided when the command is run. The default can be global or it can be specified for a specific command in the <id>CFGDEF_COMMAND</id> section. For example, if it was desirable for the default to be false for the <id>CFGCMD_STANZA_CREATE</id> then CFGDEF_NEGATE => would be set to <id>true</id> in each command listed except for <id>CFGCMD_STANZA_CREATE</id> where it would be <id>false</id> and it would not be specified (as it is here) in the global section (meaning global for all commands listed).</list-item>
<list-item><id>CFGDEF_COMMAND</id> - list each command for which the option is valid. If a command is not listed, then the option is not valid for the command and an error will be thrown if it attempted to be used for that command.</list-item>
</list>
</section>
</section>
<section id="reference-file">
<title>reference.xml</title>
<p>All options must be documented or the system will error during the build. To add an option, find the command section identified by <code>command id="COMMAND"</code> section where <id>COMMAND</id> is the name of the command (e.g. <cmd>expire</cmd>) or, if the option is used by more than one command and the definition for the option is the same for all of the commands, the <code>operation-general title="General Options"</code> section.</p>
<p>To add an option, add the following to the <code>&lt;option-list&gt;</code> section; if it does not exist, then wrap the following in <code>&lt;option-list&gt;</code> <code>&lt;/option-list&gt;</code>. This example uses the boolean option <code>force</code> of the <cmd>restore</cmd> command. Simply replace that with your new option and the appropriate <code>summary</code>, <code>text</code> and <code>example</code>.</p>
<code-block>
&lt;option id="force" name="Force"&gt;
&lt;summary&gt;Force a restore.&lt;/summary&gt;
&lt;text&gt;By itself this option forces the &lt;postgres/&gt; data and tablespace paths to be completely overwritten. In combination with &lt;br-option&gt;--delta&lt;/br-option&gt; a timestamp/size delta will be performed instead of using checksums.&lt;/text&gt;
&lt;example>y&lt;/example&gt;
&lt;/option&gt;
</code-block>
<admonition type="important">currently a period (.) is required to end the <code>summary</code> section.</admonition>
</section>
</section>
<section id="testing">
<title>Testing</title>
<p>For testing, it is recommended that Vagrant and Docker be used; instructions are provided in the <file>README.md</file> file of the <backrest/> <link url="{[github-url-master]}/test">test</link> directory. A list of all possible test combinations can be viewed by running:</p>
<code-block>
/backrest/test/test.pl --dry-run
</code-block>
<admonition type="warning">currently the <id>BACKREST_USER</id> in <file>ContainerTest.pm</file> must exists, or the test suite will fail with a string concatenation error.</admonition>
<p>If using a RHEL system, the CPAN XML parser is required for running <file>test.pl</file> and <file>doc.pl</file>. Instructions for installing Docker and the XML parse can be found in the <file>README.md</file> file of the <backrest/> <link url="{[github-url-master]}/doc">doc</link> directory in the section <quote>The following is a sample CentOS/RHEL 7 configuration that can be used for building the documentation</quote>. NOTE that the <code>Install latex (for building PDF)</code> is not required since testing of the docs need only be run for HTML output.</p>
<p>While some files are automatically generated during <code>make</code>, others are generated by running the test harness as follows:</p>
<code-block>
/backrest/test/test.pl --gen-only
</code-block>
<p>Prior to any submission, the html version of the documentation should also be run.</p>
<code-block>
/backrest/doc/doc.pl --out=html
</code-block>
<admonition type="note"><code>ERROR: [028]</code> regarding cache is invalid is OK; it just means there have been changes and the documentation will be built from scratch. In this case, be patient as the build could take 20 minutes or more depending on your system.</admonition>
<section id="unit-test">
<title>Writing a Unit Test</title>
<p>The goal of unit testing is to have 100 percent coverage. Two files will usually be involved in this process:</p>
<list>
<list-item><b>define.yaml</b> - defines the number of tests to be run for each module and test file. There is a comment at the top of the file that provides more information about this file.</list-item>
<list-item><b>src/module/somefileTest.c</b> - where <quote>somefile</quote> is the path and name of the test file where the unit tests are located for the code being updated (e.g. <file>src/module/command/expireTest.c</file>).</list-item>
</list>
<section id="define-yaml">
<title>define.yaml</title>
<p>Each module is separated by a line of asterisks (*) and each test within is separated by a line of dashes (-). In the example below, the module is <code>command</code> and the unit test is <code>check</code>. The number of calls to <code>testBegin()</code> in a unit test file will dictate the number following <code>total:</code>, in this case 2. Under <code>coverage:</code>, the list of files that will be tested must be listed followed by the coverage level, which should always be <code>full</code>.</p>
<code-block>
# ********************************************************************************************************************************
- name: command
test:
# ----------------------------------------------------------------------------------------------------------------------------
- name: check
total: 2
coverage:
command/check/common: full
command/check/check: full
</code-block>
</section>
<section id="test-file">
<title>somefileTest.c</title>
<p>Assuming that a test file already exists, new unit tests will either go in a new <code>testBegin()</code> section or be added to an existing section.</p>
<p>Unit test files are organized in the test/src/module directory with the same directory structure as the source code being tested. For example, if new code is added to src/<b>command/expire</b>.c then test/src/module/<b>command/expire</b>Test.c will need to be updated.</p>
<code-block>
// *****************************************************************************************************************************
if (testBegin("expireBackup()"))
</code-block>
<p><b>Setting up the command to be run</b></p>
<p>If configuration options are required then a string list with the command and options must be defined and passed to the function <code>harnessCfgLoad()</code>. For example, the following will set up a test to run <cmd>pgbackrest --repo-path=test/test-0/repo info</cmd> command:</p>
<code-block>
String *repoPath = strNewFmt("%s/repo", testPath()); // create a string defining the repo path on the test system
StringList *argList = strLstNew(); // create an empty string list
strLstAdd(argList, strNewFmt("--repo-path=%s/", strPtr(repoPath))); // add the --repo-path option as a formatted string
strLstAddZ(argList, "info"); // add the command
harnessCfgLoad(cfgCmdExpire, argList); // load the command and option list into the test harness
TEST_RESULT_STR(strPtr(infoRender()), "No stanzas exist in the repository.\n", "text - no stanzas"); // run the test
</code-block>
<p>Tests are run via macros. All test macros expect the first parameter to be the function to call that is being tested. With the exception of TEST_RESULT_VOID, the second parameter is the expected result, and with the exception of TEST_ERROR, the third parameter is a short description of the test. The most common macros are:</p>
<list>
<list-item><id>TEST_RESULT_STR</id> - Test the actual value of the string returned by the function.</list-item>
<list-item><id>TEST_RESULT_UINT</id> / <id>TEST_RESULT_INT</id> - Test for an unsigned integer / integer.</list-item>
<list-item><id>TEST_RESULT_BOOL</id> - Test a boolean return value.</list-item>
<list-item><id>TEST_RESULT_PTR</id> / <id>TEST_RESULT_PTR_NE</id> - Test a pointer: useful for testing if the pointer is <id>NULL</id> or not equal (<id>NE</id>) to <id>NULL</id>.</list-item>
<list-item><id>TEST_RESULT_VOID</id> - The function being tested returns a <code>void</code>. This is then usually followed by tests that ensure other actions occurred (e.g. a file was written to disk).</list-item>
<list-item><id>TEST_ERROR</id> / <id>TEST_ERROR_FMT</id> - Test for that a specific error code was raised with specific wording.</list-item>
</list>
<p><b>Storing a file</b></p>
<p>Sometimes it is necessary to store a file to the test directory. The following demonstrates that. It is not necessary to wrap the storagePutNP in TEST_RESULT_VOID, but doing so allows a short description to be displayed when running the tests (in this case <quote>store a corrupt backup.info file</quote>).</p>
<code-block>
String *content = strNew("bad content");
TEST_RESULT_VOID(
storagePutNP(storageNewWriteNP(storageTest, strNewFmt("%s/backup/demo/backup.info", strPtr(repoPath))),
harnessInfoChecksum(content)), "store a corrupt backup.info file");
</code-block>
<p><b>Testing a log message</b></p>
<p>If a function being tested logs something with <id>LOG_WARN</id>, <id>LOG_INFO</id> or other <id>LOG_</id> macro, then the logged message must be cleared before the end of the test by using the <code>harnessLogResult()</code> function.</p>
<code-block>
harnessLogResult(
"P00 WARN: WAL segment '000000010000000100000001' was not pushed due to error [25] and was manually skipped: error");
</code-block>
</section>
</section>
<section id="unit-test-run">
<title>Running a Unit Test</title>
<p>Unit tests are run, and coverage of the code being tested is provided, by running the following. This example would run the test set from the <b>define.yaml</b> section detailed above.</p>
<code-block>
/backrest/test/test.pl --vm-out --dev --module=command --test=check --coverage-only
</code-block>
<admonition type="note">If you have changed branches, it is recommended the above be run with <code>--dev-test</code> instead of <code>--dev</code> to rebuild the code from scratch.</admonition>
<p>Because no test run is specified and <code>--coverage-only</code> has been requested, a coverage report will be generated and written to the local file system under <file>backrest/test/coverage/c-coverage.html</file> and will highlight code that has not been tested.</p>
<p>Sometimes it is useful to look at files that were generated during the test. The default for running any test is that, at the start/end of the test, the test harness will clean up all files and directories created. To override this behavior, a single test run must be specified and the option <code>--no-cleanup</code> provided. Again, continuing with the check command, we see in <b>define.yaml</b> above that there are two tests. Below, test one will be run and nothing will be cleaned up so that the files and directories in test/test-0 can be inspected.</p>
<code-block>
/backrest/test/test.pl --vm-out --dev --module=command --test=check --coverage-only --run=1 --no-cleanup
</code-block>
<p> For more details on running tests, again, please refer to the <file>README.md</file> file of the <backrest/> <link url="{[github-url-master]}/test">test</link> directory.</p>
</section>
</section>
</doc>

View File

@ -15,6 +15,14 @@
<release date="XXXX-XX-XX" version="2.19dev" title="UNDER DEVELOPMENT">
<release-doc-list>
<release-improvement-list>
<release-item>
<release-item-contributor-list>
<release-item-contributor id="cynthia.shang"/>
</release-item-contributor-list>
<p>Document how to contribute to <backrest/>.</p>
</release-item>
<release-item>
<release-item-contributor-list>
<release-item-contributor id="brad.nicholson"/>
@ -24,7 +32,6 @@
</release-item>
</release-improvement-list>
</release-doc-list>
</release>
<release date="2019-10-01" version="2.18" title="PostgreSQL 12 Support">

3
test/Vagrantfile vendored
View File

@ -40,7 +40,8 @@ Vagrant.configure(2) do |config|
echo 'supersede domain-name-servers 8.8.8.8;' >> /etc/dhcp/dhclient.conf
/etc/init.d/networking restart
# Set time sync settings so builds don't fail with clock skew errors
# Set time sync settings so builds don't fail with clock skew errors. If a build does fail with "clock skew detected",
# rerun the following at the command line.
#---------------------------------------------------------------------------------------------------------------------------
echo 'Time Sync Settings' && date
sudo /etc/init.d/virtualbox-guest-utils stop