OTest2
A C++ testing framework
testmark.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2019 Ondrej Starek
3  *
4  * This file is part of OTest2.
5  *
6  * OTest2 is free software: you can redistribute it and/or modify it under
7  * the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation, either version 3 of the License,
9  * or (at your option) any later version.
10  *
11  * OTest2 is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14  * License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with OTest2. If not, see <http://www.gnu.org/licenses/>.
18  */
19 #include <testmark.h>
20 
21 #include <assert.h>
22 #include <string>
23 #include <typeinfo>
24 
25 #include <difflogarray.h>
26 #include <hirschberg.h>
27 #include <testmarkprinter.h>
28 
29 namespace OTest2 {
30 
31 namespace {
32 
33 class TestMarkScore {
34  public:
35  std::tuple<bool, int> scoreSub(
36  int step_,
37  const TestMark::LinearizedRecord left_[],
38  int left_index_,
39  const TestMark::LinearizedRecord right_[],
40  int right_index_) const {
41  /* -- check equality of both nodes */
42  const TestMark::LinearizedRecord& lrec_(left_[left_index_]);
43  const TestMark::LinearizedRecord& rrec_(right_[right_index_]);
44  auto same_(lrec_.me->isEqualValueHash(*rrec_.me)
45  && lrec_.label == rrec_.label);
46  if(same_)
47  return std::make_tuple(true, 1);
48  else
49  return std::make_tuple(false, -1);
50  }
51 
52  int scoreDel(
53  int step_,
54  const TestMark::LinearizedRecord right_[],
55  int right_index_) const {
56  return -1;
57  }
58 
59  int scoreIns(
60  int step_,
61  const TestMark::LinearizedRecord left_[],
62  int left_index_) const {
63  return -1;
64  }
65 
66  bool betterScore(
67  int score1_,
68  int score2_) const {
69  return score1_ > score2_;
70  }
71 };
72 
73 } /* -- namespace */
74 
76 
77 }
78 
80 
81 }
82 
84  return doGetHashCode();
85 }
86 
88  const TestMark& other_,
89  long double precision_) const {
90  if(typeid(*this) == typeid(other_))
91  return doIsEqual(other_, precision_);
92  else
93  return false;
94 }
95 
97  const TestMark& other_,
98  long double precision_) const {
99  if(typeid(*this) == typeid(other_))
100  return doIsEqualValue(other_, precision_);
101  else
102  return false;
103 }
104 
106  const TestMark& other_,
107  long double precision_) const {
108  if(typeid(*this) != typeid(other_))
109  return false;
110  if(doGetHashCode() != other_.doGetHashCode())
111  return false;
112  return doIsEqualValue(other_, precision_);
113 }
114 
116  std::vector<LinearizedRecord>& array_) const {
117  doLinearizedMark(0, "", array_);
118 }
119 
121  std::ostream& os_,
122  const std::string& prefix_) const {
123  doPrintOpen(os_, prefix_);
124 }
125 
127  std::ostream& os_,
128  const std::string& prefix_) const {
129  doPrintClose(os_, prefix_);
130 }
131 
133  TestMarkOut& serializer_) const {
134  doSerializeMark(serializer_);
135 }
136 
138  TestMarkFactory& factory_,
139  TestMarkIn& deserializer_) {
140  doDeserializeMark(factory_, deserializer_);
141 }
142 
143 void TestMark::computeDiff(
144  int level_,
145  const std::vector<LinearizedRecord>& left_,
146  const std::vector<LinearizedRecord>& right_,
147  std::vector<LinearizedRecord>& left_result_,
148  std::vector<LinearizedRecord>& right_result_,
149  DiffLogBuilder& diff_) {
150  /* -- compute difference of current level */
151  DiffLogArray diff_array_;
152  DiffLogBuilderArray diff_level_(&diff_array_);
153  hirschbergDiff(left_, right_, diff_level_, TestMarkScore());
154 
155  int left_index_(0);
156  int right_index_(0);
157  std::vector<LinearizedRecord> left_nested_;
158  std::vector<LinearizedRecord> right_nested_;
159  for(const auto& diff_rec_ : diff_array_) {
160  /* -- copy unchanged records */
161  int left_term_;
162  switch(diff_rec_.action) {
163  case DiffAction::CHANGE:
164  case DiffAction::INSERT:
165  left_term_ = diff_rec_.left_index;
166  break;
167  case DiffAction::DELETE:
168  left_term_ = left_index_ + diff_rec_.right_index - right_index_;
169  break;
170  default:
171  assert(false);
172  break;
173  }
174  for(; left_index_ < left_term_; ++left_index_, ++right_index_) {
175  left_result_.push_back(left_[left_index_]);
176  right_result_.push_back(right_[right_index_]);
177  diff_.addMatch(left_result_.size() - 1, right_result_.size() - 1);
178 
179  /* -- nest into next level */
180  left_nested_.clear();
181  right_nested_.clear();
182  left_[left_index_].me->doDiffArray(level_ + 1, left_nested_);
183  right_[right_index_].me->doDiffArray(level_ + 1, right_nested_);
184  computeDiff(
185  level_ + 1,
186  left_nested_,
187  right_nested_,
188  left_result_,
189  right_result_,
190  diff_);
191  }
192 
193  /* -- process the change */
194  switch(diff_rec_.action) {
195  case DiffAction::CHANGE:
196  left_result_.push_back(left_[left_index_]);
197  right_result_.push_back(right_[right_index_]);
198 
199  /* -- Distinguish a container with changed content but not values.
200  * If the containers are the same (same prefixes) I don't want
201  * to show just changes inside the container, not the opening
202  * diff line. */
203  if(left_[left_index_].me->isEqualValue(*right_[right_index_].me))
204  diff_.addMatch(left_result_.size() - 1, right_result_.size() - 1);
205  else
206  diff_.addChange(left_result_.size() - 1, right_result_.size() - 1);
207 
208  /* -- nest into next level */
209  left_nested_.clear();
210  right_nested_.clear();
211  left_[left_index_].me->doDiffArray(level_ + 1, left_nested_);
212  right_[right_index_].me->doDiffArray(level_ + 1, right_nested_);
213  computeDiff(
214  level_ + 1,
215  left_nested_,
216  right_nested_,
217  left_result_,
218  right_result_,
219  diff_);
220 
221  ++left_index_;
222  ++right_index_;
223 
224  break;
225 
226  case DiffAction::INSERT:
227  left_result_.push_back(left_[left_index_]);
228  diff_.addInsert(left_result_.size() - 1);
229 
230  /* -- nest into next level */
231  left_nested_.clear();
232  right_nested_.clear();
233  left_[left_index_].me->doDiffArray(level_ + 1, left_nested_);
234  computeDiff(
235  level_ + 1,
236  left_nested_,
237  right_nested_,
238  left_result_,
239  right_result_,
240  diff_);
241 
242  ++left_index_;
243 
244  break;
245  case DiffAction::DELETE:
246  right_result_.push_back(right_[right_index_]);
247  diff_.addDelete(right_result_.size() - 1);
248 
249  /* -- nest into next level */
250  left_nested_.clear();
251  right_nested_.clear();
252  right_[right_index_].me->doDiffArray(level_ + 1, right_nested_);
253  computeDiff(
254  level_ + 1,
255  left_nested_,
256  right_nested_,
257  left_result_,
258  right_result_,
259  diff_);
260 
261  ++right_index_;
262 
263  break;
264  default:
265  assert(false);
266  break;
267  }
268  }
269 
270  /* -- copy remaining unchanged records */
271  for(; left_index_ < left_.size(); ++left_index_, ++right_index_) {
272  assert(right_index_ < right_.size());
273 
274  left_result_.push_back(left_[left_index_]);
275  right_result_.push_back(right_[right_index_]);
276  diff_.addMatch(left_result_.size() - 1, right_result_.size() - 1);
277 
278  /* -- nest into next level */
279  left_nested_.clear();
280  right_nested_.clear();
281  left_[left_index_].me->doDiffArray(level_ + 1, left_nested_);
282  right_[right_index_].me->doDiffArray(level_ + 1, right_nested_);
283  computeDiff(
284  level_ + 1,
285  left_nested_,
286  right_nested_,
287  left_result_,
288  right_result_,
289  diff_);
290  }
291  assert(right_index_ == right_.size());
292 }
293 
294 void TestMark::computeDiff(
295  const TestMark& other_,
296  std::vector<LinearizedRecord>& left_,
297  std::vector<LinearizedRecord>& right_,
298  DiffLogBuilder& diff_) const {
299  /* -- prepare input arrays */
300  std::vector<LinearizedRecord> left_array_;
301  left_array_.push_back({0, this, ""});
302  std::vector<LinearizedRecord> right_array_;
303  right_array_.push_back({0, &other_, ""});
304 
305  /* -- compute the diff */
306  std::vector<LinearizedRecord> left_result_;
307  std::vector<LinearizedRecord> right_result_;
308  computeDiff(0, left_array_, right_array_, left_result_, right_result_, diff_);
309 
310  /* -- return the results */
311  left_.swap(left_result_);
312  right_.swap(right_result_);
313 }
314 
316  TestMarkFormatter& formatter_) const {
317  std::vector<LinearizedRecord> array_;
318  linearizedMark(array_);
319  int index_;
320  TestMarkPrinter printer_(&array_, index_);
321  while(printer_.printLine(formatter_));
322 }
323 
325  TestMarkFormatter& formatter_) const {
326  std::vector<LinearizedRecord> array_;
327  linearizedMark(array_);
328  int index_;
329  TestMarkPrinter printer_(&array_, index_);
330  while(printer_.printAdded(formatter_));
331 }
332 
333 } /* namespace OTest2 */
difflogarray.h
OTest2::TestMark::isEqual
bool isEqual(const TestMark &other_, long double precision_=DEFAULT_FLOAT_PRECISION) const
Compare two marks for equality.
Definition: testmark.cpp:87
OTest2::DiffLogBuilder::addMatch
virtual void addMatch(int left_index_, int right_index_)=0
Add match of characters in both sequences.
OTest2::TestMark::linearizedMark
void linearizedMark(std::vector< LinearizedRecord > &array_) const
Create the linearized test mark.
Definition: testmark.cpp:115
OTest2::TestMark::printOpen
void printOpen(std::ostream &os_, const std::string &prefix_) const
Print opening of the node into a stream.
Definition: testmark.cpp:120
OTest2::TestMarkIn
A generic interface of a testmark deserializer.
Definition: testmarkin.h:35
OTest2::hirschbergDiff
void hirschbergDiff(const Type_ left_[], std::size_t left_len_, const Type_ right_[], std::size_t right_len_, DiffLogArray &diff_log_, ScoreFce_ score_fce_=ScoreFce_())
Compute the diff algorithm for specified sequences.
Definition: difflogarray.h:100
OTest2::TestMark::printAddMark
void printAddMark(TestMarkFormatter &formatter_) const
Print the testmark as it's completely new.
Definition: testmark.cpp:324
OTest2::TestMark::isEqualValue
bool isEqualValue(const TestMark &other_, long double precision_=DEFAULT_FLOAT_PRECISION) const
Compare values of 2 test mark nodes.
Definition: testmark.cpp:96
OTest2::TestMarkHashCode
std::uint64_t TestMarkHashCode
Definition: testmarkhashcode.h:28
OTest2::TestMark::deserializeMark
void deserializeMark(TestMarkFactory &factory_, TestMarkIn &deserializer_)
Deserialize the testmark.
Definition: testmark.cpp:137
OTest2::TestMark::isEqualValueHash
bool isEqualValueHash(const TestMark &other_, long double precision_=DEFAULT_FLOAT_PRECISION) const
Compare values of 2 testmark nodes and their hash codes.
Definition: testmark.cpp:105
OTest2::TestMark::getHashCode
TestMarkHashCode getHashCode() const noexcept
Get testmarks' hash code.
Definition: testmark.cpp:83
OTest2::TestMark::TestMark
TestMark()
Ctor.
Definition: testmark.cpp:75
OTest2::TestMarkOut
A generic interface of a serializer of test marks.
Definition: testmarkout.h:31
OTest2::TestMark::printMark
void printMark(TestMarkFormatter &formatter_) const
Print the testmark.
Definition: testmark.cpp:315
OTest2::DiffAction::CHANGE
@ CHANGE
OTest2::TestMarkPrinter
Printer of testmarks.
Definition: testmarkprinter.h:35
OTest2::DiffLogBuilder
Generic interface of the log builder.
Definition: difflogbuilder.h:30
hirschberg.h
OTest2::TestMark::doLinearizedMark
virtual void doLinearizedMark(int level_, const std::string &label_, std::vector< LinearizedRecord > &array_) const =0
Create linearized test mark.
OTest2
Definition: assertbean.h:25
OTest2::DiffAction::INSERT
@ INSERT
OTest2::DiffLogArray
std::vector< DiffRecord > DiffLogArray
Ordered (indexes into the sequences) list of diff changes.
Definition: difflogarray.h:47
OTest2::TestMark::printClose
void printClose(std::ostream &os_, const std::string &prefix_) const
Print closing of the node into a stream.
Definition: testmark.cpp:126
OTest2::DiffLogBuilder::addChange
virtual void addChange(int left_index_, int right_index_)=0
Add change of both sequences.
testmark.h
OTest2::TestMark::serializeMark
void serializeMark(TestMarkOut &serializer_) const
Serialize the testmark into a serializer.
Definition: testmark.cpp:132
testmarkprinter.h
OTest2::DiffLogBuilder::addInsert
virtual void addInsert(int left_index_)=0
Add inserted item to the left sequence.
OTest2::TestMark::~TestMark
virtual ~TestMark()
Dtor.
Definition: testmark.cpp:79
OTest2::TestMarkPrinter::printAdded
bool printAdded(TestMarkFormatter &formatter_)
Print added line.
Definition: testmarkprinter.cpp:154
OTest2::TestMarkPrinter::printLine
bool printLine(TestMarkFormatter &formatter_)
Print current line into a stream.
Definition: testmarkprinter.cpp:142
OTest2::DiffLogBuilderArray
Builder of the diff array.
Definition: difflogarray.h:52
OTest2::TestMark
Generic interface of a test mark node.
Definition: testmark.h:41
OTest2::DiffAction::DELETE
@ DELETE
OTest2::TestMarkFactory
A factory of testmark objects used for deserialization.
Definition: testmarkfactory.h:45
OTest2::TestMarkFormatter
Generic interface of the testmark formatter.
Definition: testmarkformatter.h:32
OTest2::DiffLogBuilder::addDelete
virtual void addDelete(int right_index_)=0
Add deleted item from the right sequence.