Skip to content

Commit 384040c

Browse files
committed
bug fixes and updates
1 parent ea9f909 commit 384040c

29 files changed

Lines changed: 1884 additions & 166 deletions

README.md

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,32 +12,38 @@ See [http://cppspec.readthedocs.org/](http://cppspec.readthedocs.org/) for full
1212

1313
## Requirements
1414

15-
C++Spec requires a compiler and standard library with support for C++23: Currently tested and confirmed working are:
15+
C++Spec requires a compiler and standard library with C++23 support. Currently tested:
16+
1617
- LLVM/Clang 18 (on Linux, macOS, and Windows)
1718
- GCC 14.2 (on Linux and macOS)
1819
- MSVC 19.43 (on Windows)
1920
- AppleClang 16 (on macOS)
2021

21-
__Note:__ Only the tests require being compiled with C++23 support (`-std=c++23`). No other part of an existing project's build must be modified.
22+
__Note:__ Only spec files require C++23 (`-std=c++23`). No other part of an existing project's build needs modification.
2223

2324
## Usage
24-
The recommended usage is as a subproject integrated into your build system. For CMake this would look something like below:
25+
26+
The recommended approach is to integrate C++Spec as a CMake subproject:
27+
2528
```cmake
2629
FetchContent_Declare(
27-
c++spec
30+
cppspec
2831
GIT_REPOSITORY https://github.com/toroidal-code/cppspec
2932
GIT_TAG VERSION
3033
)
34+
FetchContent_MakeAvailable(cppspec)
3135
3236
# Or using CPM
3337
CPMAddPackage("gh:toroidal-code/cppspec@VERSION")
3438
```
3539

36-
Specs can then be automatically added as targets with
40+
Spec files are picked up automatically with:
41+
3742
```cmake
3843
discover_specs(specs_folder)
3944
```
40-
This will create a separate executable for every file ending in `_spec.cpp` in the given directory (recursive) and add them to CTest.
45+
46+
This creates a separate CTest executable for every file ending in `_spec.cpp` in the given directory (recursive).
4147

4248
## Introduction
4349

@@ -49,18 +55,18 @@ If you've ever used RSpec or Jasmine, chances are you'll be familiar with C++Spe
4955

5056
describe order_spec("Order", $ {
5157
it("sums the prices of its line items", _ {
52-
Order order();
58+
Order order;
5359

54-
order.add_entry(LineItem().set_item(Item()
55-
.set_price(Money(1.11, Money::USD))
56-
));
60+
order.add_entry(LineItem().set_item(Item()
61+
.set_price(Money(1.11, Money::USD))
62+
));
5763

58-
order.add_entry(LineItem().set_item(Item()
59-
.set_price(Money(1.11, Money::USD))
60-
.set_quantity(2)
61-
));
64+
order.add_entry(LineItem().set_item(Item()
65+
.set_price(Money(1.11, Money::USD))
66+
.set_quantity(2)
67+
));
6268

63-
expect(order.total()).to_equal(Money(5.55, Money::USD));
69+
expect(order.total()).to_equal(Money(5.55, Money::USD));
6470
});
6571
});
6672

docs/index.md

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,51 @@ C++Spec is a behavior-driven development library with an RSpec-inspired DSL.
44

55
-------------------------------------------------------------------------------
66

7-
## Overview ##
7+
## Overview
88

9-
C++Spec is a behavior-driven development library for C++ with an RSpec-inspired DSL. Designed with ease of use and rapid prototyping in mind, C++Spec offers an alternative to traditional testing libraries and frameworks.
9+
C++Spec is a behavior-driven development library for C++ with an RSpec-inspired DSL. Designed
10+
with ease of use and rapid prototyping in mind, C++Spec offers an alternative to traditional
11+
testing libraries and frameworks.
1012

11-
Also see the [official GitHub pages site](http://toroidal-code.github.io/cppspec/) for more
13+
Also see the [official GitHub pages site](http://toroidal-code.github.io/cppspec/) for more
1214
information.
1315

14-
## Installing ##
16+
## Requirements
1517

16-
Either run `git clone https://github.com/toroidal-code/cppspec.git` or download the collated header
17-
file (if it is available). You can also download a ZIP version of the repo.
18+
C++Spec requires a compiler and standard library with C++23 support. Currently tested:
1819

19-
If you want to manually generate the collated `cppspec.hpp`, you can download the ccollate tool [here](https://raw.githubusercontent.com/toroidal-code/ccollate/master/ccollate.rb) and then run `./ccollate.rb include/cppspec.hpp > cppspec.hpp` in the
20-
toplevel directory of the C++Spec repo. A `cppspec.hpp` file will then be
21-
available in the root of the project for copying.
20+
- LLVM/Clang 18 (Linux, macOS, Windows)
21+
- GCC 14.2 (Linux, macOS)
22+
- MSVC 19.43 (Windows)
23+
- AppleClang 16 (macOS)
24+
25+
## Installing
26+
27+
The recommended approach is to integrate C++Spec as a CMake subproject:
28+
29+
```cmake
30+
include(FetchContent)
31+
FetchContent_Declare(
32+
cppspec
33+
GIT_REPOSITORY https://github.com/toroidal-code/cppspec
34+
GIT_TAG VERSION
35+
)
36+
FetchContent_MakeAvailable(cppspec)
37+
```
38+
39+
Spec files are picked up automatically with:
40+
41+
```cmake
42+
discover_specs(specs_folder)
43+
```
44+
45+
This creates a separate CTest executable for every file ending in `_spec.cpp` in the given
46+
directory (recursive).
47+
48+
Alternatively, clone the repository and add the `include/` directory to your include path:
49+
50+
```sh
51+
git clone https://github.com/toroidal-code/cppspec.git
52+
```
53+
54+
Then `#include "cppspec.hpp"` in your spec files.

docs/syntax/before_after.md

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
# before_each, after_each, before_all, after_all
2+
3+
C++Spec provides four lifecycle hooks to manage setup and teardown around examples.
4+
5+
## before_each
6+
7+
Runs once **before every `it`** in the enclosing `describe` or `context`:
8+
9+
```cpp
10+
namespace { int n = 0; }
11+
12+
describe lifecycle_spec("lifecycle", $ {
13+
before_each([] { n = 0; });
14+
15+
it("starts at zero", _ {
16+
expect(n).to_equal(0);
17+
});
18+
19+
it("can be set to 5", _ {
20+
n = 5;
21+
expect(n).to_equal(5);
22+
});
23+
24+
it("is zero again (reset by before_each)", _ {
25+
expect(n).to_equal(0);
26+
});
27+
});
28+
```
29+
30+
## after_each
31+
32+
Runs once **after every `it`** in the enclosing block. Useful for releasing resources or
33+
asserting post-conditions:
34+
35+
```cpp
36+
namespace { std::FILE* f = nullptr; }
37+
38+
describe file_spec("File handle", $ {
39+
before_each([] { f = std::tmpfile(); });
40+
after_each([] { if (f) { std::fclose(f); f = nullptr; } });
41+
42+
it("is open after setup", _ {
43+
expect(f).not_().to_be_null();
44+
});
45+
});
46+
```
47+
48+
## before_all
49+
50+
Runs **once** before the first `it` in the enclosing block. Unlike `before_each`, it does
51+
**not** re-run between examples — mutations made inside examples persist:
52+
53+
```cpp
54+
namespace { int init_count = 0; }
55+
56+
describe before_all_spec("Expensive setup", $ {
57+
before_all([] { init_count = 42; });
58+
59+
it("sees the value set by before_all", _ {
60+
expect(init_count).to_equal(42);
61+
});
62+
63+
it("mutations persist (before_all does not re-run)", _ {
64+
init_count = 99;
65+
expect(init_count).to_equal(99);
66+
});
67+
68+
it("sees the mutated value from the previous it", _ {
69+
expect(init_count).to_equal(99);
70+
});
71+
});
72+
```
73+
74+
Use `before_all` only when setup is genuinely expensive and examples do not mutate shared
75+
state, or when mutation is intentional (as above).
76+
77+
## after_all
78+
79+
Runs **once** after the last `it` in the enclosing block:
80+
81+
```cpp
82+
namespace { int x = 0; }
83+
84+
describe after_all_spec("Timing", $ {
85+
context("inner context", _ {
86+
after_all([] { x = 777; });
87+
88+
it("x is still 0 while its run", _ { expect(x).to_equal(0); });
89+
it("x is still 0 here too", _ { expect(x).to_equal(0); });
90+
// after_all fires here → x = 777
91+
});
92+
93+
it("outer it sees x == 777 after inner context completed", _ {
94+
expect(x).to_equal(777);
95+
});
96+
});
97+
```
98+
99+
## Hook inheritance in nested contexts
100+
101+
Hooks defined in a parent `describe` or `context` run for all `it` blocks in child contexts
102+
as well. Additional hooks registered in a child context run **after** the parent's, stacking
103+
up:
104+
105+
```cpp
106+
namespace { int total = 0; }
107+
108+
describe stacked_spec("Stacked hooks", $ {
109+
before_each([] { total = 0; total += 1; }); // runs first: total = 1
110+
111+
context("inner", _ {
112+
before_each([] { total += 10; }); // runs second: total = 11
113+
114+
it("sees 11", _ {
115+
expect(total).to_equal(11);
116+
});
117+
});
118+
119+
it("outer it sees only 1 (inner before_each does not apply)", _ {
120+
expect(total).to_equal(1);
121+
});
122+
});
123+
```
124+
125+
Hook execution order for a nested `it`:
126+
127+
1. Parent `before_each` hooks (outermost first)
128+
2. Child `before_each` hooks
129+
3. The `it` body runs
130+
4. Child `after_each` hooks
131+
5. Parent `after_each` hooks (outermost last)

0 commit comments

Comments
 (0)