1 | // ================================================================= // |
---|
2 | // // |
---|
3 | // File : arb_diff.cxx // |
---|
4 | // Purpose : code to compare/diff files // |
---|
5 | // // |
---|
6 | // Coded by Ralf Westram (coder@reallysoft.de) in September 2013 // |
---|
7 | // Institute of Microbiology (Technical University Munich) // |
---|
8 | // http://www.arb-home.de/ // |
---|
9 | // // |
---|
10 | // ================================================================= // |
---|
11 | |
---|
12 | // AISC_MKPT_PROMOTE:#ifndef _GLIBCXX_CSTDLIB |
---|
13 | // AISC_MKPT_PROMOTE:#include <cstdlib> |
---|
14 | // AISC_MKPT_PROMOTE:#endif |
---|
15 | |
---|
16 | #include "arb_diff.h" |
---|
17 | #include "arb_match.h" |
---|
18 | #include "arb_string.h" |
---|
19 | #include "arb_msg.h" |
---|
20 | #include "arb_file.h" |
---|
21 | #include <arb_str.h> |
---|
22 | #include <arb_assert.h> |
---|
23 | #include <arbtools.h> |
---|
24 | #include <smartptr.h> |
---|
25 | |
---|
26 | #include <list> |
---|
27 | #include <string> |
---|
28 | |
---|
29 | #define MAX_REGS 13 |
---|
30 | |
---|
31 | class difflineMode : virtual Noncopyable { |
---|
32 | int mode; |
---|
33 | |
---|
34 | GBS_regex *reg[MAX_REGS]; |
---|
35 | bool wordsOnly[MAX_REGS]; // only match if regexpr hits a word |
---|
36 | const char *replace[MAX_REGS]; |
---|
37 | |
---|
38 | int count; |
---|
39 | mutable GB_ERROR error; |
---|
40 | |
---|
41 | void add(bool onlyWords, const char *regEx, GB_CASE case_flag, const char *replacement) { |
---|
42 | if (!error) { |
---|
43 | arb_assert(count<MAX_REGS); |
---|
44 | reg[count] = GBS_compile_regexpr(regEx, case_flag, &error); |
---|
45 | if (!error) { |
---|
46 | replace[count] = replacement; |
---|
47 | wordsOnly[count] = onlyWords; |
---|
48 | count++; |
---|
49 | } |
---|
50 | } |
---|
51 | } |
---|
52 | |
---|
53 | static bool is_word_char(char c) { return isalnum(c) || c == '_'; } // matches posix word def |
---|
54 | |
---|
55 | typedef SmartCustomPtr(GBS_regex, GBS_free_regexpr) GBS_regexPtr; |
---|
56 | |
---|
57 | mutable bool may_involved; |
---|
58 | static GBS_regexPtr contains_May; |
---|
59 | |
---|
60 | void avoid_may_problems(const char *str) const { |
---|
61 | if (!may_involved) { |
---|
62 | if (GBS_regmatch_compiled(str, &*contains_May, NULL)) { |
---|
63 | // 'May' does not differ between short/long monthname |
---|
64 | // -> use less accurate tests in May |
---|
65 | fprintf(stderr, "Loosening month comparison: 'May' involved in '%s'\n", str); |
---|
66 | const_cast<difflineMode*>(this)->add(false, "<Month>", GB_MIND_CASE, "<MON>"); |
---|
67 | may_involved = true; |
---|
68 | } |
---|
69 | } |
---|
70 | } |
---|
71 | |
---|
72 | public: |
---|
73 | difflineMode(int mode_) |
---|
74 | : mode(mode_), |
---|
75 | count(0), |
---|
76 | error(NULL), |
---|
77 | may_involved(false) |
---|
78 | { |
---|
79 | memset(reg, 0, sizeof(reg)); |
---|
80 | switch (mode) { |
---|
81 | case 0: break; |
---|
82 | case 1: { |
---|
83 | add(false, "[0-9]{2}:[0-9]{2}:[0-9]{2}", GB_MIND_CASE, "<TIME>"); |
---|
84 | add(true, "(Mon|Tue|Wed|Thu|Fri|Sat|Sun)", GB_IGNORE_CASE, "<DOW>"); |
---|
85 | |
---|
86 | add(true, "(January|February|March|April|May|June|July|August|September|October|November|December)", GB_IGNORE_CASE, "<Month>"); |
---|
87 | add(true, "(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)", GB_IGNORE_CASE, "<MON>"); |
---|
88 | |
---|
89 | add(false, "<MON>[ -][0-9]{4}", GB_IGNORE_CASE, "<MON> <YEAR>"); |
---|
90 | add(false, "<Month>[ -][0-9]{4}", GB_IGNORE_CASE, "<Month> <YEAR>"); |
---|
91 | |
---|
92 | add(false, "<TIME>[ -][0-9]{4}", GB_IGNORE_CASE, "<TIME> <YEAR>"); |
---|
93 | |
---|
94 | add(false, "<MON>[ -][0-9 ]?[0-9]", GB_IGNORE_CASE, "<MON> <DAY>"); |
---|
95 | add(false, "<Month>[ -][0-9 ]?[0-9]", GB_IGNORE_CASE, "<Month> <DAY>"); |
---|
96 | |
---|
97 | add(false, "[0-9]{2}[ -\\.]+<MON>", GB_IGNORE_CASE, "<DAY> <MON>"); |
---|
98 | add(false, "[0-9]{2}[ -\\.]+<Month>", GB_IGNORE_CASE, "<DAY> <Month>"); |
---|
99 | |
---|
100 | add(false, "<DAY>, [0-9]{4}", GB_IGNORE_CASE, "<DAY> <YEAR>"); |
---|
101 | |
---|
102 | break; |
---|
103 | } |
---|
104 | default: arb_assert(0); break; |
---|
105 | } |
---|
106 | } |
---|
107 | ~difflineMode() { |
---|
108 | for (int i = 0; i<count; ++i) { |
---|
109 | GBS_free_regexpr(reg[i]); |
---|
110 | reg[i] = NULL; |
---|
111 | } |
---|
112 | } |
---|
113 | |
---|
114 | const char *get_error() const { return error; } |
---|
115 | int get_count() const { return count; } |
---|
116 | |
---|
117 | private: |
---|
118 | void replaceAll(char*& str) const { |
---|
119 | for (int i = 0; i<count; ++i) { |
---|
120 | size_t matchlen; |
---|
121 | const char *matched = GBS_regmatch_compiled(str, reg[i], &matchlen); |
---|
122 | |
---|
123 | if (matched) { |
---|
124 | char *prefix = GB_strpartdup(str, matched-1); |
---|
125 | const char *suffix = matched+matchlen; |
---|
126 | |
---|
127 | bool do_repl = true; |
---|
128 | if (wordsOnly[i]) { |
---|
129 | if (prefix[0] != 0 && is_word_char(matched[-1])) do_repl = false; |
---|
130 | else if (suffix[0] != 0 && is_word_char(suffix[0])) do_repl = false; |
---|
131 | } |
---|
132 | |
---|
133 | if (do_repl) freeset(str, GBS_global_string_copy("%s%s%s", prefix, replace[i], suffix)); |
---|
134 | |
---|
135 | free(prefix); |
---|
136 | } |
---|
137 | } |
---|
138 | } |
---|
139 | public: |
---|
140 | void replaceAll(char*& str1, char*& str2) const { |
---|
141 | avoid_may_problems(str1); |
---|
142 | avoid_may_problems(str2); |
---|
143 | replaceAll(str1); |
---|
144 | replaceAll(str2); |
---|
145 | } |
---|
146 | }; |
---|
147 | |
---|
148 | static GB_ERROR static_error = NULL; |
---|
149 | difflineMode::GBS_regexPtr difflineMode::contains_May = GBS_compile_regexpr("May", GB_IGNORE_CASE, &static_error); |
---|
150 | |
---|
151 | static void cutEOL(char *s) { |
---|
152 | char *lf = strchr(s, '\n'); |
---|
153 | if (lf) lf[0] = 0; |
---|
154 | } |
---|
155 | |
---|
156 | static bool test_accept_diff_lines(const char *line1, const char *line2, const difflineMode& mode) { |
---|
157 | if (*line1++ != '-') return false; |
---|
158 | if (*line2++ != '+') return false; |
---|
159 | |
---|
160 | char *dup1 = strdup(line1); |
---|
161 | char *dup2 = strdup(line2); |
---|
162 | |
---|
163 | cutEOL(dup1); // side-effect: accepts missing trailing newline |
---|
164 | cutEOL(dup2); |
---|
165 | |
---|
166 | mode.replaceAll(dup1, dup2); |
---|
167 | |
---|
168 | bool equalNow = strcmp(dup1, dup2) == 0; |
---|
169 | #if defined(DEBUG) |
---|
170 | // printf("dup1='%s'\ndup2='%s'\n", dup1, dup2); // uncomment this line to trace replaces |
---|
171 | #endif // DEBUG |
---|
172 | |
---|
173 | free(dup2); |
---|
174 | free(dup1); |
---|
175 | |
---|
176 | return equalNow; |
---|
177 | } |
---|
178 | |
---|
179 | class DiffLines { |
---|
180 | typedef std::list<std::string> Lines; |
---|
181 | typedef Lines::iterator LinesIter; |
---|
182 | typedef Lines::const_iterator LinesCIter; |
---|
183 | |
---|
184 | Lines added_lines; |
---|
185 | Lines deleted_lines; |
---|
186 | |
---|
187 | LinesIter added_last_checked; |
---|
188 | LinesIter deleted_last_checked; |
---|
189 | |
---|
190 | static LinesIter next(LinesIter iter) { advance(iter, 1); return iter; } |
---|
191 | static LinesIter last(Lines& lines) { return (++lines.rbegin()).base(); } |
---|
192 | |
---|
193 | void set_checked() { |
---|
194 | added_last_checked = last(added_lines); |
---|
195 | deleted_last_checked = last(deleted_lines); |
---|
196 | } |
---|
197 | |
---|
198 | public: |
---|
199 | DiffLines() { set_checked(); } |
---|
200 | |
---|
201 | bool add(const char *diffline) { |
---|
202 | bool did_add = true; |
---|
203 | switch (diffline[0]) { |
---|
204 | case '-': deleted_lines.push_back(diffline); break; |
---|
205 | case '+': added_lines.push_back(diffline); break; |
---|
206 | default : did_add = false; break; |
---|
207 | } |
---|
208 | // fputs(diffline, stdout); // uncomment to show all difflines |
---|
209 | return did_add; |
---|
210 | } |
---|
211 | |
---|
212 | int added() const { return added_lines.size(); } |
---|
213 | int deleted() const { return deleted_lines.size(); } |
---|
214 | |
---|
215 | void remove_accepted_lines(const difflineMode& mode) { |
---|
216 | LinesIter d = next(deleted_last_checked); |
---|
217 | LinesIter dEnd = deleted_lines.end(); |
---|
218 | LinesIter a = next(added_last_checked); |
---|
219 | LinesIter aEnd = added_lines.end(); |
---|
220 | |
---|
221 | |
---|
222 | while (d != dEnd && a != aEnd) { |
---|
223 | if (test_accept_diff_lines(d->c_str(), a->c_str(), mode)) { |
---|
224 | d = deleted_lines.erase(d); |
---|
225 | a = added_lines.erase(a); |
---|
226 | } |
---|
227 | else { |
---|
228 | ++d; |
---|
229 | ++a; |
---|
230 | } |
---|
231 | } |
---|
232 | set_checked(); |
---|
233 | } |
---|
234 | |
---|
235 | void print_from(FILE *out, LinesCIter a, LinesCIter d) const { |
---|
236 | LinesCIter dEnd = deleted_lines.end(); |
---|
237 | LinesCIter aEnd = added_lines.end(); |
---|
238 | |
---|
239 | while (d != dEnd && a != aEnd) { |
---|
240 | fputs(d->c_str(), out); ++d; |
---|
241 | fputs(a->c_str(), out); ++a; |
---|
242 | } |
---|
243 | while (d != dEnd) { fputs(d->c_str(), out); ++d; } |
---|
244 | while (a != aEnd) { fputs(a->c_str(), out); ++a; } |
---|
245 | } |
---|
246 | |
---|
247 | void print(FILE *out) const { |
---|
248 | LinesCIter d = deleted_lines.begin(); |
---|
249 | LinesCIter a = added_lines.begin(); |
---|
250 | print_from(out, a, d); |
---|
251 | } |
---|
252 | }; |
---|
253 | |
---|
254 | bool ARB_textfiles_have_difflines(const char *file1, const char *file2, int expected_difflines, int special_mode) { |
---|
255 | // special_mode: 0 = none, 1 = accept date and time changes as equal |
---|
256 | // |
---|
257 | // Warning: also returns true if comparing two EQUAL binary files. |
---|
258 | // But it always fails if one file is binary and files differ |
---|
259 | |
---|
260 | const char *error = NULL; |
---|
261 | |
---|
262 | if (!GB_is_regularfile(file1)) error = GBS_global_string("No such file '%s'", file1); |
---|
263 | else if (!GB_is_regularfile(file2)) error = GBS_global_string("No such file '%s'", file2); |
---|
264 | else { |
---|
265 | char *cmd = GBS_global_string_copy("/usr/bin/diff --unified %s %s", file1, file2); |
---|
266 | FILE *diffout = popen(cmd, "r"); |
---|
267 | |
---|
268 | if (diffout) { |
---|
269 | #define BUFSIZE 5000 |
---|
270 | char *buffer = (char*)malloc(BUFSIZE); |
---|
271 | bool inHunk = false; |
---|
272 | DiffLines diff_lines; |
---|
273 | difflineMode mode(special_mode); |
---|
274 | |
---|
275 | // TEST_EXPECT_NO_ERROR(mode.get_error()); |
---|
276 | error = mode.get_error(); |
---|
277 | |
---|
278 | while (!error && !feof(diffout)) { |
---|
279 | char *line = fgets(buffer, BUFSIZE, diffout); |
---|
280 | if (!line) break; |
---|
281 | |
---|
282 | #if defined(ASSERTION_USED) |
---|
283 | size_t len = strlen(line); |
---|
284 | arb_assert(line && len<(BUFSIZE-1)); // increase BUFSIZE |
---|
285 | #endif |
---|
286 | |
---|
287 | if (ARB_strBeginsWith(line, "Binary files")) { |
---|
288 | if (strstr(line, "differ")) { |
---|
289 | error = "attempt to compare binary files"; |
---|
290 | } |
---|
291 | } |
---|
292 | else { |
---|
293 | bool remove_now = true; |
---|
294 | if (strncmp(line, "@@", 2) == 0) inHunk = true; |
---|
295 | else if (!inHunk && strncmp(line, "Index: ", 7) == 0) inHunk = false; |
---|
296 | else if (inHunk) { |
---|
297 | remove_now = !diff_lines.add(line); |
---|
298 | } |
---|
299 | |
---|
300 | if (remove_now) diff_lines.remove_accepted_lines(mode); |
---|
301 | } |
---|
302 | } |
---|
303 | |
---|
304 | if (!error) { |
---|
305 | diff_lines.remove_accepted_lines(mode); |
---|
306 | |
---|
307 | int added = diff_lines.added(); |
---|
308 | int deleted = diff_lines.deleted(); |
---|
309 | |
---|
310 | if (added != deleted) { |
---|
311 | error = GBS_global_string("added lines (=%i) differ from deleted lines(=%i)", added, deleted); |
---|
312 | } |
---|
313 | else if (added != expected_difflines) { |
---|
314 | error = GBS_global_string("files differ in %i lines (expected=%i)", added, expected_difflines); |
---|
315 | } |
---|
316 | |
---|
317 | if (error) { |
---|
318 | fputs("Different lines:\n", stdout); |
---|
319 | diff_lines.print(stdout); |
---|
320 | fputc('\n', stdout); |
---|
321 | } |
---|
322 | } |
---|
323 | free(buffer); |
---|
324 | IF_ASSERTION_USED(int err =) pclose(diffout); |
---|
325 | arb_assert(err != -1); |
---|
326 | #undef BUFSIZE |
---|
327 | } |
---|
328 | else { |
---|
329 | error = GBS_global_string("failed to run diff (%s)", cmd); |
---|
330 | } |
---|
331 | free(cmd); |
---|
332 | } |
---|
333 | // return result; |
---|
334 | if (error) printf("ARB_textfiles_have_difflines(%s, %s) fails: %s\n", file1, file2, error); |
---|
335 | return !error; |
---|
336 | } |
---|
337 | |
---|
338 | #ifdef UNIT_TESTS |
---|
339 | #include <test_unit.h> |
---|
340 | |
---|
341 | size_t ARB_test_mem_equal(const unsigned char *buf1, const unsigned char *buf2, size_t common, size_t blockStartAddress) { |
---|
342 | size_t equal_bytes; |
---|
343 | if (memcmp(buf1, buf2, common) == 0) { |
---|
344 | equal_bytes = common; |
---|
345 | } |
---|
346 | else { |
---|
347 | equal_bytes = 0; |
---|
348 | size_t x = 0; // position inside memory |
---|
349 | while (buf1[x] == buf2[x]) { |
---|
350 | x++; |
---|
351 | equal_bytes++; |
---|
352 | } |
---|
353 | |
---|
354 | const size_t DUMP = 7; |
---|
355 | size_t y1 = x >= DUMP ? x-DUMP : 0; |
---|
356 | size_t y2 = (x+2*DUMP)>common ? common : (x+2*DUMP); |
---|
357 | size_t blockstart = blockStartAddress+equal_bytes-x; |
---|
358 | |
---|
359 | for (size_t y = y1; y <= y2; y++) { |
---|
360 | fprintf(stderr, "[0x%04zx]", blockstart+y); |
---|
361 | arb_test::print_pair(buf1[y], buf2[y]); |
---|
362 | fputc(' ', stderr); |
---|
363 | arb_test::print_hex_pair(buf1[y], buf2[y]); |
---|
364 | if (buf1[y] != buf2[y]) fputs(" <- diff", stderr); |
---|
365 | fputc('\n', stderr); |
---|
366 | } |
---|
367 | if (y2 == common) { |
---|
368 | fputs("[end of block - truncated]\n", stderr); |
---|
369 | } |
---|
370 | } |
---|
371 | return equal_bytes; |
---|
372 | } |
---|
373 | |
---|
374 | bool ARB_files_are_equal(const char *file1, const char *file2) { |
---|
375 | const char *error = NULL; |
---|
376 | FILE *fp1 = fopen(file1, "rb"); |
---|
377 | |
---|
378 | if (!fp1) { |
---|
379 | printf("can't open '%s'\n", file1); |
---|
380 | error = "i/o error"; |
---|
381 | } |
---|
382 | else { |
---|
383 | FILE *fp2 = fopen(file2, "rb"); |
---|
384 | if (!fp2) { |
---|
385 | printf("can't open '%s'\n", file2); |
---|
386 | error = "i/o error"; |
---|
387 | } |
---|
388 | else { |
---|
389 | const int BLOCKSIZE = 4096; |
---|
390 | unsigned char *buf1 = (unsigned char*)malloc(BLOCKSIZE); |
---|
391 | unsigned char *buf2 = (unsigned char*)malloc(BLOCKSIZE); |
---|
392 | size_t equal_bytes = 0; |
---|
393 | |
---|
394 | while (!error) { |
---|
395 | int read1 = fread(buf1, 1, BLOCKSIZE, fp1); |
---|
396 | int read2 = fread(buf2, 1, BLOCKSIZE, fp2); |
---|
397 | size_t common = read1<read2 ? read1 : read2; |
---|
398 | |
---|
399 | if (!common) { |
---|
400 | if (read1 != read2) error = "filesize differs"; |
---|
401 | break; |
---|
402 | } |
---|
403 | |
---|
404 | size_t thiseq = ARB_test_mem_equal(buf1, buf2, common, equal_bytes); |
---|
405 | if (thiseq != common) { |
---|
406 | error = "content differs"; |
---|
407 | } |
---|
408 | equal_bytes += thiseq; |
---|
409 | } |
---|
410 | |
---|
411 | if (error) printf("files_are_equal: equal_bytes=%zu\n", equal_bytes); |
---|
412 | arb_assert(error || equal_bytes); // comparing empty files is nonsense |
---|
413 | |
---|
414 | free(buf2); |
---|
415 | free(buf1); |
---|
416 | fclose(fp2); |
---|
417 | } |
---|
418 | fclose(fp1); |
---|
419 | } |
---|
420 | |
---|
421 | if (error) printf("files_are_equal(%s, %s) fails: %s\n", file1, file2, error); |
---|
422 | return !error; |
---|
423 | } |
---|
424 | |
---|
425 | void TEST_diff_files() { |
---|
426 | // files are in ../UNIT_TESTER/run/diff |
---|
427 | const char *file = "diff/base.input"; |
---|
428 | const char *file_swapped = "diff/swapped.input"; |
---|
429 | const char *file_date_swapped = "diff/date_swapped.input"; |
---|
430 | const char *file_date_changed = "diff/date_changed.input"; |
---|
431 | |
---|
432 | TEST_EXPECT(ARB_textfiles_have_difflines(file, file, 0, 0)); // check identity |
---|
433 | |
---|
434 | // check if swapped lines are detected properly |
---|
435 | TEST_EXPECT(ARB_textfiles_have_difflines(file, file_swapped, 1, 0)); |
---|
436 | TEST_EXPECT(ARB_textfiles_have_difflines(file, file_swapped, 1, 1)); |
---|
437 | TEST_EXPECT(ARB_textfiles_have_difflines(file, file_date_swapped, 3, 0)); |
---|
438 | TEST_EXPECT(ARB_textfiles_have_difflines(file, file_date_swapped, 3, 1)); |
---|
439 | |
---|
440 | TEST_EXPECT(ARB_textfiles_have_difflines(file, file_date_changed, 0, 1)); |
---|
441 | TEST_EXPECT(ARB_textfiles_have_difflines(file, file_date_changed, 6, 0)); |
---|
442 | |
---|
443 | TEST_EXPECT(ARB_textfiles_have_difflines(file_date_swapped, file_date_changed, 6, 0)); |
---|
444 | TEST_EXPECT(ARB_textfiles_have_difflines(file_date_swapped, file_date_changed, 0, 1)); |
---|
445 | |
---|
446 | const char *binary = "TEST_gpt.arb.expected"; // a binary arb DB |
---|
447 | const char *binary2 = "TEST_gpt.arb.pt.expected"; // a ptserver index |
---|
448 | const char *text = file; |
---|
449 | |
---|
450 | // diff between text and binary should fail |
---|
451 | TEST_REJECT(ARB_textfiles_have_difflines(text, binary, 0, 0)); |
---|
452 | TEST_REJECT(ARB_textfiles_have_difflines(binary, text, 0, 0)); |
---|
453 | TEST_REJECT(ARB_textfiles_have_difflines(binary2, text, 0, 0)); |
---|
454 | TEST_REJECT(ARB_textfiles_have_difflines(text, binary2, 0, 0)); |
---|
455 | |
---|
456 | // diff between two binaries shall fails as well .. |
---|
457 | TEST_REJECT(ARB_textfiles_have_difflines(binary, binary2, 0, 0)); |
---|
458 | TEST_REJECT(ARB_textfiles_have_difflines(binary2, binary, 0, 0)); |
---|
459 | |
---|
460 | // when files do not differ, ARB_textfiles_have_difflines always |
---|
461 | // returns true - even if the files are binary |
---|
462 | // (unwanted but accepted behavior) |
---|
463 | TEST_EXPECT(ARB_textfiles_have_difflines(binary, binary, 0, 0)); |
---|
464 | TEST_EXPECT(ARB_textfiles_have_difflines(binary2, binary2, 0, 0)); |
---|
465 | TEST_EXPECT(ARB_textfiles_have_difflines(text, text, 0, 0)); |
---|
466 | } |
---|
467 | |
---|
468 | // -------------------------------------------------------------------------------- |
---|
469 | // tests for global code included from central ARB headers (located in $ARBHOME/TEMPLATES) |
---|
470 | |
---|
471 | // gcc reports: "warning: logical 'or' of collectively exhaustive tests is always true" |
---|
472 | // for 'implicated(any, any)'. True, obviously. Nevertheless annoying. |
---|
473 | #pragma GCC diagnostic ignored "-Wlogical-op" |
---|
474 | |
---|
475 | void TEST_logic() { |
---|
476 | #define FOR_ANY_BOOL(name) for (int name = 0; name<2; ++name) |
---|
477 | |
---|
478 | TEST_EXPECT(implicated(true, true)); |
---|
479 | // for (int any = 0; any<2; ++any) { |
---|
480 | FOR_ANY_BOOL(any) { |
---|
481 | TEST_EXPECT(implicated(false, any)); // "..aus Falschem folgt Beliebiges.." |
---|
482 | TEST_EXPECT(implicated(any, any)); |
---|
483 | |
---|
484 | TEST_EXPECT(correlated(any, any)); |
---|
485 | TEST_REJECT(correlated(any, !any)); |
---|
486 | TEST_REJECT(contradicted(any, any)); |
---|
487 | TEST_EXPECT(contradicted(any, !any)); |
---|
488 | } |
---|
489 | |
---|
490 | TEST_EXPECT(correlated(false, false)); |
---|
491 | |
---|
492 | TEST_EXPECT(contradicted(true, false)); |
---|
493 | TEST_EXPECT(contradicted(false, true)); |
---|
494 | |
---|
495 | FOR_ANY_BOOL(kermitIsFrog) { |
---|
496 | FOR_ANY_BOOL(kermitIsGreen) { |
---|
497 | bool allFrogsAreGreen = implicated(kermitIsFrog, kermitIsGreen); |
---|
498 | bool onlyFrogsAreGreen = implicated(kermitIsGreen, kermitIsFrog); |
---|
499 | |
---|
500 | TEST_EXPECT(implicated( kermitIsFrog && allFrogsAreGreen, kermitIsGreen)); |
---|
501 | TEST_EXPECT(implicated(!kermitIsGreen && allFrogsAreGreen, !kermitIsFrog)); |
---|
502 | |
---|
503 | TEST_EXPECT(implicated( kermitIsGreen && onlyFrogsAreGreen, kermitIsFrog)); |
---|
504 | TEST_EXPECT(implicated(!kermitIsFrog && onlyFrogsAreGreen, !kermitIsGreen)); |
---|
505 | |
---|
506 | TEST_EXPECT(implicated(kermitIsFrog && !kermitIsGreen, !allFrogsAreGreen)); |
---|
507 | TEST_EXPECT(implicated(kermitIsGreen && !kermitIsFrog, !onlyFrogsAreGreen)); |
---|
508 | |
---|
509 | TEST_EXPECT(correlated( |
---|
510 | correlated(kermitIsGreen, kermitIsFrog), |
---|
511 | allFrogsAreGreen && onlyFrogsAreGreen |
---|
512 | )); |
---|
513 | } |
---|
514 | } |
---|
515 | } |
---|
516 | |
---|
517 | #include <cmath> |
---|
518 | // #include <math.h> |
---|
519 | |
---|
520 | void TEST_naninf() { |
---|
521 | double num = 3.1415; |
---|
522 | double zero = num-num; |
---|
523 | double inf = num/zero; |
---|
524 | double ninf = -inf; |
---|
525 | double nan = 0*inf; |
---|
526 | |
---|
527 | TEST_EXPECT_DIFFERENT(inf, ninf); |
---|
528 | TEST_EXPECT_EQUAL(ninf, -1.0/zero); |
---|
529 | |
---|
530 | // decribe how isnan, isinf and isnormal shall behave: |
---|
531 | #define TEST_ISINF(isinf_fun) \ |
---|
532 | TEST_EXPECT(isinf_fun(inf)); \ |
---|
533 | TEST_EXPECT(!!isinf_fun(ninf)); \ |
---|
534 | TEST_EXPECT(isinf_fun(INFINITY)); \ |
---|
535 | TEST_EXPECT(!isinf_fun(nan)); \ |
---|
536 | TEST_EXPECT(!isinf_fun(NAN)); \ |
---|
537 | TEST_EXPECT(!isinf_fun(num)); \ |
---|
538 | TEST_EXPECT(!isinf_fun(zero)) |
---|
539 | |
---|
540 | #define TEST_ISNAN(isnan_fun) \ |
---|
541 | TEST_EXPECT(isnan_fun(nan)); \ |
---|
542 | TEST_EXPECT(isnan_fun(NAN)); \ |
---|
543 | TEST_EXPECT(!isnan_fun(inf)); \ |
---|
544 | TEST_EXPECT(!isnan_fun(ninf)); \ |
---|
545 | TEST_EXPECT(!isnan_fun(INFINITY)); \ |
---|
546 | TEST_EXPECT(!isnan_fun(zero)); \ |
---|
547 | TEST_EXPECT(!isnan_fun(num)) |
---|
548 | |
---|
549 | #define TEST_ISNORMAL(isnormal_fun) \ |
---|
550 | TEST_EXPECT(isnormal_fun(num)); \ |
---|
551 | TEST_EXPECT(!isnormal_fun(zero)); \ |
---|
552 | TEST_EXPECT(!isnormal_fun(inf)); \ |
---|
553 | TEST_EXPECT(!isnormal_fun(ninf)); \ |
---|
554 | TEST_EXPECT(!isnormal_fun(nan)); \ |
---|
555 | TEST_EXPECT(!isnormal_fun(INFINITY)); \ |
---|
556 | TEST_EXPECT(!isnormal_fun(NAN)) |
---|
557 | |
---|
558 | // check behavior of arb templates: |
---|
559 | TEST_ISNAN(is_nan); |
---|
560 | TEST_ISINF(is_inf); |
---|
561 | TEST_ISNORMAL(is_normal); |
---|
562 | |
---|
563 | #if (GCC_PATCHLEVEL_CODE < 50301) |
---|
564 | // (section leads to compile error with gcc 5.3.1 - suggesting std::isnan and std::isinf) |
---|
565 | // document behavior of math.h macros: |
---|
566 | TEST_ISNAN(isnan); |
---|
567 | # if (GCC_PATCHLEVEL_CODE >= 40403 && GCC_PATCHLEVEL_CODE <= 40407 && defined(DEBUG)) |
---|
568 | // TEST_ISINF(isinf); // isinf macro is broken (gcc 4.4.3 up to gcc 4.4.7, if compiled with DEBUG) |
---|
569 | TEST_EXPECT__BROKEN(isinf(ninf)); // broken; contradicts http://www.cplusplus.com/reference/cmath/isinf/ |
---|
570 | # else |
---|
571 | TEST_ISINF(isinf); |
---|
572 | # endif |
---|
573 | // TEST_ISNORMAL(isnormal); // ok if <math.h> included, compile error if <cmath> included |
---|
574 | #endif |
---|
575 | |
---|
576 | // check behavior of std-versions: |
---|
577 | TEST_ISNAN(std::isnan); |
---|
578 | TEST_ISINF(std::isinf); |
---|
579 | TEST_ISNORMAL(std::isnormal); |
---|
580 | } |
---|
581 | |
---|
582 | #endif // UNIT_TESTS |
---|