<feed xmlns='http://www.w3.org/2005/Atom'>
<title>catch3/src, branch chore/cpp17-descriptions</title>
<subtitle>Progmasoft Catch3: an independently maintained C++17 test framework with a parallel runner, property checks, snapshots and structured results.</subtitle>
<link rel='alternate' type='text/html' href='https://git.progmasoft.com/catch3/'/>
<entry>
<title>Say C++17 everywhere the project describes itself</title>
<updated>2026-10-31T21:25:59+00:00</updated>
<author>
<name>Leitwolf11</name>
</author>
<published>2026-10-31T21:25:59+00:00</published>
<link rel='alternate' type='text/html' href='https://git.progmasoft.com/catch3/commit/?id=ff1da91edffa9ee887e9fe737fcd87ec540dfa60'/>
<id>ff1da91edffa9ee887e9fe737fcd87ec540dfa60</id>
<content type='text'>
The move to C++17 left a trail of older claims. The Runner's own
.clang-format still said c++20; the result is byte for byte the same under
either value. Premake and Xmake still compiled the library as C++20 and
the main library as C++14, which meant their CI jobs were not testing the
language the project promises. Both now use C++17 for every target.

The rest is text: the Meson pkg-config description claimed C++14, comments
in the CMake and Meson files explained requirements that no longer exist,
.clang-tidy gave C++14 as the reason for two exclusions, issue templates
asked for C++20 samples, and the example headers named -std=c++14 and
catch2-with-main. The release notes and the migration guide state the
C++17 minimum. Doxygen's brief called the project "Catch2's test API",
which presents it as Catch2. Meson declared only BSL-1.0; it now names both
licenses of the tree. The amalgamated files are regenerated.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
The move to C++17 left a trail of older claims. The Runner's own
.clang-format still said c++20; the result is byte for byte the same under
either value. Premake and Xmake still compiled the library as C++20 and
the main library as C++14, which meant their CI jobs were not testing the
language the project promises. Both now use C++17 for every target.

The rest is text: the Meson pkg-config description claimed C++14, comments
in the CMake and Meson files explained requirements that no longer exist,
.clang-tidy gave C++14 as the reason for two exclusions, issue templates
asked for C++20 samples, and the example headers named -std=c++14 and
catch2-with-main. The release notes and the migration guide state the
C++17 minimum. Doxygen's brief called the project "Catch2's test API",
which presents it as Catch2. Meson declared only BSL-1.0; it now names both
licenses of the tree. The amalgamated files are regenerated.
</pre>
</div>
</content>
</entry>
<entry>
<title>Convert to double in the coverage report explicitly</title>
<updated>2026-10-31T17:20:48+00:00</updated>
<author>
<name>Leitwolf11</name>
</author>
<published>2026-10-31T17:20:48+00:00</published>
<link rel='alternate' type='text/html' href='https://git.progmasoft.com/catch3/commit/?id=e273973f9bc365a646fad427169416c37da28e98'/>
<id>e273973f9bc365a646fad427169416c37da28e98</id>
<content type='text'>
Dividing a long long by 10.0 is an implicit conversion, which fails in a
project that builds its tests with -Wimplicit-int-float-conversion and
warnings as errors, as Tala does.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Dividing a long long by 10.0 is an implicit conversion, which fails in a
project that builds its tests with -Wimplicit-int-float-conversion and
warnings as errors, as Tala does.
</pre>
</div>
</content>
</entry>
<entry>
<title>Move to the Progmasoft Linking Exception, Version 2.0</title>
<updated>2026-10-31T10:42:35+00:00</updated>
<author>
<name>Leitwolf11</name>
</author>
<published>2026-10-31T10:42:35+00:00</published>
<link rel='alternate' type='text/html' href='https://git.progmasoft.com/catch3/commit/?id=31486e3217b8424809bcf129c0ef24ef5639e919'/>
<id>31486e3217b8424809bcf129c0ef24ef5639e919</id>
<content type='text'>
Version 1.1 left programs built with Progmasoft tools under MPL-2.0 duties
for the code the tools place in them, and left other cases unclear.
Version 2.0 adds a generated code permission, covers runtime and standard
libraries, compiler transformations, interfaces and examples, and settles
how a file is identified and how to tell recipients about source.

