agentic-team-templates 0.13.1 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -1
- package/package.json +1 -1
- package/src/index.js +22 -2
- package/src/index.test.js +5 -0
- package/templates/cpp-expert/.cursorrules/concurrency.md +211 -0
- package/templates/cpp-expert/.cursorrules/error-handling.md +170 -0
- package/templates/cpp-expert/.cursorrules/memory-and-ownership.md +220 -0
- package/templates/cpp-expert/.cursorrules/modern-cpp.md +211 -0
- package/templates/cpp-expert/.cursorrules/overview.md +87 -0
- package/templates/cpp-expert/.cursorrules/performance.md +223 -0
- package/templates/cpp-expert/.cursorrules/testing.md +230 -0
- package/templates/cpp-expert/.cursorrules/tooling.md +312 -0
- package/templates/cpp-expert/CLAUDE.md +242 -0
- package/templates/csharp-expert/.cursorrules/aspnet-core.md +311 -0
- package/templates/csharp-expert/.cursorrules/async-patterns.md +206 -0
- package/templates/csharp-expert/.cursorrules/dependency-injection.md +206 -0
- package/templates/csharp-expert/.cursorrules/error-handling.md +235 -0
- package/templates/csharp-expert/.cursorrules/language-features.md +204 -0
- package/templates/csharp-expert/.cursorrules/overview.md +92 -0
- package/templates/csharp-expert/.cursorrules/performance.md +251 -0
- package/templates/csharp-expert/.cursorrules/testing.md +282 -0
- package/templates/csharp-expert/.cursorrules/tooling.md +254 -0
- package/templates/csharp-expert/CLAUDE.md +360 -0
- package/templates/java-expert/.cursorrules/concurrency.md +209 -0
- package/templates/java-expert/.cursorrules/error-handling.md +205 -0
- package/templates/java-expert/.cursorrules/modern-java.md +216 -0
- package/templates/java-expert/.cursorrules/overview.md +81 -0
- package/templates/java-expert/.cursorrules/performance.md +239 -0
- package/templates/java-expert/.cursorrules/persistence.md +262 -0
- package/templates/java-expert/.cursorrules/spring-boot.md +262 -0
- package/templates/java-expert/.cursorrules/testing.md +272 -0
- package/templates/java-expert/.cursorrules/tooling.md +301 -0
- package/templates/java-expert/CLAUDE.md +325 -0
- package/templates/javascript-expert/.cursorrules/overview.md +5 -3
- package/templates/javascript-expert/.cursorrules/typescript-deep-dive.md +348 -0
- package/templates/javascript-expert/CLAUDE.md +34 -3
- package/templates/kotlin-expert/.cursorrules/coroutines.md +237 -0
- package/templates/kotlin-expert/.cursorrules/error-handling.md +149 -0
- package/templates/kotlin-expert/.cursorrules/frameworks.md +227 -0
- package/templates/kotlin-expert/.cursorrules/language-features.md +231 -0
- package/templates/kotlin-expert/.cursorrules/overview.md +77 -0
- package/templates/kotlin-expert/.cursorrules/performance.md +185 -0
- package/templates/kotlin-expert/.cursorrules/testing.md +213 -0
- package/templates/kotlin-expert/.cursorrules/tooling.md +258 -0
- package/templates/kotlin-expert/CLAUDE.md +276 -0
- package/templates/swift-expert/.cursorrules/concurrency.md +230 -0
- package/templates/swift-expert/.cursorrules/error-handling.md +213 -0
- package/templates/swift-expert/.cursorrules/language-features.md +246 -0
- package/templates/swift-expert/.cursorrules/overview.md +88 -0
- package/templates/swift-expert/.cursorrules/performance.md +260 -0
- package/templates/swift-expert/.cursorrules/swiftui.md +260 -0
- package/templates/swift-expert/.cursorrules/testing.md +286 -0
- package/templates/swift-expert/.cursorrules/tooling.md +285 -0
- package/templates/swift-expert/CLAUDE.md +275 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
# C++ Performance
|
|
2
|
+
|
|
3
|
+
Measure first. Understand the hardware. Zero-cost abstractions are the goal.
|
|
4
|
+
|
|
5
|
+
## Profile Before Optimizing
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# perf (Linux)
|
|
9
|
+
perf record -g ./myapp
|
|
10
|
+
perf report
|
|
11
|
+
|
|
12
|
+
# Valgrind callgrind
|
|
13
|
+
valgrind --tool=callgrind ./myapp
|
|
14
|
+
kcachegrind callgrind.out.*
|
|
15
|
+
|
|
16
|
+
# Google Benchmark for micro-benchmarks
|
|
17
|
+
./benchmarks --benchmark_format=console --benchmark_min_time=2s
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### Tools
|
|
21
|
+
|
|
22
|
+
| Tool | Purpose |
|
|
23
|
+
|------|---------|
|
|
24
|
+
| perf | CPU profiling, cache analysis |
|
|
25
|
+
| Valgrind/Callgrind | Instruction-level profiling |
|
|
26
|
+
| Google Benchmark | Micro-benchmarks |
|
|
27
|
+
| Compiler Explorer | Assembly inspection |
|
|
28
|
+
| heaptrack | Heap allocation profiling |
|
|
29
|
+
| Tracy | Frame profiler (games/real-time) |
|
|
30
|
+
|
|
31
|
+
## Cache-Friendly Data Structures
|
|
32
|
+
|
|
33
|
+
```cpp
|
|
34
|
+
// Structure of Arrays (SoA) vs Array of Structures (AoS)
|
|
35
|
+
|
|
36
|
+
// AoS — bad cache utilization when iterating one field
|
|
37
|
+
struct Particle {
|
|
38
|
+
float x, y, z;
|
|
39
|
+
float vx, vy, vz;
|
|
40
|
+
float mass;
|
|
41
|
+
int type;
|
|
42
|
+
};
|
|
43
|
+
std::vector<Particle> particles; // Accesses mass? Loads x,y,z,vx,vy,vz too
|
|
44
|
+
|
|
45
|
+
// SoA — excellent cache utilization for field-wise iteration
|
|
46
|
+
struct Particles {
|
|
47
|
+
std::vector<float> x, y, z;
|
|
48
|
+
std::vector<float> vx, vy, vz;
|
|
49
|
+
std::vector<float> mass;
|
|
50
|
+
std::vector<int> type;
|
|
51
|
+
};
|
|
52
|
+
// Iterating mass only touches mass data — no waste
|
|
53
|
+
|
|
54
|
+
// Rule: profile first. SoA matters for hot loops over large datasets.
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Allocation Avoidance
|
|
58
|
+
|
|
59
|
+
```cpp
|
|
60
|
+
// Pre-allocate containers
|
|
61
|
+
std::vector<Item> items;
|
|
62
|
+
items.reserve(expected_count); // One allocation instead of many
|
|
63
|
+
|
|
64
|
+
// Small buffer optimization (SBO)
|
|
65
|
+
// std::string already uses SBO (typically 15-22 chars inline)
|
|
66
|
+
// std::function uses SBO for small callables
|
|
67
|
+
|
|
68
|
+
// Stack allocation for temporary buffers
|
|
69
|
+
std::array<char, 256> buffer; // Stack, not heap
|
|
70
|
+
|
|
71
|
+
// Object pools for frequent allocation/deallocation
|
|
72
|
+
template <typename T>
|
|
73
|
+
class ObjectPool {
|
|
74
|
+
std::vector<std::unique_ptr<T>> pool_;
|
|
75
|
+
std::vector<T*> available_;
|
|
76
|
+
|
|
77
|
+
public:
|
|
78
|
+
auto acquire() -> T* {
|
|
79
|
+
if (available_.empty()) {
|
|
80
|
+
pool_.push_back(std::make_unique<T>());
|
|
81
|
+
return pool_.back().get();
|
|
82
|
+
}
|
|
83
|
+
auto* obj = available_.back();
|
|
84
|
+
available_.pop_back();
|
|
85
|
+
return obj;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
void release(T* obj) {
|
|
89
|
+
available_.push_back(obj);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Move Semantics for Performance
|
|
95
|
+
|
|
96
|
+
```cpp
|
|
97
|
+
// Move instead of copy for expensive objects
|
|
98
|
+
std::vector<std::string> build_names(std::span<const User> users) {
|
|
99
|
+
std::vector<std::string> names;
|
|
100
|
+
names.reserve(users.size());
|
|
101
|
+
for (const auto& user : users) {
|
|
102
|
+
names.push_back(user.name()); // Copy (user is const)
|
|
103
|
+
}
|
|
104
|
+
return names; // NRVO — no copy, no move
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Sink parameters: take by value and move
|
|
108
|
+
class Widget {
|
|
109
|
+
std::string name_;
|
|
110
|
+
public:
|
|
111
|
+
explicit Widget(std::string name) : name_{std::move(name)} {}
|
|
112
|
+
// Caller can copy or move into the parameter
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
// Emplace instead of push_back to construct in-place
|
|
116
|
+
items.emplace_back(arg1, arg2); // Constructs directly in vector
|
|
117
|
+
items.push_back(Item{arg1, arg2}); // Constructs then moves
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## constexpr Computation
|
|
121
|
+
|
|
122
|
+
```cpp
|
|
123
|
+
// Move computation to compile time
|
|
124
|
+
constexpr auto factorial(int n) -> int {
|
|
125
|
+
int result = 1;
|
|
126
|
+
for (int i = 2; i <= n; ++i) result *= i;
|
|
127
|
+
return result;
|
|
128
|
+
}
|
|
129
|
+
static_assert(factorial(5) == 120);
|
|
130
|
+
|
|
131
|
+
// Compile-time lookup tables
|
|
132
|
+
constexpr auto build_crc_table() {
|
|
133
|
+
std::array<uint32_t, 256> table{};
|
|
134
|
+
for (uint32_t i = 0; i < 256; ++i) {
|
|
135
|
+
uint32_t crc = i;
|
|
136
|
+
for (int j = 0; j < 8; ++j) {
|
|
137
|
+
crc = (crc >> 1) ^ ((crc & 1) ? 0xEDB88320 : 0);
|
|
138
|
+
}
|
|
139
|
+
table[i] = crc;
|
|
140
|
+
}
|
|
141
|
+
return table;
|
|
142
|
+
}
|
|
143
|
+
constexpr auto crc_table = build_crc_table(); // Computed at compile time
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## SIMD and Vectorization
|
|
147
|
+
|
|
148
|
+
```cpp
|
|
149
|
+
// Help the compiler auto-vectorize
|
|
150
|
+
// 1. Use contiguous containers (vector, array)
|
|
151
|
+
// 2. Simple loop bodies without branches
|
|
152
|
+
// 3. No pointer aliasing (use __restrict or std::span)
|
|
153
|
+
|
|
154
|
+
void add_vectors(float* __restrict out,
|
|
155
|
+
const float* __restrict a,
|
|
156
|
+
const float* __restrict b,
|
|
157
|
+
size_t n) {
|
|
158
|
+
for (size_t i = 0; i < n; ++i) {
|
|
159
|
+
out[i] = a[i] + b[i]; // Auto-vectorized by compiler
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Verify with: -fopt-info-vec (GCC) or -Rpass=loop-vectorize (Clang)
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## String Performance
|
|
167
|
+
|
|
168
|
+
```cpp
|
|
169
|
+
// std::string_view for read-only string operations — zero copies
|
|
170
|
+
void process(std::string_view input) {
|
|
171
|
+
auto prefix = input.substr(0, 5); // No allocation
|
|
172
|
+
if (input.starts_with("http")) { /* ... */ }
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Avoid string concatenation in loops
|
|
176
|
+
std::string result;
|
|
177
|
+
result.reserve(total_estimated_size);
|
|
178
|
+
for (const auto& item : items) {
|
|
179
|
+
result.append(item.name());
|
|
180
|
+
result.push_back(',');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// fmt::format or std::format over stringstream
|
|
184
|
+
auto msg = fmt::format("User {} logged in from {}", name, ip);
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Compiler Optimization Flags
|
|
188
|
+
|
|
189
|
+
```cmake
|
|
190
|
+
# Release build
|
|
191
|
+
target_compile_options(${PROJECT_NAME} PRIVATE
|
|
192
|
+
$<$<CONFIG:Release>:-O3 -DNDEBUG -march=native>
|
|
193
|
+
$<$<CONFIG:Debug>:-O0 -g>
|
|
194
|
+
$<$<CONFIG:RelWithDebInfo>:-O2 -g -DNDEBUG>
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
# Link-Time Optimization
|
|
198
|
+
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE ON)
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## Anti-Patterns
|
|
202
|
+
|
|
203
|
+
```cpp
|
|
204
|
+
// Never: premature optimization without profiling
|
|
205
|
+
// "I think this allocation is slow" — prove it
|
|
206
|
+
|
|
207
|
+
// Never: virtual function calls in hot loops (unless measured)
|
|
208
|
+
for (auto& item : million_items) {
|
|
209
|
+
item->process(); // vtable lookup every iteration
|
|
210
|
+
}
|
|
211
|
+
// Consider: CRTP, std::variant, or batch processing
|
|
212
|
+
|
|
213
|
+
// Never: std::map when std::unordered_map or sorted vector suffices
|
|
214
|
+
std::map<std::string, int> counts; // O(log n) lookup, poor cache locality
|
|
215
|
+
// Use: std::unordered_map or flat_map (C++23)
|
|
216
|
+
|
|
217
|
+
// Never: shared_ptr for performance-critical paths
|
|
218
|
+
// Atomic reference count operations are expensive
|
|
219
|
+
// Use unique_ptr or raw non-owning pointers
|
|
220
|
+
|
|
221
|
+
// Never: exceptions in hot paths
|
|
222
|
+
// Exceptions are zero-cost on the happy path but expensive when thrown
|
|
223
|
+
```
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
# C++ Testing
|
|
2
|
+
|
|
3
|
+
Test behavior. Test edge cases. Test with sanitizers. No excuses.
|
|
4
|
+
|
|
5
|
+
## Framework Stack
|
|
6
|
+
|
|
7
|
+
| Tool | Purpose |
|
|
8
|
+
|------|---------|
|
|
9
|
+
| Google Test | Test framework |
|
|
10
|
+
| Google Mock | Mocking |
|
|
11
|
+
| Catch2 | Alternative test framework (header-only) |
|
|
12
|
+
| Google Benchmark | Micro-benchmarks |
|
|
13
|
+
| ASan | Address sanitizer (memory errors) |
|
|
14
|
+
| UBSan | Undefined behavior sanitizer |
|
|
15
|
+
| TSan | Thread sanitizer (data races) |
|
|
16
|
+
| MSan | Memory sanitizer (uninitialized reads) |
|
|
17
|
+
| Valgrind | Memory leak detection |
|
|
18
|
+
|
|
19
|
+
## Test Structure (Google Test)
|
|
20
|
+
|
|
21
|
+
```cpp
|
|
22
|
+
class OrderServiceTest : public ::testing::Test {
|
|
23
|
+
protected:
|
|
24
|
+
void SetUp() override {
|
|
25
|
+
repo_ = std::make_unique<MockOrderRepository>();
|
|
26
|
+
inventory_ = std::make_unique<MockInventoryClient>();
|
|
27
|
+
sut_ = std::make_unique<OrderService>(*repo_, *inventory_);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
std::unique_ptr<MockOrderRepository> repo_;
|
|
31
|
+
std::unique_ptr<MockInventoryClient> inventory_;
|
|
32
|
+
std::unique_ptr<OrderService> sut_;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
TEST_F(OrderServiceTest, Create_WithValidItems_ReturnsOrder) {
|
|
36
|
+
// Arrange
|
|
37
|
+
EXPECT_CALL(*inventory_, check_availability("SKU-001", 2))
|
|
38
|
+
.WillOnce(Return(true));
|
|
39
|
+
EXPECT_CALL(*repo_, save(testing::_))
|
|
40
|
+
.WillOnce(Return(Order{.id = "order-1"}));
|
|
41
|
+
|
|
42
|
+
// Act
|
|
43
|
+
auto result = sut_->create(CreateOrderRequest{
|
|
44
|
+
.customer_id = "customer-1",
|
|
45
|
+
.items = {{"SKU-001", 2}}
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// Assert
|
|
49
|
+
ASSERT_TRUE(result.has_value());
|
|
50
|
+
EXPECT_EQ(result->customer_id, "customer-1");
|
|
51
|
+
EXPECT_EQ(result->items.size(), 1);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
TEST_F(OrderServiceTest, Create_WithInsufficientInventory_ReturnsError) {
|
|
55
|
+
EXPECT_CALL(*inventory_, check_availability("SKU-001", 100))
|
|
56
|
+
.WillOnce(Return(false));
|
|
57
|
+
|
|
58
|
+
auto result = sut_->create(CreateOrderRequest{
|
|
59
|
+
.customer_id = "customer-1",
|
|
60
|
+
.items = {{"SKU-001", 100}}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
ASSERT_FALSE(result.has_value());
|
|
64
|
+
EXPECT_EQ(result.error(), OrderError::insufficient_inventory);
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Catch2 Alternative
|
|
69
|
+
|
|
70
|
+
```cpp
|
|
71
|
+
#include <catch2/catch_test_macros.hpp>
|
|
72
|
+
#include <catch2/matchers/catch_matchers_string.hpp>
|
|
73
|
+
|
|
74
|
+
TEST_CASE("Parser handles edge cases", "[parser]") {
|
|
75
|
+
SECTION("empty input returns error") {
|
|
76
|
+
auto result = parse("");
|
|
77
|
+
REQUIRE_FALSE(result.has_value());
|
|
78
|
+
REQUIRE(result.error() == ParseError::empty_input);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
SECTION("valid input returns parsed value") {
|
|
82
|
+
auto result = parse("42");
|
|
83
|
+
REQUIRE(result.has_value());
|
|
84
|
+
REQUIRE(*result == 42);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
SECTION("overflow returns error") {
|
|
88
|
+
auto result = parse("999999999999999999");
|
|
89
|
+
REQUIRE_FALSE(result.has_value());
|
|
90
|
+
REQUIRE(result.error() == ParseError::out_of_range);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Parameterized Tests
|
|
96
|
+
|
|
97
|
+
```cpp
|
|
98
|
+
struct ParseTestCase {
|
|
99
|
+
std::string input;
|
|
100
|
+
std::expected<int, ParseError> expected;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
class ParseTest : public ::testing::TestWithParam<ParseTestCase> {};
|
|
104
|
+
|
|
105
|
+
TEST_P(ParseTest, ParsesCorrectly) {
|
|
106
|
+
auto [input, expected] = GetParam();
|
|
107
|
+
auto result = parse(input);
|
|
108
|
+
|
|
109
|
+
if (expected.has_value()) {
|
|
110
|
+
ASSERT_TRUE(result.has_value()) << "Input: " << input;
|
|
111
|
+
EXPECT_EQ(*result, *expected);
|
|
112
|
+
} else {
|
|
113
|
+
ASSERT_FALSE(result.has_value()) << "Input: " << input;
|
|
114
|
+
EXPECT_EQ(result.error(), expected.error());
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
INSTANTIATE_TEST_SUITE_P(ParseTests, ParseTest, ::testing::Values(
|
|
119
|
+
ParseTestCase{"42", 42},
|
|
120
|
+
ParseTestCase{"-1", -1},
|
|
121
|
+
ParseTestCase{"", std::unexpected{ParseError::empty_input}},
|
|
122
|
+
ParseTestCase{"abc", std::unexpected{ParseError::invalid_format}}
|
|
123
|
+
));
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Mocking
|
|
127
|
+
|
|
128
|
+
```cpp
|
|
129
|
+
class MockDatabase : public Database {
|
|
130
|
+
public:
|
|
131
|
+
MOCK_METHOD(std::optional<User>, find_user, (std::string_view email), (const, override));
|
|
132
|
+
MOCK_METHOD(void, save_user, (const User& user), (override));
|
|
133
|
+
MOCK_METHOD(bool, delete_user, (std::string_view id), (override));
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
// Expectations
|
|
137
|
+
EXPECT_CALL(mock_db, find_user("alice@example.com"))
|
|
138
|
+
.Times(1)
|
|
139
|
+
.WillOnce(Return(User{.name = "Alice", .email = "alice@example.com"}));
|
|
140
|
+
|
|
141
|
+
// Matchers
|
|
142
|
+
EXPECT_CALL(mock_db, save_user(
|
|
143
|
+
Field(&User::email, HasSubstr("@example.com"))))
|
|
144
|
+
.Times(AtLeast(1));
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Sanitizers
|
|
148
|
+
|
|
149
|
+
```cmake
|
|
150
|
+
# CMake options for sanitizers
|
|
151
|
+
option(ENABLE_SANITIZERS "Enable ASan and UBSan" OFF)
|
|
152
|
+
|
|
153
|
+
if(ENABLE_SANITIZERS)
|
|
154
|
+
target_compile_options(${PROJECT_NAME} PRIVATE
|
|
155
|
+
-fsanitize=address,undefined
|
|
156
|
+
-fno-omit-frame-pointer)
|
|
157
|
+
target_link_options(${PROJECT_NAME} PRIVATE
|
|
158
|
+
-fsanitize=address,undefined)
|
|
159
|
+
endif()
|
|
160
|
+
|
|
161
|
+
# Thread sanitizer (separate — incompatible with ASan)
|
|
162
|
+
option(ENABLE_TSAN "Enable Thread Sanitizer" OFF)
|
|
163
|
+
if(ENABLE_TSAN)
|
|
164
|
+
target_compile_options(${PROJECT_NAME} PRIVATE -fsanitize=thread)
|
|
165
|
+
target_link_options(${PROJECT_NAME} PRIVATE -fsanitize=thread)
|
|
166
|
+
endif()
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
# Run with sanitizers
|
|
171
|
+
cmake -B build -DENABLE_SANITIZERS=ON
|
|
172
|
+
cmake --build build
|
|
173
|
+
ctest --test-dir build
|
|
174
|
+
|
|
175
|
+
# Thread sanitizer (separate build)
|
|
176
|
+
cmake -B build-tsan -DENABLE_TSAN=ON
|
|
177
|
+
cmake --build build-tsan
|
|
178
|
+
ctest --test-dir build-tsan
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Benchmarks (Google Benchmark)
|
|
182
|
+
|
|
183
|
+
```cpp
|
|
184
|
+
#include <benchmark/benchmark.h>
|
|
185
|
+
|
|
186
|
+
static void BM_VectorPushBack(benchmark::State& state) {
|
|
187
|
+
for (auto _ : state) {
|
|
188
|
+
std::vector<int> v;
|
|
189
|
+
for (int i = 0; i < state.range(0); ++i) {
|
|
190
|
+
v.push_back(i);
|
|
191
|
+
}
|
|
192
|
+
benchmark::DoNotOptimize(v);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
BENCHMARK(BM_VectorPushBack)->Range(8, 1 << 20);
|
|
196
|
+
|
|
197
|
+
static void BM_VectorReserved(benchmark::State& state) {
|
|
198
|
+
for (auto _ : state) {
|
|
199
|
+
std::vector<int> v;
|
|
200
|
+
v.reserve(static_cast<size_t>(state.range(0)));
|
|
201
|
+
for (int i = 0; i < state.range(0); ++i) {
|
|
202
|
+
v.push_back(i);
|
|
203
|
+
}
|
|
204
|
+
benchmark::DoNotOptimize(v);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
BENCHMARK(BM_VectorReserved)->Range(8, 1 << 20);
|
|
208
|
+
|
|
209
|
+
BENCHMARK_MAIN();
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## Anti-Patterns
|
|
213
|
+
|
|
214
|
+
```cpp
|
|
215
|
+
// Never: tests that depend on execution order
|
|
216
|
+
// Each test must be independent and self-contained
|
|
217
|
+
|
|
218
|
+
// Never: testing private implementation details
|
|
219
|
+
// Test public interface and observable behavior
|
|
220
|
+
|
|
221
|
+
// Never: ignoring sanitizer warnings
|
|
222
|
+
// Every ASan/UBSan/TSan finding is a real bug
|
|
223
|
+
|
|
224
|
+
// Never: skipping edge cases
|
|
225
|
+
// Test empty input, max values, boundary conditions, error paths
|
|
226
|
+
|
|
227
|
+
// Never: mocking everything
|
|
228
|
+
// Only mock external dependencies (I/O, network, time)
|
|
229
|
+
// Use real objects for in-process logic
|
|
230
|
+
```
|