Skip to content

Commit 85a2bee

Browse files
committed
Progress on tests and general code improvements
1 parent 241da0a commit 85a2bee

File tree

15 files changed

+234
-137
lines changed

15 files changed

+234
-137
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
A behavior-driven development testing library for C++ with an RSpec-inspired DSL.
99

10+
## Warning! This is pre-release software and may be incomplete, contain bugs, and/or introduce major breaking changes within a short period of time
11+
1012
## Installation ##
1113

1214
C++Spec will be released as a single collated header-file that can be placed in any include path in your project. After that, all features are available via `#include "cppspec.hpp"`.

docs/syntax/describe.md

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,63 @@
1+
Every test suite begins with either `describe` or `describe_a`.
12

2-
Every test suite begins with either `describe` or `describe_a`.
3-
4-
# describe #
3+
# describe
54

65
Describes have the form of:
76

87
```c++
98
describe example_spec("An example", $ { });
109
```
1110
12-
Each `describe` is a global instance of the `Description` class, the name of the spec being
13-
the name of the global variable that the test is contained in.
11+
Each `describe` is a global instance of the `Description` class, the name of the spec being the name of the global variable that the test is contained in.
1412
15-
__Important!__ Take note of the `$`. This is used whenever you write a `describe` or a `describe_a`.
13+
!!! important
1614
17-
In conventional C++, the above snippet would be written as:
15+
```
16+
Take note of the `$`. This is used whenever you write a `describe` or a `describe_a`.
17+
```
18+
19+
In conventional C++14, after macro-expansion the above snippet would be written as:
1820
1921
```c++
2022
Description example_spec("An example", [](&self auto) { });
2123
```
2224

23-
The `Description` constructor takes two arguments: a string, and a lambda. For simplicity's sake,
24-
any lambdas passed to any C++Spec functions are referred to as "blocks", as the capture-list
25-
and arguments of the lambda are effectively never seen.
25+
The `Description` constructor takes two arguments: a string, and a lambda. For simplicity's sake, any lambdas passed to any C++Spec functions are referred to as "blocks", as the capture-list and arguments of the lambda are effectively never seen.
2626

27-
# describe_a #
27+
# describe_a
2828

29-
A `describe_a` is more complex than `describe`.
29+
A `describe_a` is more complex than `describe`.
3030

31-
Unlike `describe` which creates instances of `Description`, `describe_a` creates instances of
32-
`ClassDescription`. `ClassDescription` is a template class, where the template's type variable
33-
is used to specialize the description and create a subject available to all statements in the
34-
description.
31+
Unlike `describe` which creates instances of `Description`, `describe_a` creates instances of `ClassDescription`. `ClassDescription` is a template class, where the template's type variable is used to specialize the description and create a subject available to all statements in the description. The subject is available via the `subject` keyword.
3532

3633
```c++
3734
template <typename T>
38-
class ClassDescription : public Description { };
35+
class ClassDescription : public Description { ... };
3936
```
4037
41-
Also unlike `describe`, there are two forms of `describe_a`: one where the subject is explicit,
42-
and another where it is implicit.
38+
Also unlike `describe`, there are two forms of `describe_a`: one where the subject is explicit, and another where it is implied.
39+
40+
## Explicit subject describe_a
41+
42+
An explicit describe_a has the subject passed into it as the first argument if there is no provided description, or after the description if there is one.
43+
44+
For example:
45+
46+
```c++
47+
describe_a <TestClass> tc_spec(TestClass(arg1, arg2), $ { ... });
48+
```
4349

44-
The explicit form:
50+
and
4551

4652
```c++
47-
describe_a <TestClass> tc_spec(TestClass(arg1, arg2), $ { });
53+
describe_an <AnotherTestClass> atc_spec
54+
("The class AnotherTest class", AnotherTestClass(args...), $ { ... });
4855
```
4956
50-
The implicit form:
57+
## Implied subject describe_a
5158
5259
```c++
53-
describe_a <AnotherTestClass> atc_spec($ { });
60+
describe_a <YetAnotherTestClass> yatc_spec( $ { ... });
5461
```
5562