Every project SPDX expression moves to AdditionRef-Progmasoft-Exception-2.0.
The older texts stay, because released versions name them.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Version 1.1 left programs built with Progmasoft tools under MPL-2.0 duties
for the code the tools place in them, and left other cases unclear.
Version 2.0 adds a generated code permission, covers runtime and standard
libraries, compiler transformations, interfaces and examples, and settles
how a file is identified and how to tell recipients about source.

Every project SPDX expression moves to AdditionRef-Progmasoft-Exception-2.0.
The older texts stay, because released versions name them.
</pre>
</div>
</content>
</entry>
<entry>
<title>Finish the move to C++17 in POSIX code and the amalgamated files</title>
<updated>2026-10-31T10:36:43+00:00</updated>
<author>
<name>Leitwolf11</name>
</author>
<published>2026-10-31T10:36:43+00:00</published>
<link rel='alternate' type='text/html' href='https://git.progmasoft.com/catch3/commit/?id=afe91797e9d50cd4712d3694a2e5fedfc0b966b1'/>
<id>afe91797e9d50cd4712d3694a2e5fedfc0b966b1</id>
<content type='text'>
A lambda in the POSIX half of Process.cpp captured a structured binding,
which needs C++20 and which a Windows build never compiles. The files in
extras still held the C++20 sources, so they are generated again, and the
generator no longer says the runner needs C++20.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
A lambda in the POSIX half of Process.cpp captured a structured binding,
which needs C++20 and which a Windows build never compiles. The files in
extras still held the C++20 sources, so they are generated again, and the
generator no longer says the runner needs C++20.
</pre>
</div>
</content>
</entry>
<entry>
<title>Build Catch3 as C++17</title>
<updated>2026-10-31T10:00:24+00:00</updated>
<author>
<name>Leitwolf11</name>
</author>
<published>2026-10-31T10:00:24+00:00</published>
<link rel='alternate' type='text/html' href='https://git.progmasoft.com/catch3/commit/?id=8814bd0b796b2e291fa5b4a1033f0313c8a26065'/>
<id>8814bd0b796b2e291fa5b4a1033f0313c8a26065</id>
<content type='text'>
C++17 is still the widest common target, and a library built on it stays
usable from C++17 and every later standard, while C++20 would have left
out many users.

Concepts become enable_if on named traits in Detail/TypeTraits.hpp, which
also replaces std::remove_cvref_t and std::identity. std::span becomes
std::vector references, std::bit_cast becomes static_cast and memcpy,
template lambdas become helper templates, and std::erase_if, contains,
starts_with and ends_with have their C++17 forms. Random::Between no
longer uses a lambda over a constexpr local, which MSVC rejects outside
/permissive-, so the headers work under its default mode too.

Tests give up designated initializers, which MSVC refuses in C++17. CI
builds the matrix in C++17 and C++20, since the headers promise both.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
C++17 is still the widest common target, and a library built on it stays
usable from C++17 and every later standard, while C++20 would have left
out many users.

Concepts become enable_if on named traits in Detail/TypeTraits.hpp, which
also replaces std::remove_cvref_t and std::identity. std::span becomes
std::vector references, std::bit_cast becomes static_cast and memcpy,
template lambdas become helper templates, and std::erase_if, contains,
starts_with and ends_with have their C++17 forms. Random::Between no
longer uses a lambda over a constexpr local, which MSVC rejects outside
/permissive-, so the headers work under its default mode too.

