File: contexts.qbk

package info (click to toggle)
boost1.88 1.88.0-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 576,932 kB
  • sloc: cpp: 4,149,234; xml: 136,789; ansic: 35,092; python: 33,910; asm: 5,698; sh: 4,604; ada: 1,681; makefile: 1,633; pascal: 1,139; perl: 1,124; sql: 640; yacc: 478; ruby: 271; java: 77; lisp: 24; csh: 6
file content (184 lines) | stat: -rw-r--r-- 6,850 bytes parent folder | download | duplicates (8)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
[/
 / Copyright (c) 2015 Boost.Test contributors
 /
 / Distributed under the Boost Software License, Version 1.0. (See accompanying
 / file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
 /]

[section:contexts Contexts]

Contexts are a facility provided by the __UTF__ in order to be able to trace the location of assertions better. To grasp
the idea, consider the following example:


``
void test_operations(Processor& processor, int limit)
{
  for (int i = 0; i < limit; ++i) {
    BOOST_TEST(processor.op1(i));
    for (int j = 0; j < i; ++j) {
      BOOST_TEST(processor.op2(i, j));
    }
  }
}
``

In case of failure, in order to see in the logs at which point of the loops the failure occurred, we need some extra
information in the assertion, which can be achieved for instance [link boost_test.testing_tools.reports.custom_messages the following way]:


``
BOOST_TEST(processor.op1(i));
``

replaced by

``
BOOST_TEST(processor.op1(i), "With parameter i = " << i);
``

We see in this trivial example that a context, which is the variable `i` in this case, should be acknowledged by the
assertion `BOOST_CHECK` in a particular way. In the approach above, this is done by adding a message to the assertion
itself.

What if the context is more complex than that? In case the complexity of the context increases, the fact that the
assertion and the context is tightly coupled as in the approach above is difficult to maintain:

``
void test_operations(Processor& processor, int limit, int level)
{
  for (int i = 0; i < limit; ++i) {
    BOOST_TEST(processor.op1(i),
               "With optimization level " << level << ", With parameter i = " << i);
    for (int j = 0; j < i; ++j) {
      BOOST_TEST(processor.op2(i, j),
                 "With optimization level " << level <<
                 ", With parameter i = " << i << ", With parameter j = " << j);
    }
  }
}

BOOST_AUTO_TEST_CASE(test1)
{
  Processor processor;

  for (int level = 0; level < 3; ++level) {
    processor.optimization_level(level);
    test_operations(processor, 2, level);
  }
}
``

Note the length of the messages, the repetition, and the fact, that we pass argument `level` to function
`test_operations` only for the sake of generating an error message in case of a failure.

Therefore, *loose* coupling between the context of an assertion and the assertion point is a property that is desirable.

[#ref_BOOST_TEST_INFO][h3 Assertion-bound context]

`BOOST_TEST_INFO` can be used to define an error message to be bound to the first following assertion. If (and only
if) the assertion fails, the bound message will be displayed along:

[bt_example example80_contexts..Assertion-bound context..run-fail]

The information composed inside `BOOST_TEST_INFO` is bound only to the first assertion
following the declaration. This information is only displayed if the assertion fails; otherwise the message is
discarded. The `BOOST_TEST_INFO` declaration does not have to immediately precede the assertion, it is allowed to
intertwine them with other instructions, they can even be declared in different scopes. It is also possible to
bind more than one information to a given assertion.

With `BOOST_TEST_INFO`, we can improve our initial example as follows:


``
void test_operations(Processor& processor, int limit, int level)
{
  for (int i = 0; i < limit; ++i) {
    BOOST_TEST_INFO("With optimization level " << level);
    BOOST_TEST_INFO("With parameter i = " << i);
    BOOST_TEST(processor.op1(i));
    for (int j = 0; j < i; ++j) {
      BOOST_TEST_INFO("With optimization level " << level);
      BOOST_TEST_INFO("With parameter i = " << i);
      BOOST_TEST_INFO("With parameter j = " << j);
      BOOST_TEST(processor.op2(i, j));
    }
  }
}

BOOST_AUTO_TEST_CASE(test1)
{
  Processor processor;

  for (int level = 0; level < 3; ++level) {
    processor.optimization_level(level);
    test_operations(processor, 2, level);
  }
}
``

[#ref_BOOST_TEST_CONTEXT][h3 Scope-bound context]

In the previous example, the information stored inside the calls to `BOOST_TEST_INFO` were all consumed by the next assertion. There are cases
where we would like this information be persistent for the current scope. __UTF__ provides two tools to achieve this:

* `BOOST_TEST_CONTEXT` defines a diagnostic message and a scope. The message is bound to every assertion in that scope,
  and is displayed along with every failed assertion.
* `BOOST_TEST_INFO_SCOPE` acts the same as `BOOST_TEST_INFO`, but the stored context information is bound to all the assertions
  that follow the call to `BOOST_TEST_INFO_SCOPE` within the current scope.

[tip Since Boost [link ref_CHANGE_LOG_3_10 Boost 1.70], `BOOST_TEST_CONTEXT` can accept multiple arguments.]
[tip `BOOST_TEST_INFO_SCOPE` has been introduced in [link ref_CHANGE_LOG_3_10 Boost 1.70].]

[bt_example example81_contexts..Scope-bound context..run-fail]

In the previous example, there is an opening brace right after `BOOST_TEST_CONTEXT`: this pair of braces defines the scope in which
the diagnostic message is in effect. If there is no braces, the scope applies only to the following statement.
`BOOST_TEST_CONTEXT` declarations can nest.

With `BOOST_TEST_CONTEXT`, we can further improve our initial example, by putting variable `level` into a scope-level context
and not pass it as function parameter:

``
void test_operations(Processor& processor, int limit)
{
  for (int i = 0; i < limit; ++i) {
    BOOST_TEST_INFO("With parameter i = " << i);
    BOOST_TEST(processor.op1(i));
    for (int j = 0; j < i; ++j) {
      BOOST_TEST_INFO("With parameter i = " << i);
      BOOST_TEST_INFO("With parameter j = " << j);
      BOOST_TEST(processor.op2(i, j));
    }
  }
}

BOOST_AUTO_TEST_CASE(test1)
{
  Processor processor;

  for (int level = 0; level < 3; ++level) {
    BOOST_TEST_CONTEXT("With optimization level " << level) {
      processor.optimization_level(level);
      test_operations(processor, 2);
    }
  }
}
``
If we observe that variable `i` also applies in a certain scope, we can improve our example further still.

[bt_example example82_contexts..Using contexts..run-fail]

Finally, it is possible to pass several arguments to `BOOST_TEST_CONTEXT`, which is more convenient than having
several scopes:

[bt_example example83_contexts..Multiple arguments to `BOOST_TEST_CONTEXT`..run-fail]

`BOOST_TEST_INFO_SCOPE` is convenient when you augment the current scope information as new information arrives.
The following example calls several time a quadratic polynomial estimation function with random polynomial. As the
random values are drawn in a loop, they are placed in the current scope with `BOOST_TEST_INFO_SCOPE`, which allows us
to easily debug the function.

[bt_example example84_contexts..Sticky context with `BOOST_TEST_INFO_SCOPE`..run-fail]

[endsect] [/ contexts ]