boost_test_collection_comparison.qbk 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. [/
  2. / Copyright (c) 2015 Boost.Test contributors
  3. /
  4. / Distributed under the Boost Software License, Version 1.0. (See accompanying
  5. / file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. /]
  7. [section:collections Collections comparison]
  8. Instead of comparing a single value against another, there is often a need for comparing /collections/ of values.
  9. A collection and indirectly the values it contains may be considered in several ways:
  10. * collection as a /sequence of values/: this is the case for instance when `N` values are stored in a
  11. container. Containers in this case are used for storing several values, and iterating over the containers yields
  12. sequences that can be compared *element-wise*. The iteration should be in an order that is /a priori/ known
  13. [footnote this might not be the case
  14. for e.g. `std::unordered_map`, for which the buckets might be filled differently depending on the insertion order.],
  15. for being able to compare the sequences. The values in the collection are independent each other, and subsets can be compared as well.
  16. * collection as an /ensemble/: this is the case where the elements of the collection define an /entity/,
  17. and no element can be dissociated from the others.
  18. An example would be a collection of letters for a specific word in the natural language; in this settings
  19. any of the character in the word/collection depends /semantically/ on the other and it is not possible to take
  20. a subset of it without breaking the meaning of the word. Another example would be a vector of size `N` representing a
  21. point in a `N` dimensional space, compared to another point with the relation "`<`": the comparison is application
  22. specific and a possible comparison would be the lexicographical ordering
  23. [footnote in this case `v_a < v_b` means that the point `v_a` is inside the rectangle (origin, `v_b`)].
  24. The following observations can be done:
  25. * the methods employed for comparing collections should be chosen adequately with the meaning of the collection,
  26. * comparing sequences *element-wise* often involves writing loops in the test body, and if a dedicated tool is already in place
  27. the test body would gain in clarity and expressiveness (including the report in case of failure),
  28. * some comparison methods such as the lexicographical one, have good general behavior (e.g. total ordering,
  29. defined for collections of different size), but are sometimes inappropriate.
  30. __BOOST_TEST__ provides specific tools for comparing collections:
  31. * using the /native/ [footnote either defined by the container or by the user] operator of the container of the collection,
  32. which is mentioned as the [link ref_boost_test_coll_default_comp /default behavior/].
  33. * using [link boost_test_coll_perelement element-wise] comparison for which extended failure diagnostic is provided,
  34. * and using [link boost_test_coll_default_lex lexicographical] comparison for which extended failure diagnostic is provided,
  35. More details about the concept of /collection/ in the __UTF__ is given [link what_is_a_collection /here/].
  36. [#ref_boost_test_coll_default_comp][h3 Default comparison]
  37. The default comparison dispatches to the existing overloaded comparison operator. The __UTF__ distinguishes two use cases
  38. # none of the comparison operand is a C-Array, in which case we use the [link ref_boost_test_coll_default_comp_container container default behavior]
  39. # one of the comparison operand is a C-array, in which case we [link ref_boost_test_coll_c_arrays mimic `std::vector`] behavior
  40. [#ref_boost_test_coll_default_comp_container][h4 Container default behavior]
  41. Given two containers `c_a` and `c_b` that are not C-arrays,
  42. ``
  43. BOOST_TEST(c_a op c_b)
  44. ``
  45. is equivalent, in terms of test success, to
  46. ``
  47. auto result = c_a op c_b;
  48. BOOST_TEST(result);
  49. ``
  50. In the example below, `operator==` is not defined for `std::vector` of different types, and the program would fail to
  51. compile if the corresponding lines were uncommented (`std::vector` uses lexicographical comparison by default).
  52. [note In the case of default comparison, there is no additional diagnostic provided by the __UTF__. See the section
  53. [link ref_boost_test_coll_special_macro `BOOST_TEST_SPECIALIZED_COLLECTION_COMPARE`] below.]
  54. [bt_example boost_test_container_default..BOOST_TEST containers comparison default..run-fail]
  55. [#ref_boost_test_coll_c_arrays][h4 C-arrays default behavior]
  56. As soon as one of the operands is a C-array, there is no /default behavior/ the __UTF__ can dispatch to.
  57. This is why in that case, the comparison mimics the `std::vector` behavior.
  58. [bt_example boost_test_macro_container_c_array..BOOST_TEST C-arrays..run-fail]
  59. [#boost_test_coll_perelement][h3 Element-wise comparison]
  60. By specifying the manipulator [classref boost::test_tools::per_element], the comparison of the elements of the containers
  61. are performed /element-wise/, in the order given by the forward iterators of the containers. This is a comparison on
  62. the /sequences/ of elements generated by the containers, for which the __UTF__ provides advanced diagnostic.
  63. In more details, let `c_a = (a_1,... a_n)` and `c_b = (b_1,... b_n)` be two sequences of same length, but not necessarily of same type.
  64. Those sequences correspond to the content of the respective containers, in the order given by their iterator. Let
  65. `op` be one of the [link boost_test_statement_overloads binary comparison operators].
  66. ``
  67. BOOST_TEST(c_a op c_b, boost::test_tools::per_element() );
  68. ``
  69. is equivalent to
  70. ``
  71. if(c_a.size() == c_b.size())
  72. {
  73. for(int i=0; i < c_a.size(); i++)
  74. {
  75. __BOOST_TEST_CONTEXT__("index " << i)
  76. {
  77. BOOST_TEST(a_i op b_i);
  78. }
  79. }
  80. }
  81. else
  82. {
  83. BOOST_TEST(c_a.size() == c_b.size());
  84. }
  85. ``
  86. [warning this is fundamentally different from using the containers' default comparison operators (default behavior).]
  87. [warning this is not an order relationship on containers. As a side effect, it is possible to have
  88. ``BOOST_TEST(c_a == c_b)`` and ``BOOST_TEST(c_a != c_b)`` failing at the same time]
  89. Sequences are compared using the specified operator `op`, evaluated on the left and right elements of the respective sequences.
  90. The order of the compared elements is given by the iterators of the respective containers [footnote the containers should yield the same
  91. sequences for a fixed set of elements they contain].
  92. In case of failure, the indices of the elements failing `op` are returned.
  93. [bt_example boost_test_sequence_per_element..BOOST_TEST sequence comparison..run-fail]
  94. [h4 Requirements]
  95. For the sequences to be comparable element-wise, the following conditions should be met:
  96. * the containers should meet the [link what_is_a_collection sequence] definition,
  97. * the containers should yield the same number of elements,
  98. * `op` should be one of the comparison operator `==`, `!=`, `<`, `<=`, `>`, `>=`
  99. * the `a_i op b_i` should be defined, where the type of `a_i` and `b_i` are the type returned by the dereference operator
  100. of the respective collections.
  101. [caution the resulting type of "`c_a == c_b`" is an [classref boost::test_tools::assertion_result assertion_result]: it is not
  102. possible to compose more that one comparison on the `BOOST_TEST` statement:
  103. ``
  104. BOOST_TEST(c_a == c_b == 42, boost::test_tools::per_element() ); // does not compile
  105. ``]
  106. [#boost_test_coll_default_lex][h3 Lexicographic comparison]
  107. By specifying the manipulator [classref boost::test_tools::lexicographic], the containers are compared using the /lexicographical/
  108. order and for which the __UTF__ provides additional diagnostic in case of failure.
  109. ``
  110. BOOST_TEST(c_a op c_b, boost::test_tools::lexicographic() );
  111. ``
  112. The comparison is performed in the order given by forward iterators of the containers.
  113. [tip lexicographic comparison yields a total order on the containers: the statements `c_a < c_b` and
  114. `c_b <= c_a` are mutually exclusive.]
  115. [note The equality `==` and inequality `!=` are not available for this type of comparison.]
  116. [bt_example boost_test_container_lex..BOOST_TEST container comparison using lexicographical order..run-fail]
  117. [#ref_boost_test_coll_special_macro][h3 Extended diagnostic by default for specific containers]
  118. As seen above,
  119. * for testing equality, the `==` relation is either explicit (using `boost::test_tools::per_element()`) or
  120. implicit when the container overloads/implements this type of comparison,
  121. * for testing inequality, lexicographical comparison for `<` (and derived operations) is either explicit
  122. (using `boost::test_tools::lexicographic()`) or implicit when
  123. the container overloads/implements uses this type of comparison.
  124. When the default is to using the container implementation, it is not possible to benefit from an extended failure diagnostic.
  125. The __UTF__ provides a mechanism for performing the same comparisons through the __UTF__ instead of the container operator,
  126. through the macro `BOOST_TEST_SPECIALIZED_COLLECTION_COMPARE` that might be used as follow:
  127. [bt_example boost_test_container_lex_default..Default `std::vector<int>` to lexicographic with extended diagnostic..run-fail]
  128. [h4 Requirements]
  129. * the containers should meet the [link what_is_a_collection sequence] definition,
  130. * the containers should be of the exact same type
  131. * `op` should be one of the comparison operator `==`, `!=`, `<`, `<=`, `>`, `>=`
  132. [note Note that the operation `!=` is in this case not an element-wise comparison, ]
  133. [#what_is_a_collection][h3 What is a sequence?]
  134. A /sequence/ is given by the iteration over a /forward iterable/ container. A forward iterable container is:
  135. * either a C-array,
  136. * or a `class`/`struct` that implements the member functions `begin` and `end`.
  137. For collection comparisons, both sequences are also required to be different than `string` sequences. In that case, the sequences are
  138. dispatched to string [link boost_test.testing_tools.extended_comparison.strings comparison instead].
  139. [warning `string` (or `wstring`) meets the sequence concept by definition, but their handling with __BOOST_TEST__ is done differently.
  140. See [link boost_test.testing_tools.extended_comparison.strings Strings and C-strings comparison] for more details.]
  141. [tip If the behavior of __BOOST_TEST__ is not the one you expect, you can always use raw comparison. See [link boost_test_statement_limitations this section]
  142. for details.]
  143. [note Since [link ref_CHANGE_LOG_3_6 Boost.Test 3.6] (Boost 1.65) the requirements for the collection concepts have been relaxed to
  144. include C-arrays as well]
  145. [note Since [link ref_CHANGE_LOG_3_7 Boost.Test 3.7] (Boost 1.67) the definition of `const_iterator` and `value_type` in the collection
  146. type is not required anymore (for the compilers properly supporting `decltype`). ]
  147. The detection of the types that meet these requirements containers is delegated to the class [classref boost::unit_test::is_forward_iterable],
  148. which for C++11 detects the required member functions and fields. However for C++03, the types providing the sequences should be explicitly
  149. indicated to the __UTF__ by a specialization of [classref boost::unit_test::is_forward_iterable]
  150. [footnote Standard containers of the `STL` are recognized as /forward iterable/ container.].
  151. [endsect] [/ sequences]