63+
With an implied subject, the default constructor of the templated class is called to create the subject.

gh-pages/index.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,17 @@ layout: default
33
profile: true
44
---
55

6-
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. Some of the core concepts:
6+
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. Some things that make C++Spec different than other testing libraries:
77

88
- A clean, readable syntax
99
- As few macros as possibles
1010
- Use as a library, not a framework
1111
- Easily extensible with custom matchers.
1212
- Support for the RSpec and Jasmine constructs you'd expect, such as describe, context, it, expect, and let.
1313
- Can automatically generate documentation strings based on your tests
14+
- Cross-platform with no need to change complex build settings.
15+
16+
1417

1518
## An example:
1619

@@ -42,17 +45,17 @@ int_list_spec("A list of ints", {1,2,3}, $ {
4245
4346
## Usage:
4447
45-
Download the [header file]() and put it in your project either alongside your tests or in a folder that is in your `INCLUDE` path. Then, simply `#include "cppspec.hpp"` and you're ready to go.
48+
Download the [header file]() and put it in your project either alongside your tests or in a folder that is in your `INCLUDE` path. Then, simply `#include "cppspec.hpp"` and you're ready to go. Both user and API documentation is available at the top of this page, and a tutorial will soon be available.
4649
4750
## How does it work?
4851
4952
C++Spec utilizes templated classes and functions as well as C++14 features in order to automatically deduce the types of your objects and construct your tests.
5053
5154
Lambdas are passed to functions (such as `context` and `it`) in order to build an execution tree at runtime. A formatter object visits each node when the tests are run and prints the status of the tests and any errors.
5255
53-
## I'm getting really long errors. What's going on?
56+
## I'm getting really long compiler errors. What's going on?
5457
55-
Due to how the library is constructed with both templates and auto-type lambdas, error messages from the compiler can be difficult to understand. Errors also tend to cascade and make the enclosing objects also fail, further obfuscating what's actually going wrong.
58+
Due to how the library is constructed with both templates and type-deduced (auto) lambdas, error messages from the compiler can be difficult to understand. Errors also tend to cascade and make the structures above the problem code also fail to compile, further obfuscating what's actually going wrong.
5659
5760
Usually, the only information you want is the actual error, not all of the template substitution notes. You can reduce the template backtrace by using the flag `-ftemplate-backtrace-limit=1` when compiling with GCC and Clang.
5861

include/class_description.hpp

Lines changed: 61 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@ class ClassDescription : public Description {
5656
type(" : " + Util::demangle(typeid(T).name())),
5757
subject(subject) {}
5858

59-
ClassDescription(std::string descr, T &subject, block_t body)
60-
: Description(descr), body(body), subject(subject) {}
59+
// ClassDescription(std::string descr, T &subject, block_t body)
60+
// : Description(descr), body(body), subject(subject) {}
6161

6262
template <typename U>
6363
ClassDescription(std::initializer_list<U> init_list, block_t body)
@@ -78,10 +78,28 @@ class ClassDescription : public Description {
7878

7979
Result it(std::string descr, std::function<void(ItCd<T> &)> body);
8080
Result it(std::function<void(ItCd<T> &)> body);
81-
Result context(T subject, block_t body);
82-
Result context(T &subject, block_t body);
83-
Result context(block_t body);
81+
/** @brief an alias for it */
82+
Result specify(std::string descr, std::function<void(ItCd<T> &)> body) {
83+
return it(descr, body);
84+
}
85+
/** @brief an alias for it */
86+
Result specify(std::function<void(ItCd<T> &)> body) { return it(body); }
87+
88+
template <class U>
89+
Result context(std::string descr, U subject,
90+
std::function<void(ClassDescription<U> &)> body);
91+
template <class U>
92+
Result context(std::string descr, U &subject,
93+
std::function<void(ClassDescription<U> &)> body);
94+
template <class U>
95+
Result context(U subject, std::function<void(ClassDescription<U> &)> body);
96+
template <class U>
97+
Result context(U &subject, std::function<void(ClassDescription<U> &)> body);
98+
99+
Result context(std::string descr, std::function<void(ClassDescription<T> &)> body);
100+
84101
Result run(Formatters::BaseFormatter &printer) override;
102+
85103
std::string get_descr() override { return descr; }
86104
const std::string get_descr() const override { return descr; }
87105
std::string get_subject_type() override { return type; }
@@ -92,25 +110,47 @@ template <class T>
92110
using ClassContext = ClassDescription<T>;
93111

94112
template <class T>
113+
template <class U>
95114
Result ClassDescription<T>::context(
96-
T subject, std::function<void(ClassDescription &)> body) {
97-
ClassContext<T> context(subject, body);
115+
std::string descr, U subject,
116+
std::function<void(ClassDescription<U> &)> body) {
117+
ClassContext<U> context(descr, subject, body);
98118
context.set_parent(this);
99-
context.before_eaches = this->before_eaches;
100-
context.after_eaches = this->after_eaches;
119+
context.ClassContext<U>::before_eaches = this->before_eaches;
120+
context.ClassContext<U>::after_eaches = this->after_eaches;
101121
return context.run(this->get_formatter());
102122
}
103123

104124
template <class T>
125+
template <class U>
126+
Result ClassDescription<T>::context(
127+
U subject, std::function<void(ClassDescription<U> &)> body) {
128+
return context("", std::forward<U>(subject), body);
129+
}
130+
131+
template <class T>
132+
template <class U>
105133
Result ClassDescription<T>::context(
106-
T &subject, std::function<void(ClassDescription &)> body) {
107-
return context(subject, body);
134+
std::string descr, U &subject,
135+
std::function<void(ClassDescription<U> &)> body) {
136+
ClassContext<U> context(descr, subject, body);
137+
context.set_parent(this);
138+
context.ClassContext<U>::before_eaches = this->before_eaches;
139+
context.ClassContext<U>::after_eaches = this->after_eaches;
140+
return context.run(this->get_formatter());
108141
}
109142

110143
template <class T>
144+
template <class U>
111145
Result ClassDescription<T>::context(
112-
std::function<void(ClassDescription &)> body) {
113-
ClassContext<T> context(body);
146+
U &subject, std::function<void(ClassDescription<U> &)> body) {
147+
return context("", std::forward<U>(subject), body);
148+
}
149+
150+
template <class T>
151+
Result ClassDescription<T>::context(std::string descr,
152+
std::function<void(ClassDescription<T> &)> body) {
153+
ClassContext<T> context(descr, this->subject, body);
114154
context.set_parent(this);
115155
context.before_eaches = this->before_eaches;
116156
context.after_eaches = this->after_eaches;
@@ -120,14 +160,20 @@ Result ClassDescription<T>::context(
120160
template <class T>
121161
Result Description::context(T subject,
122162
std::function<void(ClassDescription<T> &)> body) {
123-
ClassContext<T> context(body);
124-
context.subject = subject;
163+
return this->context("", subject, body);
164+
}
165+
166+
template <class T>
167+
Result Description::context(std::string descr, T subject,
168+
std::function<void(ClassDescription<T> &)> body) {
169+
ClassContext<T> context(descr, subject, body);
125170
context.set_parent(this);
126171
context.before_eaches = this->before_eaches;
127172
context.after_eaches = this->after_eaches;
128173
return context.run(this->get_formatter());
129174
}
130175

176+
131177
// template <class T>
132178
// ClassContext<T>& Description::context(
133179
// T& subject, std::function<void(ClassDescription<T>&)> body) {

include/cppspec.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#define _ [=](auto &self) mutable -> void
2121

2222
#define it self.it
23+
#define specify it
2324
#ifdef _MSC_VER // Apparently MSVC++ doesn't conform to C++14 14.2/4. Annoying.
2425
#define context self.context
2526
#define expect self.expect

include/description.hpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,6 @@ class Description : public Runnable {
2323

2424
protected:
2525
std::string descr = "";
26-
std::deque<rule_block_t> after_alls;
27-
std::deque<rule_block_t> before_eaches;
28-
std::deque<rule_block_t> after_eaches;
2926
std::unordered_set<LetBase *> lets;
3027

3128
Description() {}
@@ -34,10 +31,14 @@ class Description : public Runnable {
3431
: Runnable(parent), body(body), descr(descr) {}
3532

3633
public:
34+
3735
// Constructor
3836
Description(std::string descr, block_t body) : body(body), descr(descr) {}
3937

4038
const bool has_subject = false;
39+
std::deque<rule_block_t> after_alls;
40+
std::deque<rule_block_t> before_eaches;
41+
std::deque<rule_block_t> after_eaches;
4142

4243
// Spec functions
4344
Result it(std::string descr, std::function<void(ItD &)> body);
@@ -47,8 +48,8 @@ class Description : public Runnable {
4748
template <class T>
4849
Result context(T subject, std::function<void(ClassDescription<T> &)> body);
4950

50-
// template <class T>
51-
// bool context(T &subject, std::function<void(ClassDescription<T> &)> body);
51+
template <class T>
52+
Result context(std::string descr, T subject, std::function<void(ClassDescription<T> &)> body);
5253

5354
template <class T, typename U>
5455
Result context(std::initializer_list<U> init_list,

include/expectations/expectation.hpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,6 @@ Expectation<A> &Expectation<A>::not_() {
142142
*/
143143
template <typename A>
144144
Expectation<A> &Expectation<A>::ignore() {
145-
// std::cout << "IGNORE: ";
146145
this->ignore_failure = true;
147146
return *this;
148147
}

include/matchers/equal.hpp

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class Equal : public BaseMatcher<A, E> {
3737
template <typename A, typename E>
3838
std::string Equal<A, E>::description() {
3939
std::stringstream ss;
40-
ss << "equal" << Pretty::to_sentance(this->expected);
40+
ss << "equal" << Pretty::to_sentance<E>(this->expected);
4141
return ss.str();
4242
}
4343

@@ -54,23 +54,20 @@ std::string Equal<A, E>::failure_message() {
5454
template <typename A, typename E>
5555
std::string Equal<A, E>::failure_message_when_negated() {
5656
std::stringstream ss;
57-
ss << "\n"
58-
<< "expected not "
59-
<< Util::inspect_object(BaseMatcher<A, E>::get_expected()) << "\n"
57+
ss << "expected not "
58+
<< Pretty::inspect_object(BaseMatcher<A, E>::get_expected()) << "\n"
6059
<< " got " << actual_inspected() << "\n"
61-
<< "\n"
62-
<< "Compared using `==`.\n\n";
60+
<< "Compared using `==`" << std::endl;
6361
return ss.str();
6462
}
6563

6664
template <typename A, typename E>
6765
std::string Equal<A, E>::simple_failure_message() {
6866
std::stringstream ss;
69-
ss << "\n"
70-
<< "expected " << Util::inspect_object(BaseMatcher<A, E>::get_expected())
67+
ss << "expected " << Pretty::inspect_object(BaseMatcher<A, E>::get_expected())
7168
<< "\n"
7269
<< " got " << actual_inspected() << "\n"
73-
<< "Compared using `==`.\n\n";
70+
<< "Compared using `==`" << std::endl;
7471
return ss.str();
7572
}
7673

@@ -81,7 +78,6 @@ bool Equal<A, E>::diffable() {
8178

8279
template <typename A, typename E>
8380
bool Equal<A, E>::match() {
84-
// TODO: compare pointers too
8581
return this->get_expected() == this->get_actual();
8682
}
8783

@@ -99,7 +95,7 @@ bool Equal<A, E>::expected_is_a_literal() {
9995

10096
template <typename A, typename E>
10197
std::string Equal<A, E>::actual_inspected() {
102-
return Util::inspect_object(BaseMatcher<A, E>::get_actual());
98+
return Pretty::inspect_object(BaseMatcher<A, E>::get_actual());
10399
}
104100

105101
// template <typename A, bool E>

0 commit comments

Comments
 (0)