Tests give up designated initializers, which MSVC refuses in C++17. CI
builds the matrix in C++17 and C++20, since the headers promise both.
</pre>
</div>
</content>
</entry>
<entry>
<title>Compare whole numbers in catch3.json without floating-point equality</title>
<updated>2026-10-29T16:30:46+00:00</updated>
<author>
<name>Leitwolf11</name>
</author>
<published>2026-10-29T16:30:46+00:00</published>
<link rel='alternate' type='text/html' href='https://git.progmasoft.com/catch3/commit/?id=0b4b57a0688f2943f4c797c20d68d533cf70fd15'/>
<id>0b4b57a0688f2943f4c797c20d68d533cf70fd15</id>
<content type='text'>
Clang's -Wfloat-equal, an error in the development builds, rejected the
version check, which compared the JSON number with 1.0 by ==. The version
is now read through the same whole-number check as the counts and
compared as an integer, and that check asks whether the number exceeds
its floor instead of comparing the two for equality. The test of the
confidence interval compares within 1e-12. The amalgamated source is
regenerated.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Clang's -Wfloat-equal, an error in the development builds, rejected the
version check, which compared the JSON number with 1.0 by ==. The version
is now read through the same whole-number check as the counts and
compared as an integer, and that check asks whether the number exceeds
its floor instead of comparing the two for equality. The test of the
confidence interval compares within 1e-12. The amalgamated source is
regenerated.
</pre>
</div>
</content>
</entry>
<entry>
<title>Read the options of a project from catch3.json</title>
<updated>2026-10-29T16:22:21+00:00</updated>
<author>
<name>Leitwolf11</name>
</author>
<published>2026-10-29T16:22:21+00:00</published>
<link rel='alternate' type='text/html' href='https://git.progmasoft.com/catch3/commit/?id=f41a9c3c01b4df1100e24a63987716cb4dbf28f2'/>
<id>f41a9c3c01b4df1100e24a63987716cb4dbf28f2</id>
<content type='text'>
A project can now keep the options it always wants in a catch3.json
instead of repeating them in every CTest, Bazel or CI command line.

Where it is found: the runner looks in the working directory and in each
directory above it, up to the root of the Git repository that holds the
working directory, and reads the nearest file. Outside a repository it
looks only in the working directory, so a stray file in a home directory
never applies. --config=FILE names a file instead (relative to the
working directory) and --no-config reads none; the last of the two wins.
Workers, --help and --version read no file, so a broken file never stands
in the way of help, and workers keep taking everything from their runner.

What it says: strict JSON with "version": 1 required and "$schema"
allowed for editors. The keys are the long options of the command line,
and each value becomes the arguments it stands for and goes through the
same parser, so there is one set of checks and one set of messages; the
file and the key are named in front of them. Values are typed: switches
take true (false for the negated adaptive-timeout and bisect), counts
take whole numbers, timeout and the confidence interval take numbers,
retries and seed take a number or a word, tests and warn take arrays,
verbosity takes quiet, normal or verbose, and history takes a path or
false. Paths are relative to the directory of the file. An unknown key is
an error with a suggestion when it is close to a known one; a key that
names an option for one run (shard, section, list, replay, ...) is told
that it belongs on the command line. Repeated keys are refused.

Precedence: the file's options are applied first and the command line on
top, so the command line wins; test specs on the command line replace the
file's tests instead of widening them with "or". --verbose names the file
that was read.

Options gains --config and --no-config, a ParseOptions overload that
starts from given options, and a public EditDistance for the suggestions.
The new ProjectConfig module holds the search, the key table and
ParseCommandLine, which Runner now calls in place of ParseOptions.

Tests cover the search (nearest file, the repository boundary, outside a
repository), every key, words and defaults, relative and absolute paths,
the messages for wrong files, precedence, --config, --no-config and the
modes that read no file. A fuzzer reads arbitrary file text and checks
that the worker arguments of the result parse again. The command line
reference, the runner guide and the release notes describe the file; the
amalgamated sources are regenerated.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
A project can now keep the options it always wants in a catch3.json
instead of repeating them in every CTest, Bazel or CI command line.

Where it is found: the runner looks in the working directory and in each
directory above it, up to the root of the Git repository that holds the
working directory, and reads the nearest file. Outside a repository it
looks only in the working directory, so a stray file in a home directory
never applies. --config=FILE names a file instead (relative to the
working directory) and --no-config reads none; the last of the two wins.
Workers, --help and --version read no file, so a broken file never stands
in the way of help, and workers keep taking everything from their runner.

