OTest2
A C++ testing framework
testmarkstorage.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2020 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 
20 #include <testmarkstorage.h>
21 
22 #include <algorithm>
23 #include <assert.h>
24 #include <cctype>
25 #include <fstream>
26 #include <iostream>
27 #include <map>
28 #include <sstream>
29 #include <strstream>
30 
31 #include <base64istream.h>
32 #include <base64ostream.h>
33 #include <bzip2istream.h>
34 #include <bzip2ostream.h>
35 #include <exctestmarkin.h>
36 #include <testmark.h>
37 #include <testmarkfactory.h>
38 #include <testmarkinbinios.h>
39 #include <testmarkoutbinios.h>
40 #include <testmarkptr.h>
41 #include <utils.h>
42 
43 namespace OTest2 {
44 
45 namespace {
46 
47 bool isLineEmpty(
48  const std::string& line_) {
49  return std::all_of(
50  line_.begin(), line_.end(), [](char c_) { return std::isspace(c_); });
51 }
52 
53 std::string::size_type searchForSeparator(
54  const std::string& line_) {
55  bool escaped_(false);
56  const std::string::size_type length_(line_.size());
57  for(std::string::size_type i_(0); i_ < length_; ++i_) {
58  char c_(line_[i_]);
59  if(escaped_) {
60  escaped_ = false;
61  }
62  else if(c_ == '\\') {
63  escaped_ = true;
64  }
65  else if(c_ == ':') {
66  return i_;
67  }
68  }
69  return std::string::npos;
70 }
71 
72 std::string escapeKey(
73  const std::string& unescaped_key_) {
74  std::ostringstream oss_;
75  for(char c_ : unescaped_key_) {
76  if(c_ == ':' || c_ == '\\' || std::isspace(c_))
77  oss_.put('\\');
78  oss_.put(c_);
79  }
80  return oss_.str();
81 }
82 
83 std::string unescapeKey(
84  const std::string& escaped_key_) {
85  std::ostringstream oss_;
86  bool escape_(false);
87  for(char c_ : escaped_key_) {
88  if(escape_) {
89  oss_.put(c_);
90  escape_ = false;
91  }
92  else {
93  if(c_ == '\\') {
94  escape_ = true;
95  }
96  else {
97  oss_.put(c_);
98  }
99  }
100  }
101  return oss_.str();
102 }
103 
104 } /* -- namespace */
105 
106 struct TestMarkStorage::Impl {
107  TestMarkFactory* factory;
108  std::string storage_file;
109 
110  typedef std::map<std::string, TestMarkPtr> Storage;
111  Storage storage;
112  bool changed;
113 
114  /* -- avoid copying */
115  Impl(
116  const Impl&) = delete;
117  Impl& operator = (
118  const Impl&) = delete;
119 
120  explicit Impl(
121  TestMarkFactory* factory_,
122  const std::string& storage_file_);
123  ~Impl();
124 };
125 
126 TestMarkStorage::Impl::Impl(
127  TestMarkFactory* factory_,
128  const std::string& storage_file_) :
129  factory(factory_),
130  storage_file(storage_file_),
131  storage(),
132  changed(false) {
133  assert(factory != nullptr);
134 
135  /* -- read the test marks from the file */
136  std::ifstream ifs_(storage_file.c_str());
137  while(!!ifs_) {
138  std::ostringstream oss_;
139  ifs_.get(*oss_.rdbuf()); /* -- get the whole line */
140  ifs_.get(); /* -- skip the newline */
141  std::string line_(oss_.str());
142 
143  /* -- ignore empty lines */
144  if(isLineEmpty(line_))
145  continue;
146 
147  /* -- parse the key */
148  auto sep_index_(searchForSeparator(line_));
149  if(sep_index_ <= 0 || sep_index_ == std::string::npos) /* -- wrong line */
150  throw ExcTestMarkIn("invalid format of the test mark storage file " + storage_file);
151  std::string key_(line_.data(), line_.data() + sep_index_);
152 
153  /* -- parse the test mark */
154  std::istrstream iss_(
155  line_.data() + sep_index_ + 1, line_.size() - sep_index_ - 1);
156  Base64IStream base64i_(&iss_);
157  Bzip2IStream bzip2i_(&base64i_);
158  TestMarkInBinIOS reader_(&bzip2i_);
159  TestMarkPtr testmark_(TestMarkInBinIOS::deserialize(*factory, reader_));
160 
161  /* -- insert into the map */
162  storage.insert({unescapeKey(key_), testmark_});
163  }
164 }
165 
166 TestMarkStorage::Impl::~Impl() {
167  /* -- store the storage if it changed */
168  if(changed) {
169  std::ofstream ofs_(storage_file.c_str());
170  for(const auto& mark_ : storage) {
171  /* -- write key */
172  ofs_ << escapeKey(mark_.first) << ':';
173 
174  /* -- write the mark */
175  {
176  Base64OStream base64o_(&ofs_);
177  Bzip2OStream bzip2o_(&base64o_);
178  TestMarkOutBinIOS writer_(&bzip2o_);
179  mark_.second->serializeMark(writer_);
180  }
181 
182  /* -- record separator */
183  ofs_ << '\n';
184  }
185  }
186 }
187 
189  TestMarkFactory* factory_,
190  const std::string& storage_file_) :
191  pimpl(new Impl(factory_, storage_file_)) {
192 
193 }
194 
196  odelete(pimpl);
197 }
198 
200  const std::string& key_,
201  TestMarkPtr test_mark_) {
202  assert(!key_.empty() && test_mark_ != nullptr);
203 
204  pimpl->storage[key_] = test_mark_;
205  pimpl->changed = true;
206 }
207 
209  const std::string& key_) const {
210  assert(!key_.empty());
211 
212  auto iter_(pimpl->storage.find(key_));
213  if(iter_ != pimpl->storage.end())
214  return (*iter_).second;
215  else
216  return TestMarkPtr();
217 }
218 
219 } /* -- namespace OTest2 */
testmarkptr.h
OTest2::TestMarkStorage::setTestMark
void setTestMark(const std::string &key_, TestMarkPtr test_mark_)
Set new or reset old test mark.
Definition: testmarkstorage.cpp:199
OTest2::TestMarkIn::deserialize
static TestMarkPtr deserialize(TestMarkFactory &factory_, TestMarkIn &deserializer_)
Deserialize a testmark.
Definition: testmarkin.cpp:37
base64istream.h
testmarkoutbinios.h
testmarkstorage.h
base64ostream.h
exctestmarkin.h
OTest2::TestMarkStorage::~TestMarkStorage
virtual ~TestMarkStorage()
Dtor.
Definition: testmarkstorage.cpp:195
utils.h
OTest2::TestMarkPtr
std::shared_ptr< TestMark > TestMarkPtr
Definition: testmarkptr.h:26
OTest2::TestMarkStorage::TestMarkStorage
TestMarkStorage(TestMarkFactory *factory_, const std::string &storage_file_)
Ctor.
Definition: testmarkstorage.cpp:188
OTest2
Definition: assertbean.h:25
testmarkfactory.h
testmark.h
OTest2::TestMarkStorage::operator=
TestMarkStorage & operator=(const TestMarkStorage &)=delete
bzip2istream.h
testmarkinbinios.h
OTest2::TestMarkStorage::getTestMark
TestMarkPtr getTestMark(const std::string &key_) const
Get test mark.
Definition: testmarkstorage.cpp:208
bzip2ostream.h
OTest2::TestMarkFactory
A factory of testmark objects used for deserialization.
Definition: testmarkfactory.h:45
OTest2::odelete
void odelete(T_ *&object_)
Delete a pointer and set it invalid.
Definition: utils.h:34