Contributing
Thank you for your interest in contributing to fluent-asserts! This guide will help you get started.
Getting Started
What You Need
- A D compiler (DMD, LDC, or GDC)
- The DUB package manager
- Git
- Node.js (for documentation work)
Clone the Repository
First, get a copy of the project on your computer.
git clone https://github.com/gedaiu/fluent-asserts.gitcd fluent-assertsBuild and Test
Next, build the library and run the tests to make sure everything is working.
# Build the librarydub build
# Run testsdub test
# Run tests with a specific compilerdub test --compiler=ldc2Project Structure
Here is how the project is organized in v2:
fluent-asserts/ source/ fluent/ asserts.d # The main file you import in your project fluentasserts/ core/ base.d # Re-exports and Assert struct expect.d # The main Expect struct and fluent API evaluator.d # Evaluator structs that execute assertions lifecycle.d # Lifecycle singleton and statistics config.d # FluentAssertsConfig settings listcomparison.d # Helpers for comparing lists evaluation/ # Evaluation pipeline eval.d # Evaluation struct and print methods value.d # ValueEvaluation struct equable.d # Type comparison helpers types.d # Type detection utilities constraints.d # Constraint checking memory/ # @nogc memory management heapstring.d # HeapString for @nogc strings heapequable.d # HeapEquableValue for comparisons process.d # Platform memory tracking typenamelist.d # Type name storage diff/ # Myers diff algorithm conversion/ # Type conversion utilities operations/ # All assertion operations registry.d # Operation registry snapshot.d # Snapshot testing comparison/ # greaterThan, lessThan, between, approximately equality/ # equal, arrayEqual exception/ # throwException, throwAnyException memory/ # allocateGCMemory, allocateNonGCMemory string/ # contain, startWith, endWith type/ # beNull, instanceOf results/ # Result formatting and output asserts.d # AssertResult struct message.d # Message building printer.d # Output printing formatting.d # Value formatting source/ # Source code extraction serializers/ # Type serialization heap_registry.d # HeapSerializerRegistry (@nogc) stringprocessing.d # String processing utilities docs/ # Documentation website (Starlight)Key Concepts for Contributors
Memory Management
fluent-asserts v2 uses manual memory management to reduce GC pressure:
- HeapString - Reference-counted string type with Small Buffer Optimization
- HeapEquableValue - Stores values for comparison without GC
- FixedAppender - Fixed-size buffer for building strings
When writing new code, prefer these types over regular D strings in hot paths. See Memory Management for details.
The Evaluation Pipeline
Assertions flow through this pipeline:
expect(value)creates anExpectstruct- Chain methods (
.to,.be,.not) modify state - Terminal operations (
.equal(),.contain()) trigger evaluation Evaluatorexecutes the operation and handles results- On failure,
Lifecycleformats and throws the exception
Adding a New Operation
To add a new assertion operation:
1. Create the Operation Function
Create a new file in the appropriate operations/ subfolder:
module fluentasserts.operations.myCategory.myOperation;
import fluentasserts.core.evaluation.eval : Evaluation;
/// Asserts that the value satisfies some condition.void myOperation(ref Evaluation evaluation) @safe nothrow { // 1. Get values (use [] to access HeapString content) auto actual = evaluation.currentValue.strValue[]; auto expected = evaluation.expectedValue.strValue[];
// 2. Perform the check auto isSuccess = /* your logic here */;
// 3. Handle negation (.not) if (evaluation.isNegated) { isSuccess = !isSuccess; }
// 4. Set error messages on failure (use .put() for FixedAppender) if (!isSuccess) { evaluation.result.expected.put("description of what was expected"); evaluation.result.actual.put(actual); }}2. Add to Expect Struct
Add the method to expect.d:
auto myOperation(T)(T expected) { _evaluation.addOperationName("myOperation"); // Set up expected value... return Evaluator(_evaluation, &myOperationOp);}3. Write Tests
@("myOperation returns success when condition is met")unittest { auto evaluation = ({ expect(actualValue).to.myOperation(expectedValue); }).recordEvaluation;
// Check the results (use [] for FixedAppender content) expect(evaluation.result.expected[]).to.equal("..."); expect(evaluation.result.actual[]).to.equal("...");}4. Add Documentation
Create a documentation page in docs/src/content/docs/api/myCategory/myOperation.mdx.
Writing Tests
Use recordEvaluation to test assertion behavior without throwing:
@("equal returns expected and actual on mismatch")unittest { auto evaluation = ({ expect(5).to.equal(10); }).recordEvaluation;
expect(evaluation.result.expected[]).to.equal("10"); expect(evaluation.result.actual[]).to.equal("5");}
@("equal passes when values match")unittest { auto evaluation = ({ expect(42).to.equal(42); }).recordEvaluation;
// No failure means empty expected/actual expect(evaluation.result.expected[]).to.beEmpty();}Documentation
The documentation website is built with Starlight.
Local Development
cd docsnpm installnpm run devVisit http://localhost:4321 to see the site.
Documentation Structure
docs/src/content/docs/guide/- User guides and tutorialsdocs/src/content/docs/api/- API reference pagesdocs/src/content/docs/index.mdx- Landing page
Writing Documentation
- Use clear, concise language
- Include code examples that can be copy-pasted
- Link to related pages
- Update the upgrade guide if adding breaking changes
Code Style
General Guidelines
- Use
@safe nothrowfor all operation functions - Use
@nogcwhere possible for memory-sensitive code - Follow D naming conventions:
camelCasefor functions,PascalCasefor types - Add doc comments (
///) to public functions and types
Specific to v2
- Access
HeapStringcontent with[]slice operator - Use
FixedAppender.put()instead of string assignment - Prefer
HeapSerializerRegistryfor new serializers - Keep operations focused on a single check
Submitting Changes
- Fork the repository on GitHub
- Create a feature branch:
git checkout -b feature/my-feature - Make your changes
- Run tests:
dub test - Commit with a clear message describing the change
- Push and create a Pull Request
Pull Request Guidelines
- Describe what the PR does and why
- Reference any related issues
- Include tests for new functionality
- Update documentation if needed
- Keep changes focused - one feature per PR
Questions?
- Open an issue on GitHub
- Check existing issues for similar questions
- See Core Concepts for architecture details
- See Extending for custom operation examples