What it says: strict JSON with "version": 1 required and "$schema"
allowed for editors. The keys are the long options of the command line,
and each value becomes the arguments it stands for and goes through the
same parser, so there is one set of checks and one set of messages; the
file and the key are named in front of them. Values are typed: switches
take true (false for the negated adaptive-timeout and bisect), counts
take whole numbers, timeout and the confidence interval take numbers,
retries and seed take a number or a word, tests and warn take arrays,
verbosity takes quiet, normal or verbose, and history takes a path or
false. Paths are relative to the directory of the file. An unknown key is
an error with a suggestion when it is close to a known one; a key that
names an option for one run (shard, section, list, replay, ...) is told
that it belongs on the command line. Repeated keys are refused.

Precedence: the file's options are applied first and the command line on
top, so the command line wins; test specs on the command line replace the
file's tests instead of widening them with "or". --verbose names the file
that was read.

Options gains --config and --no-config, a ParseOptions overload that
starts from given options, and a public EditDistance for the suggestions.
The new ProjectConfig module holds the search, the key table and
ParseCommandLine, which Runner now calls in place of ParseOptions.

Tests cover the search (nearest file, the repository boundary, outside a
repository), every key, words and defaults, relative and absolute paths,
the messages for wrong files, precedence, --config, --no-config and the
modes that read no file. A fuzzer reads arbitrary file text and checks
that the worker arguments of the result parse again. The command line
reference, the runner guide and the release notes describe the file; the
amalgamated sources are regenerated.
</pre>
</div>
</content>
</entry>
<entry>
<title>Read and write JSON with yyjson and JUnit XML with pugixml</title>
<updated>2026-10-25T10:57:03+00:00</updated>
<author>
<name>Leitwolf11</name>
</author>
<published>2026-10-25T10:57:03+00:00</published>
<link rel='alternate' type='text/html' href='https://git.progmasoft.com/catch3/commit/?id=45f8c539e86099cb80ecf29b0bf2b3340e141001'/>
<id>45f8c539e86099cb80ecf29b0bf2b3340e141001</id>
<content type='text'>
The runner's hand-written JSON parser and writer, and its XML escaping,
are replaced by two established MIT libraries, vendored in third_party/:
yyjson 0.13.0 and pugixml 1.16. JsonValue and the JUnit model stay as they
are.

Both are compiled into Catch3's own translation units, so a project that
links its own yyjson or pugixml is not affected:
- src/Progmasoft/Runner/Yyjson.inl includes yyjson.c into Json.cpp with
  yyjson_api defined as static, so no yyjson symbol leaves that file.
- src/Progmasoft/PugiXml.inl includes pugixml header-only into
  XmlWriter.cpp with its namespace renamed to Progmasoft_Catch3_Pugi.
- Both are read as system headers, outside the warnings Catch3 builds
  with; they are .inl files, which the header install rules leave out.

JSON keeps its behaviour: strings are bytes and come back as written
(invalid UTF-8 is allowed both ways), non-finite numbers are written as
null, documents nested deeper than 256 levels are refused while they are
converted, and whole numbers are written as integers. Fractions are now
written as the shortest text that reads back as the same double.

JUnit XML stays equivalent; pugixml writes ' and &gt; in attributes as they
are, U+FFFD as the character itself and empty elements as &lt;x /&gt;. The
tests follow.

third_party/{yyjson,pugixml} each have BUILD.bazel, CMakeLists.txt,
meson.build, xmake.lua and premake5.lua, which only provide the include
directory to the library; CMake links them through BUILD_INTERFACE.

tools/scripts/updateThirdParty.py pins the version and sha256 of each
archive; --check, now a step of the header and license workflow, fails
when a vendored file differs from its archive, and --update writes them.
Renovate proposes new versions through a regex manager on that table.

The amalgamation now has a third file, catch_amalgamated_third_party.hpp,
with both wrappers and the libraries inlined; catch_amalgamated.cpp
includes it once. The documents and the release notes say so.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
The runner's hand-written JSON parser and writer, and its XML escaping,
are replaced by two established MIT libraries, vendored in third_party/:
yyjson 0.13.0 and pugixml 1.16. JsonValue and the JUnit model stay as they
are.

Both are compiled into Catch3's own translation units, so a project that
links its own yyjson or pugixml is not affected:
- src/Progmasoft/Runner/Yyjson.inl includes yyjson.c into Json.cpp with
  yyjson_api defined as static, so no yyjson symbol leaves that file.
