source: trunk/CORE/arb_diff.cxx

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