source: tags/svn.1.5.4/TOOLS/arb_test.cxx

Last change on this file was 8268, checked in by westram, 14 years ago
  • removed unused(-but-set) variables/params
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.7 KB
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : arb_test.cxx                                      //
4//   Purpose   : unit tester for tools                             //
5//                                                                 //
6//   Coded by Ralf Westram (coder@reallysoft.de) in February 2011  //
7//   Institute of Microbiology (Technical University Munich)       //
8//   http://www.arb-home.de/                                       //
9//                                                                 //
10// =============================================================== //
11
12#include <arbdbt.h>
13#include <arb_defs.h>
14#include <unistd.h>
15
16int ARB_main(int , const char *[]) {
17    fputs("don't call us\n", stderr);
18    return EXIT_SUCCESS;
19}
20
21// --------------------------------------------------------------------------------
22
23#ifdef UNIT_TESTS
24#include <arb_file.h>
25#include <test_unit.h>
26#include <ut_valgrinded.h>
27
28// --------------------------------------------------------------------------------
29
30
31inline void make_valgrinded_call_from_pipe(char *&command) {
32    using namespace utvg;
33    static valgrind_info valgrind;
34    if (valgrind.wanted) {
35        char *pipeSym = strchr(command, '|');
36        if (pipeSym) {
37            char *left  = GB_strpartdup(command, pipeSym-1);
38            char *right = strdup(pipeSym+1);
39
40            make_valgrinded_call(left);
41            make_valgrinded_call_from_pipe(right);
42
43            freeset(command, GBS_global_string_copy("%s | %s", left, right));
44
45            free(right);
46            free(left);
47        }
48        else {
49            make_valgrinded_call(command);
50        }
51    }
52}
53
54inline GB_ERROR valgrinded_system(const char *cmdline) {
55    char *cmddup = strdup(cmdline);
56    make_valgrinded_call_from_pipe(cmddup);
57
58    GB_ERROR error = GBK_system(cmddup);
59    free(cmddup);
60    return error;
61}
62
63#define RUN_TOOL(cmdline)                valgrinded_system(cmdline)
64#define RUN_TOOL_NEVER_VALGRIND(cmdline) GBK_system(cmdline)
65
66#define TEST_RUN_TOOL(cmdline)                TEST_ASSERT_NO_ERROR(RUN_TOOL(cmdline))
67#define TEST_RUN_TOOL_NEVER_VALGRIND(cmdline) TEST_ASSERT_NO_ERROR(RUN_TOOL_NEVER_VALGRIND(cmdline))
68
69void TEST_SLOW_ascii_2_bin_2_ascii() {
70    const char *ascii_ORG  = "TEST_loadsave_ascii.arb";
71    const char *ascii      = "bin2ascii.arb";
72    const char *binary     = "ascii2bin.arb";
73    const char *binary_2ND = "ascii2bin2.arb";
74
75    // test conversion file -> file
76    TEST_RUN_TOOL(GBS_global_string("arb_2_bin   %s %s", ascii_ORG, binary));
77    TEST_RUN_TOOL(GBS_global_string("arb_2_ascii %s %s", binary, ascii));
78
79    TEST_ASSERT_FILES_EQUAL(ascii, ascii_ORG);
80
81    // test conversion (bin->ascii->bin) via stream (this tests 'arb_repair')
82    TEST_RUN_TOOL(GBS_global_string("arb_2_ascii %s - | arb_2_bin - %s", binary, binary_2ND));
83
84    // TEST_ASSERT_FILES_EQUAL(binary, binary_2ND); // can't compare binary files (they contain undefined bytes)
85
86    // instead convert back to ascii and compare result with original
87    TEST_ASSERT_ZERO_OR_SHOW_ERRNO(GB_unlink(ascii));
88    TEST_RUN_TOOL(GBS_global_string("arb_2_ascii %s %s", binary_2ND, ascii));
89    TEST_ASSERT_FILES_EQUAL(ascii, ascii_ORG);
90
91    TEST_ASSERT_ZERO_OR_SHOW_ERRNO(GB_unlink(ascii));
92    TEST_ASSERT_ZERO_OR_SHOW_ERRNO(GB_unlink(binary));
93    TEST_ASSERT_ZERO_OR_SHOW_ERRNO(GB_unlink(binary_2ND));
94    TEST_ASSERT_ZERO_OR_SHOW_ERRNO(GB_unlink("ascii2bin.ARF"));
95    TEST_ASSERT_ZERO_OR_SHOW_ERRNO(GB_unlink("ascii2bin2.ARF"));
96}
97
98void TEST_arb_primer() {
99    const char *primer_db       = "TEST_nuc.arb";
100    const char *primer_stdin    = "tools/arb_primer.in";
101    const char *primer_out      = "tools/arb_primer.out";
102    const char *primer_expected = "tools/arb_primer_expected.out";
103
104    TEST_RUN_TOOL(GBS_global_string("arb_primer %s < %s", primer_db, primer_stdin));
105    TEST_ASSERT_FILES_EQUAL(primer_out, primer_expected);
106    TEST_ASSERT_ZERO_OR_SHOW_ERRNO(GB_unlink(primer_out));
107}
108
109static GB_ERROR removeVaryingDateFromTreeRemarks(const char *dbname) {
110    GB_ERROR  error     = NULL;
111    GB_shell  shell;
112    GBDATA   *gb_main   = GB_open(dbname, "rw");
113    if (!gb_main) error = GB_await_error();
114    else {
115        {
116            GB_transaction  ta(gb_main);
117            GBDATA         *gb_tree_data    = GB_entry(gb_main, "tree_data");
118            const char     *truncate_after  = "\nunittest-tree\n";
119            size_t          truncate_offset = strlen(truncate_after);
120
121            if (!gb_tree_data) error = GB_await_error();
122            else {
123                for (GBDATA *gb_tree = GB_child(gb_tree_data);
124                     gb_tree && !error;
125                     gb_tree = GB_nextChild(gb_tree))
126                {
127                    GBDATA *gb_remark = GB_entry(gb_tree, "remark");
128                    if (!gb_remark) {
129                        error = "could not find 'remark' entry";
130                    }
131                    else {
132                        char *remark = GB_read_string(gb_remark);
133                        char *found  = strstr(remark, truncate_after);
134
135                        if (found) {
136                            strcpy(found+truncate_offset, "<date removed for testing>");
137                            error                  = GB_write_string(gb_remark, remark);
138                        }
139                        free(remark);
140                    }
141                }
142            }
143
144            ta.close(error);
145        }
146        if (!error) error = GB_save_as(gb_main, dbname, "a");
147        GB_close(gb_main);
148    }
149    return error;
150}
151
152void TEST_SLOW_arb_read_tree() {
153    struct {
154        const char *basename;
155        const char *extraArgs;
156    }
157    run[] = {
158        { "newick",           "" },
159        { "newick_sq",        "-commentFromFile general/text.input" },
160        { "newick_dq",        "-scale 0.5" },
161        { "newick_group",     "-scale 10 -consense 10" },
162        { "newick_len",       "" },
163        { "newick_len_group", "" },
164    };
165
166    const char *dbin       = "min_ascii.arb";
167    const char *dbout      = "tools/read_tree_out.arb";
168    const char *dbexpected = "tools/read_tree_out_expected.arb";
169
170    for (size_t b = 0; b<ARRAY_ELEMS(run); ++b) {
171        const char *basename  = run[b].basename;
172        const char *extraArgs = run[b].extraArgs;
173        char       *treefile  = GBS_global_string_copy("tools/%s.tree", basename);
174        char       *treename  = GBS_global_string_copy("tree_%s", basename);
175
176        TEST_RUN_TOOL(GBS_global_string("arb_read_tree -db %s %s %s %s \"test %s\" %s",
177                                                   dbin, dbout, treename, treefile, basename, extraArgs));
178
179        dbin = dbout; // use out-db from previous loop ( = write all trees into one db)
180
181        free(treename);
182        free(treefile);
183    }
184
185    TEST_ASSERT_NO_ERROR(removeVaryingDateFromTreeRemarks(dbout));
186    TEST_ASSERT_TEXTFILES_EQUAL(dbout, dbexpected);
187    TEST_ASSERT_ZERO_OR_SHOW_ERRNO(GB_unlink(dbout));
188}
189
190#define TEST_ARB_REPLACE(infile,expected,args) do {                     \
191        char *tmpfile = GBS_global_string_copy("%s.tmp", expected);     \
192        TEST_RUN_TOOL_NEVER_VALGRIND(GBS_global_string("cp %s %s", infile, tmpfile));  \
193        TEST_RUN_TOOL(GBS_global_string("arb_replace %s %s", args, tmpfile)); \
194        TEST_ASSERT_TEXTFILES_EQUAL(tmpfile, expected);                 \
195        TEST_ASSERT_ZERO_OR_SHOW_ERRNO(GB_unlink(tmpfile));             \
196        free(tmpfile);                                                  \
197    } while(0)
198
199void TEST_arb_replace() {
200    const char *infile = "tools/arb_replace.in";
201    const char *file1  = "tools/arb_replace_1.out";
202    const char *file2  = "tools/arb_replace_2.out";
203
204    TEST_ARB_REPLACE(infile, "tools/arb_replace_1.out", "'gene=GONE'");
205    TEST_ARB_REPLACE(file1,  infile,                    "-l 'GONE=gene'");
206    TEST_ARB_REPLACE(file1,  file2,                     "-L 'GONE=gene:\"*\"=( * )'");
207}
208
209// --------------------------------------------------------------------------------
210
211class CommandOutput : virtual Noncopyable {
212    char     *stdoutput; // output from command
213    char     *stderrput;
214    GB_ERROR  error;
215
216    void appendError(GB_ERROR err) {
217        if (!error) error = err;
218        else error = GBS_global_string("%s\n%s", error, err);
219    }
220
221    char *readAndUnlink(const char *file) {
222        char *content = GB_read_file(file);
223        if (!content) appendError(GB_await_error());
224        int res = GB_unlink(file);
225        if (res == -1) appendError(GB_await_error());
226        return content;
227    }
228public:
229    CommandOutput(const char *command, bool try_valgrind)
230        : stdoutput(NULL), stderrput(NULL), error(NULL)
231    {
232        char *escaped = GBS_string_eval(command, "'=\\\\'", NULL);
233        if (!escaped) {
234            appendError(GB_await_error());
235        }
236        else {
237            if (try_valgrind) make_valgrinded_call(escaped);
238
239            char *cmd = GBS_global_string_copy("bash -c '%s >stdout.log 2>stderr.log'", escaped);
240
241            appendError(GBK_system(cmd));
242            free(cmd);
243            free(escaped);
244
245            stdoutput = readAndUnlink("stdout.log");
246            stderrput = readAndUnlink("stderr.log");
247        }
248        if (error) {
249            printf("command '%s'\n"
250                   "escaped command '%s'\n"
251                   "stdout='%s'\n"
252                   "stderr='%s'\n", 
253                   command, escaped, stdoutput, stderrput);
254        }
255    }
256    ~CommandOutput() {
257        free(stderrput);
258        free(stdoutput);
259    }
260
261    GB_ERROR get_error() const { return error; }
262    const char *get_stdoutput() const { return stdoutput; }
263    const char *get_stderrput() const { return stderrput; }
264};
265
266// --------------------------------------------------------------------------------
267
268#define TEST_OUTPUT_EQUALS(cmd, expected_std, expected_err)             \
269    do {                                                                \
270        CommandOutput out(cmd, expected_err == NULL);                   \
271        TEST_ASSERT_NO_ERROR(out.get_error());                          \
272        if (expected_std) { TEST_ASSERT_EQUAL(out.get_stdoutput(), expected_std); } \
273        if (expected_err) { TEST_ASSERT_EQUAL(out.get_stderrput(), expected_err); } \
274    } while(0)
275
276#define TEST_OUTPUT_CONTAINS(cmd, expected_std, expected_err)           \
277    do {                                                                \
278        CommandOutput out(cmd, expected_err == NULL);                   \
279        TEST_ASSERT_NO_ERROR(out.get_error());                          \
280        if (expected_std) { TEST_ASSERT_CONTAINS(out.get_stdoutput(), expected_std); } \
281        if (expected_err) { TEST_ASSERT_CONTAINS(out.get_stderrput(), expected_err); } \
282    } while(0)
283
284#define TEST_OUTPUT_HAS_CHECKSUM(cmd, checksum)                         \
285    do {                                                                \
286        CommandOutput out(cmd, false);                                  \
287        TEST_ASSERT_NO_ERROR(out.get_error());                          \
288        uint32_t      css = GBS_checksum(out.get_stdoutput(), false, ""); \
289        uint32_t      cse = GBS_checksum(out.get_stderrput(), false, ""); \
290        TEST_ASSERT_EQUAL(css^cse, uint32_t(checksum));                 \
291    } while(0)
292
293#define TEST_STDOUT_EQUALS(cmd, expected_std) TEST_OUTPUT_EQUALS(cmd, expected_std, (const char *)NULL)
294#define TEST_STDERR_EQUALS(cmd, expected_err) TEST_OUTPUT_EQUALS(cmd, (const char *)NULL, expected_err)
295
296#define TEST_STDOUT_CONTAINS(cmd, part) TEST_OUTPUT_CONTAINS(cmd, part, "")
297
298// --------------------------------------------------------------------------------
299
300void TEST_arb_message() {
301    TEST_STDERR_EQUALS("arb_message \"this is the test message\"",
302                       "arb_message: this is the test message\n");
303}
304
305void TEST_SLOW_arb_probe() {
306    // test called here currently are duplicating the tests in
307    // arb_probe.cxx@TEST_SLOW_match_probe
308    // and arb_probe.cxx@TEST_SLOW_design_probe
309    //
310    // Here test of functionality is secondary.
311    // The primary goal here is to test calling the tools (i.e. arb_probe)
312   
313    TEST_SETUP_GLOBAL_ENVIRONMENT("ptserver");
314    TEST_STDOUT_EQUALS("arb_probe"
315                       " serverid=-666"
316                       " matchsequence=UAUCGGAGAGUUUGA",
317
318                       /* ---- */ "    name---- fullname mis N_mis wmis pos ecoli rev          'UAUCGGAGAGUUUGA'\1"
319                       "BcSSSS00\1" "  BcSSSS00            0     0  0.0   3     2 0   .......UU-===============-UCAAGUCGA\1"
320        );
321
322    TEST_STDOUT_EQUALS("arb_probe"
323                       " serverid=-666"
324                       " designnames=ClnCorin#CltBotul#CPPParap#ClfPerfr"
325                       " designmintargets=100",
326
327                       "Probe design Parameters:\n"
328                       "Length of probe      18\n"
329                       "Temperature        [ 0.0 -400.0]\n"
330                       "GC-Content         [30.0 -80.0]\n"
331                       "E.Coli Position    [any]\n"
332                       "Max Non Group Hits     0\n"
333                       "Min Group Hits       100%\n"
334                       "Target             le     apos ecol grps  G+C 4GC+2AT Probe sequence     | Decrease T by n*.3C -> probe matches n non group species\n"
335                       "CGAAAGGAAGAUUAAUAC 18 A=    94   82    4 33.3 48.0    GUAUUAAUCUUCCUUUCG |  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,\n"
336                       "GAAAGGAAGAUUAAUACC 18 A+     1   83    4 33.3 48.0    GGUAUUAAUCUUCCUUUC |  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,\n"
337                       "UCAAGUCGAGCGAUGAAG 18 B=    18   17    4 50.0 54.0    CUUCAUCGCUCGACUUGA |  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,\n"
338                       "AUCAAGUCGAGCGAUGAA 18 B-     1   16    4 44.4 52.0    UUCAUCGCUCGACUUGAU |  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1,  1,  2,  3,\n"
339                       );
340}
341
342#define IN_DB     "tools/dnarates.arb"
343#define OUT_DB    "tools/dnarates_result.arb"
344#define WANTED_DB "tools/dnarates_expected.arb"
345
346void TEST_SLOW_arb_dna_rates() {
347    TEST_STDOUT_CONTAINS("arb_dnarates tools/dnarates.inp " IN_DB " " OUT_DB, "\nWriting 'POS_VAR_BY_ML_1'\n");
348    TEST_ASSERT_FILES_EQUAL(OUT_DB, WANTED_DB);
349    TEST_ASSERT_ZERO_OR_SHOW_ERRNO(GB_unlink(OUT_DB));
350}
351
352#define RATES_DB "tools/exportrates.arb"
353
354void TEST_arb_export_rates() {
355    // Note: just testing against regression here.
356    // Since the output is quite longish, we just test the checksums of the results.
357    //
358    // If one of the checksums changes unexpectedly and you want to see more details about the change,
359    // - go back to a revision with a correct checksum,
360    // - add passing TEST_OUTPUT_EQUALS for broken command and
361    // - move that test to broken revision.
362
363    TEST_OUTPUT_HAS_CHECKSUM("arb_export_rates -d " RATES_DB " POS_VAR_BY_PARSIMONY", 0xc75a5fad);
364    TEST_OUTPUT_HAS_CHECKSUM("arb_export_rates -d " RATES_DB " -r POS_VAR_BY_PARSIMONY", 0xd69fb01e);
365    TEST_OUTPUT_HAS_CHECKSUM("arb_export_rates -d " RATES_DB " -r \"\"", 0xad0461ce);
366}
367
368#define TREE_DB "tools/tree.arb"
369
370void TEST_arb_export_tree() {
371    TEST_STDOUT_EQUALS("arb_export_tree tree_mini " TREE_DB,
372                       "((( 'VibFurni' :0.02952, 'VibVulni' :0.01880):0.04015, 'VibChole' :0.03760):1.00000,( 'AcnPleur' :0.12011, 'PrtVulga' :0.06756):1.00000, 'HlmHalod' :1.00000);\n");
373    TEST_STDOUT_EQUALS("arb_export_tree --bifurcated tree_mini " TREE_DB,
374                       "(((( 'VibFurni' :0.02952, 'VibVulni' :0.01880):0.04015, 'VibChole' :0.03760):0.04610,( 'AcnPleur' :0.12011, 'PrtVulga' :0.06756):0.01732):0.07176, 'HlmHalod' :0.12399)'inner';\n");
375    TEST_STDOUT_EQUALS("arb_export_tree --doublequotes tree_mini " TREE_DB,
376                       "((( \"VibFurni\" :0.02952, \"VibVulni\" :0.01880):0.04015, \"VibChole\" :0.03760):1.00000,( \"AcnPleur\" :0.12011, \"PrtVulga\" :0.06756):1.00000, \"HlmHalod\" :1.00000);\n");
377
378    TEST_STDOUT_EQUALS("arb_export_tree --nobranchlens tree_mini " TREE_DB,
379                       "((( 'VibFurni'  'VibVulni' ) 'VibChole' ),( 'AcnPleur'  'PrtVulga' ), 'HlmHalod' );\n");
380    TEST_ASSERT__BROKEN(0); // the test above returns a wrong result (commas are missing)
381
382    TEST_OUTPUT_EQUALS("arb_export_tree \"\" " TREE_DB,
383                       ";\n",                                                                    // shall export an empty newick tree
384                       "");                                                                      // without error!
385    TEST_OUTPUT_EQUALS("arb_export_tree tree_nosuch " TREE_DB,
386                       ";\n",                                                                    // shall export an empty newick tree
387                       "arb_export_tree: Tree 'tree_nosuch' does not exist in DB '" TREE_DB "'\n"); // with error!
388}
389
390static char *notification_result = NULL;
391static void test_notification_cb(const char *message, void *cd) {
392    const char *cds     = (const char *)cd;
393    notification_result = GBS_global_string_copy("message='%s' cd='%s'", message, cds);
394}
395
396#define INIT_NOTIFICATION                                                                       \
397    GB_shell shell;                                                                             \
398    GBDATA *gb_main = GBT_open("nosuch.arb", "crw");                                            \
399    const char *cd  = "some argument";                                                          \
400    char *cmd = GB_generate_notification(gb_main, test_notification_cb, "the note", (void*)cd)
401
402#define EXIT_NOTIFICATION       \
403    GB_close(gb_main);          \
404    free(cmd)
405   
406#define TEST_DBSERVER_OPEN(gbmain) TEST_ASSERT_NO_ERROR(GBCMS_open(":", 0, gbmain))
407#define TEST_DBSERVER_SERVE_UNTIL(gbmain, cond) do {                    \
408        bool success            = GBCMS_accept_calls(gb_main, false);   \
409        while (success) success = GBCMS_accept_calls(gb_main, true);    \
410        GB_usleep(10000);                                               \
411    } while(!(cond))
412
413#define TEST_DBSERVER_CLOSE(gbmain) GBCMS_shutdown(gb_main)
414
415void TEST_close_with_pending_notification() {
416    INIT_NOTIFICATION;
417    EXIT_NOTIFICATION;
418}
419void TEST_close_after_pending_notification_removed() {
420    INIT_NOTIFICATION;
421    TEST_ASSERT_NO_ERROR(GB_remove_last_notification(gb_main));
422    EXIT_NOTIFICATION;
423}
424void TEST_arb_notify() {
425    INIT_NOTIFICATION;
426
427    TEST_DBSERVER_OPEN(gb_main);
428
429    TEST_ASSERT_NULL(notification_result);
430    TEST_ASSERT_NO_ERROR(GBK_system(GBS_global_string("%s &", cmd))); // async call to arb_notify
431
432    TEST_DBSERVER_SERVE_UNTIL(gb_main, notification_result);
433    TEST_DBSERVER_CLOSE(gb_main);
434
435    TEST_ASSERT_EQUAL(notification_result, "message='the note' cd='some argument'");
436    freenull(notification_result);
437
438    EXIT_NOTIFICATION;
439}
440
441#endif // UNIT_TESTS
442
Note: See TracBrowser for help on using the repository browser.