addSkipDirectory(name),
called at build time. It extends the 11 defaults rather than replacing them, so a
build that adds .vs cannot lose .git by accident.
addSkipDirectory(name)
.vs lose
.git by accident, and two implementations could then prune different trees
while both satisfying the specification.
void addSkipDirectory(const std::string& name), defined in the binary's own
translation unit and exported from nothing, so no library and no test can call it.
addSkipDirectory because the two are fixed for opposite reasons. §3.6
requires a run to close its output with a line counting the files and directories it
reached, and rather than leave each language to decide which component keeps those counts,
it fixes that too: the traversal component holds them, increments them where it already
makes its selection decisions, never resets them between root paths, and owns the summary's
text, while the entry binary calls for the line once - after the last root path, the one
fact no component inside the library can know.
emitRunSummary() in C++
and C#, emit_run_summary(&mut self) in Rust - in the idiom §3.5
already leaves open for addSkipDirectory. No implementation exposes an
accessor for either count: the line is the whole of what they are for, and a unit suite
reads them by reading that line through its own output double.
Checker struct rather than a test
framework.char* argv[] and Windows does not decode that system-codepage vector to
Unicode.
accessed 47 files, 36 directories.
| Language | Engine |
|---|---|
| C++ | std::regex constructed with std::regex::ECMAScript |
| Rust | the regex crate |
| C# | System.Text.RegularExpressions.Regex |
| Python | the re module of the standard library |
/L reports the whole line,
never a match position, a matched substring, or a capture group. Greediness, alternation
preference, and capture-group numbering are unobservable, so the four engines differ on
nothing TextFinder observes so long as the pattern is one they all accept. That portable
subset is:
\ before any of . \ * + ? ( ) [ ] { } | ^ $ /.*, +, ?, {n}, {n,}, and {n,m}, each also in its lazy form|, grouping (...), and non-capturing grouping (?:...)[...] and [^...], ranges included^ and $\d, \D, \w, \W, \s, \S, \b, \B\n, \r, \t, \f, \v, and \xHH., ^, and
$ agree because §3.3 splits lines before matching and a line holds no
terminator, so the engines' differing treatment of a terminator inside the subject never
arises. The rest agree on ASCII lines: std::regex over
char matches one byte where the other three engines match one Unicode scalar
value, and \d, \w, \s, and \b are
Unicode-aware in those three and ASCII-only in std::regex. On a line holding a
non-ASCII character, a pattern using ., a character class, or a class escape
can match in three implementations and not the fourth.
(?i), atomic and possessive quantifiers, Unicode property escapes such
as \p{L}, POSIX class names such as [[:alpha:]], and the anchors
\A, \z, and \Z. Each is accepted by some of the four
engines and rejected by others, and two that accept the same construct do not always spell
it the same way.
., a character class, or a
class escape, so none turns on which engine an implementation names. Where the set does
reach for a pattern in one language's own vocabulary - ^export for C++,
^pub for Rust, ^public for C# - it confines that
pattern to an invocation rooted in that implementation's own component directories, so the
pattern is language-specific but the invocation it runs in searches nothing shared.
Checker rather than a framework.
Assertion wording and counts belong to each implementation; what the suites must agree on
across implementations is the observable behavior §3 through §5 already fix. A
demonstration's output moves as this project's tree changes, so a capture states its date
and is replaced wholesale rather than edited. The
C++ Testing and
C++ Demonstration
pages are that requirement satisfied once.