1 /*
2 * ebusd - daemon for communication with eBUS heating systems.
3 * Copyright (C) 2014-2021 John Baier <ebusd@ebusd.eu>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <iostream>
20 #include <iomanip>
21 #include <string>
22 #include <vector>
23 #include "lib/ebus/filereader.h"
24
25 using namespace std;
26 using namespace ebusd;
27
28 static bool error = false;
29
verify(bool expectFailMatch,string type,string input,bool match,string expectStr,string gotStr)30 void verify(bool expectFailMatch, string type, string input,
31 bool match, string expectStr, string gotStr) {
32 match = match && expectStr == gotStr;
33 if (expectFailMatch) {
34 if (match) {
35 cout << " failed " << type << " match >" << input
36 << "< error: unexpectedly succeeded" << endl;
37 error = true;
38 } else {
39 cout << " failed " << type << " match >" << input << "< OK" << endl;
40 }
41 } else if (match) {
42 cout << " " << type << " match >" << input << "< OK" << endl;
43 } else {
44 cout << " " << type << " match >" << input << "< error: got >"
45 << gotStr << "<, expected >" << expectStr << "<" << endl;
46 error = true;
47 }
48 }
49
50 string resultlines[][3] = {
51 {"col 1", "col 2", "col 3"},
52 {"line 2 col 1 de", "line 2 col 2", "line 2 \"col 3\";default of col 3"},
53 {"", "", ""},
54 {"line 4 col 1 de", "line 4 col 2 part 1;line 4 col 2 part 2", "line 4 col 3;default of col 3"},
55 {"", "", ""},
56 {"line 6 col 1 de", "", "line 6 col 3;default of col 3"},
57 {"", "", ""},
58 {"line 8 col 1 de", "line 8 col 2 part 1;line 8 col 2 part 2", "line 8 col 3;default of col 3"},
59 };
60
61 string resultsublines[][2][4] = {
62 {},
63 {{"subcol 1", "line 2 subcol 1", "subcol 2", "line 2 subcol 2;default of sub 0 subcol 2"},{"subcol 2", "line 2 subcol 2", "subcol 3", "line 2 subcol 3"}},
64 {},
65 {{"subcol 1", "line 4 subcol 1", "subcol 2", "line 4 subcol 2;default of sub 0 subcol 2"},{"subcol 2", "line 4 subcol 2", "subcol 3", "line 4 subcol 3"}},
66 {},
67 {{"subcol 1", "line 6 subcol 1", "subcol 2", "line 6 subcol 2;default of sub 0 subcol 2"},{"subcol 2", "line 6 subcol 2", "subcol 3", "line 6 subcol 3"}},
68 {},
69 {{"subcol 1", "line 8 subcol 1", "subcol 2", "line 8 subcol 2;default of sub 0 subcol 2"},{"subcol 2", "line 8 subcol 2", "subcol 3", "line 8 subcol 3"}},
70 };
71
72 static unsigned int baseLine = 0;
73
74 class NoopReader : public FileReader {
75 public:
addFromFile(const string & filename,unsigned int lineNo,vector<string> * row,string * errorDescription,bool replace)76 result_t addFromFile(const string& filename, unsigned int lineNo, vector<string>* row, string* errorDescription,
77 bool replace) override {
78 return RESULT_OK;
79 }
80 };
81
82 class TestReader : public MappedFileReader {
83 public:
TestReader(size_t expectedCols,size_t langCols)84 TestReader(size_t expectedCols, size_t langCols)
85 : MappedFileReader::MappedFileReader(false, ""), m_expectedCols(expectedCols), m_langCols(langCols) {}
getFieldMap(const string & preferLanguage,vector<string> * row,string * errorDescription) const86 result_t getFieldMap(const string& preferLanguage, vector<string>* row, string* errorDescription) const override {
87 if (row->size() == m_expectedCols+m_langCols) {
88 cout << "get field map: split OK" << endl;
89 if (m_langCols == 1) {
90 (*row)[0] = SKIP_COLUMN;
91 size_t pos = (*row)[1].find_last_of('.');
92 (*row)[1] = (*row)[1].substr(0, pos);
93 }
94 return RESULT_OK;
95 }
96 cout << "get field map: error got " << static_cast<unsigned>(row->size()) << " columns, expected " <<
97 static_cast<unsigned>(m_expectedCols+m_langCols) << endl;
98 return RESULT_ERR_EOF;
99 }
addFromFile(const string & filename,unsigned int lineNo,map<string,string> * row,vector<map<string,string>> * subRows,string * errorDescription,bool replace)100 result_t addFromFile(const string& filename, unsigned int lineNo, map<string, string>* row,
101 vector< map<string, string> >* subRows, string* errorDescription, bool replace) override {
102 if (row->empty() || (m_expectedCols == 3) != subRows->empty()) {
103 cout << "read line " << static_cast<unsigned>(baseLine + lineNo) << ": read error: got "
104 << static_cast<unsigned>(row->size()) << "/3 main, " << static_cast<unsigned>(subRows->size())
105 << (m_expectedCols == 3 ? "/0 sub" : "/>0 sub") << endl;
106 return RESULT_ERR_EOF;
107 }
108 if (lineNo < 2 || lineNo >= 1+sizeof(resultlines)/sizeof(string[3])) {
109 cout << "read line " << static_cast<unsigned>(baseLine + lineNo) << ": error invalid line" << endl;
110 return RESULT_ERR_INVALID_ARG;
111 }
112 cout << "read line " << static_cast<unsigned>(baseLine + lineNo) << ": split OK" << endl;
113 string* resultline = resultlines[lineNo - 1];
114 if (row->empty()) {
115 cout << " result empty";
116 if (resultline[0] == "") {
117 cout << ": OK" << endl;
118 } else {
119 cout << ": error" << endl;
120 return RESULT_ERR_INVALID_ARG;
121 }
122 return RESULT_EMPTY;
123 }
124
125 bool error = false;
126 string* colnames = resultlines[0];
127 map<string, string>& defaults = getDefaults()[""];
128 for (size_t colIdx = 0; colIdx < 3; colIdx++) {
129 string col = colnames[colIdx];
130 string got = (*row)[col] + defaults[col];
131 string expect = resultline[colIdx];
132 ostringstream type;
133 type << "line " << static_cast<unsigned>(baseLine + lineNo) << " column \"" << col << "\"";
134 bool match = got == expect;
135 verify(false, type.str(), expect, match, expect, got);
136 if (!match) {
137 error = true;
138 }
139 }
140 if (row->size() > 3) {
141 ostringstream type;
142 type << "line " << static_cast<unsigned>(baseLine + lineNo);
143 verify(false, type.str(), "", false, "", "extra column");
144 error = true;
145 }
146
147 for (size_t subIdx = 0; subIdx < subRows->size(); subIdx++) {
148 string* resultsubline = resultsublines[lineNo - 1][subIdx];
149 *row = (*subRows)[subIdx];
150 if (row->empty()) {
151 cout << " sub " << subIdx << " result empty";
152 if (resultline[0] == "") {
153 cout << ": OK" << endl;
154 } else {
155 cout << ": error" << endl;
156 return RESULT_ERR_INVALID_ARG;
157 }
158 return RESULT_EMPTY;
159 }
160
161 vector< map<string, string> >& subDefaults = getSubDefaults()[""];
162 for (size_t colIdx = 0; colIdx < 2; colIdx++) {
163 string col = resultsubline[colIdx*2];
164 string got = (*row)[col];
165 if (subIdx < subDefaults.size()) {
166 got += subDefaults[subIdx][col];
167 }
168 string expect = resultsubline[colIdx*2+1];
169 ostringstream type;
170 type << "line " << static_cast<unsigned>(baseLine + lineNo) << " sub " << subIdx << " column \"" << col << "\"";
171 bool match = got == expect;
172 verify(false, type.str(), expect, match, expect, got);
173 if (!match) {
174 error = true;
175 }
176 }
177 if (row->size() > 2) {
178 ostringstream type;
179 type << "line " << static_cast<unsigned>(baseLine + lineNo) << " sub " << subIdx;
180 verify(false, type.str(), "", false, "", "extra sub column");
181 error = true;
182 }
183 }
184 return error ? RESULT_ERR_INVALID_ARG : RESULT_OK;
185 }
186 private:
187 size_t m_expectedCols;
188 size_t m_langCols;
189 };
190
191
main(int argc,char ** argv)192 int main(int argc, char** argv) {
193 if (argc > 1) {
194 NoopReader reader;
195 for (int argpos = 1; argpos < argc; argpos++) {
196 size_t hash = 0, size = 0;
197 time_t time = 0;
198 string errorDescription;
199 istream* stream = FileReader::openFile(argv[argpos], &errorDescription, &time);
200 result_t result;
201 if (!stream) {
202 result = RESULT_ERR_NOTFOUND;
203 } else {
204 result = reader.readFromStream(stream, argv[argpos], time, false, nullptr, &errorDescription, false, &hash, &size);
205 }
206 cout << argv[argpos] << " ";
207 if (result != RESULT_OK) {
208 cout << getResultCode(result) << ", " << errorDescription << endl;
209 error = true;
210 continue;
211 }
212 FileReader::formatHash(hash, &cout);
213 cout << " " << size << " " << time << endl;
214 }
215 return error ? 1 : 0;
216 }
217 baseLine = __LINE__+1;
218 istringstream ifs(
219 "col 1.en,col 1.de,col 2,col 3\n"
220 "line 2 col 1 en,line 2 col 1 de,\"line 2 col 2\",\"line 2 \"\"col 3\"\";default of col 3\"\n"
221 "line 4 col 1 en,line 4 col 1 de,\"line 4 col 2 part 1\n"
222 "line 4 col 2 part 2\",line 4 col 3;default of col 3\n"
223 ",,,\n"
224 "line 6 col 1 en,line 6 col 1 de,,line 6 col 3;default of col 3\n"
225 "line 8 col 1 en,line 8 col 1 de,\"line 8 col 2 part 1;\n"
226 "line 8 col 2 part 2\",line 8 col 3;default of col 3\n"
227 );
228 size_t hash = 0, size = 0, expectHash = 0xb958f1cb, expectSize = 389;
229 TestReader reader{3, 1};
230 unsigned int lineNo = 0;
231 vector<string> row;
232 string errorDescription;
233 while (ifs.peek() != EOF) {
234 result_t result = reader.readLineFromStream(&ifs, "", true, &lineNo, &row, &errorDescription, false, &hash, &size);
235 if (result != RESULT_OK) {
236 cout << " error " << getResultCode(result) << endl;
237 error = true;
238 }
239 }
240 if (hash == expectHash) {
241 cout << "hash OK" << endl;
242 } else {
243 cout << "hash error: got 0x" << hex << hash << ", expected 0x" << expectHash << dec << endl;
244 error = true;
245 }
246 if (size == expectSize) {
247 cout << "size OK" << endl;
248 } else {
249 cout << "size error: got " << size << ", expected " << expectSize << endl;
250 error = true;
251 }
252
253 ifs.clear();
254 baseLine = __LINE__+1;
255 ifs.str(
256 "col 1,col 2,col 3,*subcol 1,subcol 2,*subcol 2,subcol 3\n"
257 "line 2 col 1 de,\"line 2 col 2\",\"line 2 \"\"col 3\"\"\",line 2 subcol 1,line 2 subcol 2,line 2 subcol 2,line 2 subcol 3\n"
258 "line 4 col 1 de,\"line 4 col 2 part 1\n"
259 "line 4 col 2 part 2\",line 4 col 3,line 4 subcol 1,line 4 subcol 2,line 4 subcol 2,line 4 subcol 3\n"
260 ",,,\n"
261 "line 6 col 1 de,,line 6 col 3,line 6 subcol 1,line 6 subcol 2,line 6 subcol 2,line 6 subcol 3\n"
262 "line 8 col 1 de,\"line 8 col 2 part 1;\n"
263 "line 8 col 2 part 2\",line 8 col 3,line 8 subcol 1,line 8 subcol 2,line 8 subcol 2,line 8 subcol 3\n"
264 );
265 hash = 0, size = 0, expectHash = 0x2584e0f2, expectSize = 539;
266 TestReader reader2{7, 0};
267 lineNo = 0;
268 map<string, string> defaults;
269 reader2.getDefaults()[""]["col 3"] = ";default of col 3";
270 vector< map<string, string> >& subDefaults = reader2.getSubDefaults()[""];
271 subDefaults.resize(1);
272 subDefaults[0]["subcol 2"] = ";default of sub 0 subcol 2";
273 while (ifs.peek() != EOF) {
274 result_t result = reader2.readLineFromStream(&ifs, "", true, &lineNo, &row, &errorDescription, false, &hash, &size);
275 if (result != RESULT_OK) {
276 cout << " error " << getResultCode(result) << endl;
277 error = true;
278 }
279 }
280 if (hash == expectHash) {
281 cout << "hash OK" << endl;
282 } else {
283 cout << "hash error: got 0x" << hex << hash << ", expected 0x" << expectHash << dec << endl;
284 error = true;
285 }
286 if (size == expectSize) {
287 cout << "size OK" << endl;
288 } else {
289 cout << "size error: got " << size << ", expected " << expectSize << endl;
290 error = true;
291 }
292
293 return error ? 1 : 0;
294 }
295