- src/Progmasoft/PugiXml.inl includes pugixml header-only into
  XmlWriter.cpp with its namespace renamed to Progmasoft_Catch3_Pugi.
- Both are read as system headers, outside the warnings Catch3 builds
  with; they are .inl files, which the header install rules leave out.

JSON keeps its behaviour: strings are bytes and come back as written
(invalid UTF-8 is allowed both ways), non-finite numbers are written as
null, documents nested deeper than 256 levels are refused while they are
converted, and whole numbers are written as integers. Fractions are now
written as the shortest text that reads back as the same double.

JUnit XML stays equivalent; pugixml writes ' and &gt; in attributes as they
are, U+FFFD as the character itself and empty elements as &lt;x /&gt;. The
tests follow.

third_party/{yyjson,pugixml} each have BUILD.bazel, CMakeLists.txt,
meson.build, xmake.lua and premake5.lua, which only provide the include
directory to the library; CMake links them through BUILD_INTERFACE.

tools/scripts/updateThirdParty.py pins the version and sha256 of each
archive; --check, now a step of the header and license workflow, fails
when a vendored file differs from its archive, and --update writes them.
Renovate proposes new versions through a regex manager on that table.

The amalgamation now has a third file, catch_amalgamated_third_party.hpp,
with both wrappers and the libraries inlined; catch_amalgamated.cpp
includes it once. The documents and the release notes say so.
</pre>
</div>
</content>
</entry>
<entry>
<title>Give Catch3 its own build names and headers</title>
<updated>2026-10-24T23:37:35+00:00</updated>
<author>
<name>Leitwolf11</name>
</author>
<published>2026-10-24T23:37:35+00:00</published>
<link rel='alternate' type='text/html' href='https://git.progmasoft.com/catch3/commit/?id=126dc333564cb72b5115d9a9242d66694694b6d0'/>
<id>126dc333564cb72b5115d9a9242d66694694b6d0</id>
<content type='text'>
Catch2 compatibility means the API tests are written in: the macros, such
as TEST_CASE and REQUIRE, and the Catch:: namespace. It does not mean
Catch2's build names or header paths. find_package(Catch2) found Catch3,
and a project using both frameworks met two packages, two sets of targets,
two Catch.cmake modules and two catch2/ header trees with the same names.

Build names:
- CMake: project and package Catch3, targets Catch3::Catch3 and
  Catch3::Catch3WithMain, libraries Catch3 and Catch3Main, options
  CATCH3_*, lib/cmake/Catch3 with Catch3Config, and pkg-config catch3 and
  catch3-with-main. The version is Catch3's, 0.3.0, compatible within a
  minor version.
- The CMake scripts are Catch3.cmake (catch3_discover_tests),
  Catch3AddTests.cmake and Catch3ShardTests.cmake
  (catch3_add_sharded_tests); the helpers they define at ctest time have a
  catch3_ prefix. ParseAndAddCatchTests.cmake, deprecated upstream, is gone.
- Bazel //:catch3 and //:catch3_main, Meson catch3 and catch3-with-main
  (with override_dependency), Xmake catch3 and catch3_main, Premake Catch3
  and Catch3WithMain. Visual Studio projects get GUIDs of their own.
- The Progmasoft::Catch3 target and package are gone: property testing
  and snapshots are part of Catch3::Catch3. Their sources now build with
  the library's warning flags, so property failures are built field by
  field (no missing initializers) and the default property name is a
  thread-local pointer to the name its scope owns (no global destructor).

Headers: the engine moved from src/catch2 to src/Progmasoft/Catch3 with
Progmasoft names, so &lt;catch2/catch_test_macros.hpp&gt; is
&lt;Progmasoft/Catch3/TestMacros.hpp&gt;, &lt;catch2/matchers/catch_matchers_string.hpp&gt;
is &lt;Progmasoft/Catch3/Matchers/String.hpp&gt;, and interface headers are named
after their interface, such as Interfaces/IConfig.hpp. Include guards
became #pragma once. The installed headers go to include/Progmasoft.
src/Progmasoft/Context.cpp is StrategyContext.cpp, so no two sources share
a file name.

tools/scripts/upstreamFiles.txt lists the files that come from Catch2.
The license check expects BSL-1.0 on them and Progmasoft's header on the
rest of src/Progmasoft; the convenience-header check, the amalgamation
script and the surrogate tests take the engine's headers from it. The
amalgamated files are regenerated. The header-guard workflow checks for
#pragma once instead of checkguard.

Documents, the README and the release notes describe the new names.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Catch2 compatibility means the API tests are written in: the macros, such
as TEST_CASE and REQUIRE, and the Catch:: namespace. It does not mean
Catch2's build names or header paths. find_package(Catch2) found Catch3,
and a project using both frameworks met two packages, two sets of targets,
two Catch.cmake modules and two catch2/ header trees with the same names.

Build names:
- CMake: project and package Catch3, targets Catch3::Catch3 and
  Catch3::Catch3WithMain, libraries Catch3 and Catch3Main, options
  CATCH3_*, lib/cmake/Catch3 with Catch3Config, and pkg-config catch3 and
  catch3-with-main. The version is Catch3's, 0.3.0, compatible within a
  minor version.
- The CMake scripts are Catch3.cmake (catch3_discover_tests),
  Catch3AddTests.cmake and Catch3ShardTests.cmake
  (catch3_add_sharded_tests); the helpers they define at ctest time have a
  catch3_ prefix. ParseAndAddCatchTests.cmake, deprecated upstream, is gone.
- Bazel //:catch3 and //:catch3_main, Meson catch3 and catch3-with-main
  (with override_dependency), Xmake catch3 and catch3_main, Premake Catch3
  and Catch3WithMain. Visual Studio projects get GUIDs of their own.
- The Progmasoft::Catch3 target and package are gone: property testing
  and snapshots are part of Catch3::Catch3. Their sources now build with
  the library's warning flags, so property failures are built field by
  field (no missing initializers) and the default property name is a
  thread-local pointer to the name its scope owns (no global destructor).

Headers: the engine moved from src/catch2 to src/Progmasoft/Catch3 with
Progmasoft names, so &lt;catch2/catch_test_macros.hpp&gt; is
&lt;Progmasoft/Catch3/TestMacros.hpp&gt;, &lt;catch2/matchers/catch_matchers_string.hpp&gt;
is &lt;Progmasoft/Catch3/Matchers/String.hpp&gt;, and interface headers are named
after their interface, such as Interfaces/IConfig.hpp. Include guards
became #pragma once. The installed headers go to include/Progmasoft.
src/Progmasoft/Context.cpp is StrategyContext.cpp, so no two sources share
a file name.

tools/scripts/upstreamFiles.txt lists the files that come from Catch2.
The license check expects BSL-1.0 on them and Progmasoft's header on the
rest of src/Progmasoft; the convenience-header check, the amalgamation
script and the surrogate tests take the engine's headers from it. The
amalgamated files are regenerated. The header-guard workflow checks for
#pragma once instead of checkguard.

Documents, the README and the release notes describe the new names.
</pre>
</div>
</content>
</entry>
<entry>
<title>Do not write the history without its lock</title>
<updated>2026-10-24T14:43:17+00:00</updated>
<author>
<name>Leitwolf11</name>
</author>
<published>2026-10-24T14:43:17+00:00</published>
<link rel='alternate' type='text/html' href='https://git.progmasoft.com/catch3/commit/?id=622188f9c08e4af35941c146979883ed4882410e'/>
<id>622188f9c08e4af35941c146979883ed4882410e</id>
<content type='text'>
History::Commit went on writing when the lock was still held by another
run after ten seconds, so two runs that finished together could replace
each other's record. It now returns an error instead, which the runner
reports as a history that was not saved. The wait is a parameter, so the
new unit test can hold the lock and see the commit refused.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
History::Commit went on writing when the lock was still held by another
run after ten seconds, so two runs that finished together could replace
each other's record. It now returns an error instead, which the runner
reports as a history that was not saved. The wait is a parameter, so the
new unit test can hold the lock and see the commit refused.
</pre>
</div>
</content>
</entry>
</feed>
