ARB
arb_consensus_tree.cxx
Go to the documentation of this file.
1 // ============================================================= //
2 // //
3 // File : arb_consensus_tree.cxx //
4 // Purpose : build consensus tree with the same library //
5 // as ARB-NJ //
6 // //
7 // Coded by Ralf Westram (coder@reallysoft.de) in March 2012 //
8 // Institute of Microbiology (Technical University Munich) //
9 // http://www.arb-home.de/ //
10 // //
11 // ============================================================= //
12 
13 #include <CT_ctree.hxx>
14 #include <TreeRead.h>
15 #include <TreeWrite.h>
16 #include <arb_str.h>
17 #include <arb_diff.h>
18 #include <arb_defs.h>
19 
20 using namespace std;
21 
22 static TreeNode *build_consensus_tree(const CharPtrArray& input_trees, GB_ERROR& error, size_t& different_species, double weight, char *&comment) {
23  // read all input trees, generate and return consensus tree
24  // (Note: the 'weight' used here doesn't matter atm, since all trees are added with the same weight)
25 
26  arb_assert(!error);
27  error = NULp;
28  comment = NULp;
29 
30  TreeNode *consense_tree = NULp;
31  if (input_trees.empty()) {
32  error = "no trees given";
33  }
34  else {
35  ConsensusTreeBuilder tree_builder;
36 
37  for (size_t i = 0; !error && i<input_trees.size(); ++i) {
38  char *warnings = NULp;
39 
40  TreeRoot *root = new SizeAwareRoot; // will be deleted when tree gets deleted
41  SizeAwareTree *tree = DOWNCAST(SizeAwareTree*, TREE_load(input_trees[i], root, NULp, true, &warnings));
42  if (!tree) {
43  error = GBS_global_string("Failed to load tree '%s' (Reason: %s)", input_trees[i], GB_await_error());
44  }
45  else {
46  if (warnings) {
47  GB_warningf("while loading tree '%s':\n%s", input_trees[i], warnings);
48  free(warnings);
49  }
50  tree_builder.add(tree, input_trees[i], weight);
51  }
52  }
53 
54  if (!error) consense_tree = tree_builder.get(different_species, error);
55  if (!error) comment = tree_builder.get_tree_remark();
56  }
57  arb_assert(contradicted(consense_tree, error));
58  return consense_tree;
59 }
60 
61 static char *create_tree_name(const char *savename) {
62  // create a DB treename (using savename as hint)
63  char *tree_name;
64  {
65  // as default use part behind '/' and remove file extension
66  const char *lslash = strrchr(savename, '/');
67  if (lslash) savename = lslash+1;
68 
69  const char *ldot = strrchr(savename, '.');
70 
71  tree_name = ldot ? ARB_strpartdup(savename, ldot-1) : ARB_strdup(savename);
72  if (tree_name[0] == 0) freedup(tree_name, "tree_consensus");
73  }
74 
75  // make sure tree name starts with 'tree_'
76  if (!ARB_strBeginsWith(tree_name, "tree_")) {
77  freeset(tree_name, GBS_global_string_copy("tree_%s", tree_name));
78  }
79  return tree_name;
80 }
81 
82 static GB_ERROR save_tree_as_newick(TreeNode *tree, const char *savename, const char *comment) {
83  // save a tree to a newick file
84 
85  // since ARB only saves trees out of a database,
86  // i use a hack here:
87  // - create temp DB
88  // - save tree there
89  // - save to newick as usual
90 
91  GB_shell shell;
92  GBDATA *gb_main = GB_open("", "crw");
94 
95  if (!gb_main) {
96  error = GB_await_error();
97  }
98  else {
99  char *db_tree_name = create_tree_name(savename);
100 
101  {
102  GB_transaction ta(gb_main);
103  error = GBT_write_tree_with_remark(gb_main, db_tree_name, tree, comment);
104  }
105  if (!error) {
106  Node_ID_Labeler labels_use_IDs;
107  error = TREE_write_Newick(gb_main, db_tree_name, labels_use_IDs, true, SAVE_BOOTSTRAPS, true, true, LABEL_SINGLE_QUOTES, savename);
108  }
109 
110  free(db_tree_name);
111  GB_close(gb_main);
112  }
113 
114  if (error) {
115  error = GBS_global_string("Failed to save tree to '%s' (Reason: %s)", savename, error);
116  }
117 
118  return error;
119 }
120 
121 int ARB_main(int argc, char *argv[]) {
122  GB_ERROR error = NULp;
123 
124  if (argc<2) {
125  printf("Usage: arb_consensus_tree [options] [tree]+\n"
126  "Purpose: Create a consensus tree out of multiple trees\n"
127  " options:\n"
128  " -w outfile write consensus tree to outfile\n");
129 
130  // @@@ wanted options
131  // - do not add relative frequency of used subtrees as bootstrap values
132  // - multifurcate branches with bootstrap value below XXX
133  // - eliminate branches with bootstrap value below YYY
134  // - ... ?
135  }
136  else {
137  char *savename = NULp;
138 
139  ConstStrArray input_tree_names;
140 
141  for (int a = 1; a<argc; ++a) {
142  const char *arg = argv[a];
143  if (arg[0] == '-') {
144  switch (arg[1]) {
145  case 'w': savename = ARB_strdup(argv[++a]); break;
146  default : error = GBS_global_string("Unknown switch '-%c'", arg[1]); break;
147  }
148  }
149  else {
150  input_tree_names.put(argv[a]);
151  }
152  }
153 
154  if (!error && input_tree_names.empty()) error = "no input trees specified";
155 
156  if (!error) {
157  size_t species_count;
158  char *comment;
159  TreeNode *cons_tree = build_consensus_tree(input_tree_names, error, species_count, 1.0, comment);
160 
161  if (!cons_tree) {
162  error = GBS_global_string("Failed to build consensus tree (Reason: %s)", error);
163  }
164  else {
165  size_t leafs = GBT_count_leafs(cons_tree);
166  double percent = size_t((leafs*1000)/species_count)/10.0;
167 
168  printf("Generated tree contains %.1f%% of species (%zu of %zu found in input trees)\n",
169  percent, leafs, species_count);
170 
171  if (savename) {
172  error = save_tree_as_newick(cons_tree, savename, comment);
173  }
174  else {
175  printf("successfully created consensus tree\n"
176  "(no savename specified -> tree not saved)\n");
177  }
178  UNCOVERED();
179  destroy(cons_tree);
180  }
181  free(comment);
182  }
183  free(savename);
184  }
185 
186  if (error) {
187  printf("Error in arb_consensus_tree: %s\n", error);
188  }
189 
190  return error ? EXIT_FAILURE : EXIT_SUCCESS;
191 }
192 
193 // --------------------------------------------------------------------------------
194 
195 #ifdef UNIT_TESTS
196 #ifndef TEST_UNIT_H
197 #include <test_unit.h>
198 #endif
199 
200 #include "command_output.h"
201 
202 // #define TEST_AUTO_UPDATE // uncomment to update expected trees (if more than date differs)
203 
204 static char *custom_tree_name(int dir, const char *name) { return GBS_global_string_copy("consense/%i/%s.tree", dir, name); }
205 static char *custom_numbered_tree_name(int dir, const char *name, int treeNr) { return GBS_global_string_copy("consense/%i/%s_%i.tree", dir, name, treeNr); }
206 
207 static void add_inputnames(StrArray& to, int dir, const char *basename, int first_tree, int last_tree) {
208  for (int t = first_tree; t <= last_tree; ++t) {
209  to.put(custom_numbered_tree_name(dir, basename, t));
210  }
211 }
212 
213 static double calc_intree_distance(TreeNode *tree) {
214  if (tree->is_leaf()) return 0.0;
215  return
216  tree->leftlen +
217  tree->rightlen +
218  calc_intree_distance(tree->get_leftson()) +
219  calc_intree_distance(tree->get_rightson());
220 }
221 
222 #define LENSUM_EPSILON .000001
223 
224 static arb_test::match_expectation consense_tree_generated(TreeNode *tree, GB_ERROR error, size_t species_count, size_t expected_species_count, double expected_intree_distance) {
225  using namespace arb_test;
226  expectation_group expected;
227 
228  expected.add(that(error).is_equal_to_NULL());
229  expected.add(that(tree).does_differ_from_NULL());
230 
231  if (tree) {
232  expected.add(that(species_count).is_equal_to(expected_species_count));
233  expected.add(that(GBT_count_leafs(tree)).is_equal_to(expected_species_count));
234  expected.add(that(calc_intree_distance(tree)).fulfills(epsilon_similar(LENSUM_EPSILON), expected_intree_distance));
235  }
236 
237  return all().ofgroup(expected);
238 }
239 
240 static arb_test::match_expectation build_expected_consensus_tree(const int treedir, const char *basename, int first_tree, int last_tree, double weight, const char *outbasename, size_t expected_species_count, double expected_intree_distance) {
241  using namespace arb_test;
242  expectation_group expected;
243  arb_suppress_progress hideProgress;
244 
245  GB_ERROR error = NULp;
246  StrArray input_tree_names;
247  add_inputnames(input_tree_names, treedir, basename, first_tree, last_tree);
248 
249  size_t species_count;
250  char *comment;
251  TreeNode *tree = build_consensus_tree(input_tree_names, error, species_count, weight, comment);
252  expected.add(consense_tree_generated(tree, error, species_count, expected_species_count, expected_intree_distance));
253 
254  char *saveas = custom_tree_name(treedir, outbasename);
255  error = save_tree_as_newick(tree, saveas, comment);
256  expected.add(that(error).is_equal_to_NULL());
257 
258  if (!error) {
259  char *expected_save = custom_tree_name(treedir, GBS_global_string("%s_expected", outbasename));
260  bool exported_as_expected = arb_test::textfiles_have_difflines_ignoreDates(saveas, expected_save, 0);
261 
262 #if defined(TEST_AUTO_UPDATE)
263  if (!exported_as_expected) {
264  TEST_COPY_FILE(saveas, expected_save);
265  }
266 #else // !defined(TEST_AUTO_UPDATE)
267  expected.add(that(exported_as_expected).is_equal_to(true));
268 #endif
270  free(expected_save);
271  }
272 
273  free(saveas);
274  free(comment);
275  destroy(tree);
276 
277  return all().ofgroup(expected);
278 }
279 
280 void TEST_consensus_tree_1() {
281  TEST_EXPECTATION(build_expected_consensus_tree(1, "bootstrapped", 1, 5, 0.7, "consense1", 22, 0.925779));
282  // ../UNIT_TESTER/run/consense/1/consense1.tree
283 }
284 void TEST_consensus_tree_1_single() {
285  TEST_EXPECTATION(build_expected_consensus_tree(1, "bootstrapped", 1, 1, 0.01, "consense1_single", 22, 0.924610));
286  // ../UNIT_TESTER/run/consense/1/consense1_single.tree
287 }
288 
289 void TEST_consensus_tree_2() {
290  TEST_EXPECTATION(build_expected_consensus_tree(2, "bootstrapped", 1, 4, 2.5, "consense2", 59, 2.849827));
291  // ../UNIT_TESTER/run/consense/2/consense2.tree
292 }
293 
294 void TEST_consensus_tree_3() {
295  TEST_EXPECTATION(build_expected_consensus_tree(3, "bootstrapped", 1, 3, 137.772, "consense3", 128, 2.170685));
296  // ../UNIT_TESTER/run/consense/3/consense3.tree
297 }
298 
299 void TEST_consensus_tree_from_disjunct_trees() {
300  TEST_EXPECTATION(build_expected_consensus_tree(4, "disjunct", 1, 2, 137.772, "disjunct_merged", 15, 2.034290));
301  // ../UNIT_TESTER/run/consense/4/disjunct_merged.tree
302 }
303 
304 void TEST_consensus_tree_from_partly_overlapping_trees() {
305  // tree_disjunct_3 contains 7 species
306  // (3 from upper subtree (tree_disjunct_1) and 4 from lower subtree (tree_disjunct_2))
307 
308  TEST_EXPECTATION(build_expected_consensus_tree(4, "disjunct", 1, 3, 137.772, "overlap_merged", 15, 2.596455));
309  // ../UNIT_TESTER/run/consense/4/overlap_merged.tree
310 }
311 
312 void TEST_consensus_tree_from_minimal_overlapping_trees() {
313  // tree_disjunct_0 only contains 2 species (1 from upper and 1 from lower subtree).
314  TEST_EXPECTATION(build_expected_consensus_tree(4, "disjunct", 0, 2, 137.772, "overlap_mini_merged", 15, 2.750745));
315  // ../UNIT_TESTER/run/consense/4/overlap_mini_merged.tree
316 }
317 
318 void TEST_consensus_tree_described_in_arbhelp() {
319  // see ../HELP_SOURCE/source/consense_algo.hlp
320  TEST_EXPECTATION(build_expected_consensus_tree(5, "help", 1, 2, 2.0, "help_merged", 6, 1.050000));
321  // ../UNIT_TESTER/run/consense/5/help_merged.tree
322 }
323 
324 void TEST_consensus_tree_from_trees_overlapping_by_twothirds() {
325  // These 3 trees where copied from an existing tree.
326  // From each copy one third of all species has been removed
327  // (removed sets were disjunct)
328  TEST_EXPECTATION(build_expected_consensus_tree(6, "overlap_two_thirds", 1, 3, 19.2, "overlap_twothirds_merged", 15, 3.561680));
329  // ../UNIT_TESTER/run/consense/6/overlap_twothirds_merged.tree
330 }
331 
332 void TEST_consensus_tree_from_mostly_overlapping_trees() {
333  // the 3 trees were copied from tree_disjunct_source.
334  // from each tree 2 (different) species were deleted.
335  TEST_EXPECTATION(build_expected_consensus_tree(7, "disjunct_del2", 1, 3, 137.772, "overlap_mostly", 15, 1.820057));
336  // ../UNIT_TESTER/run/consense/7/overlap_mostly.tree
337 }
338 
339 void TEST_consensus_tree_from_mostly_overlapping_trees_2() {
340  // the 3 trees were copied from tree_disjunct1
341  // from each tree 1 (different) species was deleted.
342  TEST_EXPECTATION(build_expected_consensus_tree(8, "overlap2", 1, 3, 137.772, "overlap2_mostly", 8, 0.529109));
343  // ../UNIT_TESTER/run/consense/8/overlap2_mostly.tree
344 }
345 TEST_PUBLISH(TEST_consensus_tree_from_mostly_overlapping_trees_2);
346 
347 
348 #define REPEATED_TESTS
349 
350 #if defined(REPEATED_TESTS)
351 void TEST_consensus_tree_generation_is_deterministic() {
352  TEST_consensus_tree_described_in_arbhelp();
353  TEST_consensus_tree_from_minimal_overlapping_trees();
354  TEST_consensus_tree_from_partly_overlapping_trees();
355  TEST_consensus_tree_from_disjunct_trees();
356  TEST_consensus_tree_3();
357  TEST_consensus_tree_2();
358  TEST_consensus_tree_1_single();
359  TEST_consensus_tree_1();
360 }
361 
362 void TEST_arb_consensus_tree() {
363  TEST_STDOUT_CONTAINS("(arb_consensus_tree -x || true)", "Unknown switch '-x'");
364  TEST_STDOUT_CONTAINS("(arb_consensus_tree -w sth || true)", "no input trees specified");
365 
366  {
367  char *saveas = custom_tree_name(1, "consense1");
368  char *expected = custom_tree_name(1, "consense1_expected");
369 
370  TEST_OUTPUT_CONTAINS("arb_consensus_tree"
371  " -w consense/1/consense1.tree"
372  " consense/1/bootstrapped_1.tree"
373  " consense/1/bootstrapped_2.tree"
374  " consense/1/bootstrapped_3.tree"
375  " consense/1/bootstrapped_4.tree"
376  " consense/1/bootstrapped_5.tree",
377  (const char *)NULp,
378  "Created new database \"\"");
379 
380  TEST_EXPECT_TEXTFILE_DIFFLINES_IGNORE_DATES(saveas, expected, 0);
382 
383  free(expected);
384  free(saveas);
385  }
386 
387  {
388  char *saveas = custom_tree_name(2, "consense2");
389  char *expected = custom_tree_name(2, "consense2_expected");
390 
391  TEST_OUTPUT_CONTAINS("arb_consensus_tree"
392  " -w consense/2/consense2.tree"
393  " consense/2/bootstrapped_1.tree"
394  " consense/2/bootstrapped_2.tree"
395  " consense/2/bootstrapped_3.tree"
396  " consense/2/bootstrapped_4.tree",
397  (const char *)NULp,
398  "Created new database \"\"");
399 
400  TEST_EXPECT_TEXTFILE_DIFFLINES_IGNORE_DATES(saveas, expected, 0);
402 
403  free(expected);
404  free(saveas);
405  }
406 }
407 #endif // REPEATED_TESTS
408 
409 // #define TREEIO_AUTO_UPDATE // uncomment to unconditionally auto-update expected test-results (does not fail on instability; updates all to exported state)
410 //
411 // #define TREEIO_AUTO_UPDATE_IF_EXPORT_DIFFERS // uncomment to auto-update expected test-results
412 // #define TREEIO_AUTO_UPDATE_IF_REEXPORT_DIFFERS // uncomment to auto-update expected test-results
413 //
414 // Note that uncommenting both will update all expected results to reexported state (w/o failing after first) + then fail.
415 // use in contrary to TREEIO_AUTO_UPDATE to generate diff showing instabilities.
416 
417 static const char *findFirstNameContaining(TreeNode *tree, const char *part) {
418  const char *found = NULp;
419  if (tree->name && strstr(tree->name, part)) {
420  found = tree->name;
421  }
422  else if (!tree->is_leaf()) {
423  found = findFirstNameContaining(tree->get_leftson(), part);
424  if (!found) found = findFirstNameContaining(tree->get_rightson(), part);
425  }
426  return found;
427 }
428 
429 void TEST_SLOW_treeIO_stable() {
430  const char *dbname = "trees/bootstrap_groups.arb";
431  const char *treename = "tree_bootstrap_and_groups";
432  const char *savename = "bg";
433 
434  GB_shell shell;
435  GBDATA *gb_main = GB_open(dbname, "rw");
436 
437  TEST_REJECT_NULL(gb_main);
438 
439  char *outfile = GBS_global_string_copy("trees/%s.tree", savename);
440 
441  GB_HASH *seenIDs = GBS_create_hash(2*6*2*2*3, GB_MIND_CASE);
442 
443  for (int save_branchlengths = 0; save_branchlengths <= 1; ++save_branchlengths) {
444  for (BootstrapSaveStyle save_style = SAVE_NO_BRANCH_REMARKS;
445  save_style <= SAVE_ANY_BRANCH_REMARKS_INCL_100;
446  save_style = BootstrapSaveStyle(save_style+1))
447  {
448  const bool save_remarks_or_bootstraps = save_style != SAVE_NO_BRANCH_REMARKS;
449  const bool save_custom_remarks = save_style >= SAVE_CUSTOM_REMARKS;
450 
451  for (int save_groupnames = 0; save_groupnames <= 1; ++save_groupnames) {
452  const bool quoting_required = save_custom_remarks || (save_remarks_or_bootstraps && save_groupnames); // true -> quoting will be required to store nodeinfo
453  for (int pretty = 0; pretty <= 1; ++pretty) {
454 
455  for (int quoting = LABEL_DISALLOW_QUOTES; quoting <= (quoting_required ? LABEL_DOUBLE_QUOTES : LABEL_DISALLOW_QUOTES); ++quoting) {
456  LabelQuoting quoteMode = LabelQuoting(quoting);
457 
458  const char *remarkID;
459  switch (save_style) {
460  case SAVE_NO_BRANCH_REMARKS: remarkID= ""; break;
461  case SAVE_BOOTSTRAPS: remarkID= "Bs"; break;
462  case SAVE_BOOTSTRAPS_INCL_100: remarkID= "Bs100"; break;
463  case SAVE_CUSTOM_REMARKS: remarkID= "Cus"; break;
464  case SAVE_ANY_BRANCH_REMARKS: remarkID= "Rem"; break;
465  case SAVE_ANY_BRANCH_REMARKS_INCL_100: remarkID= "Rem100"; break;
466  }
467 
468  char *paramID = GBS_global_string_copy("%s_%s%s%s_%i",
469  pretty ? "p" : "s",
470  remarkID,
471  save_groupnames ? "Grp" : "",
472  save_branchlengths ? "Len" : "",
473  quoteMode);
474 
475  TEST_ANNOTATE(GBS_global_string("for paramID='%s'", paramID));
476  fprintf(stderr, "-------------------- Testing stable tree i/o for '%s'\n", paramID);
477 
478  // store paramID's and avoid duplicates:
479  if (GBS_incr_hash(seenIDs, paramID)>1) {
480  TEST_ERROR("duplicated use of paramID '%s'", paramID);
481  }
482 
483  char *expectedfile = GBS_global_string_copy("trees/%s_exp_%s.tree", savename, paramID);
484 
485  {
486  Node_ID_Labeler labels_use_IDs;
487  GB_ERROR export_error = TREE_write_Newick(gb_main, treename, labels_use_IDs, save_branchlengths, save_style, save_groupnames, pretty, quoteMode, outfile);
488 
489  if (quoting == LABEL_DISALLOW_QUOTES && quoting_required) {
490  TEST_EXPECT_CONTAINS(export_error, "required to allow quoting"); // generated by ../SL/TREE_WRITE/TreeWrite.cxx@QUOTING_REQUIRED_WHEN_SAVING
491  if (GB_is_regularfile(expectedfile)) {
492  TEST_WARNING("unneeded test data '%s'", expectedfile);
493  // system(GBS_global_string("rm %s", expectedfile)); // un-comment to delete superfluous expected trees.
494  }
495  free(paramID);
496  free(expectedfile);
497  continue;
498  }
499 
500  TEST_EXPECT_NULL(export_error);
501  }
502 
503 #if defined(TREEIO_AUTO_UPDATE)
504  system(GBS_global_string("cp %s %s", outfile, expectedfile));
505 #else // !defined(TREEIO_AUTO_UPDATE)
506  bool exported_as_expected = arb_test::textfiles_have_difflines_ignoreDates(expectedfile, outfile, 0);
507 #if defined(TREEIO_AUTO_UPDATE_IF_EXPORT_DIFFERS)
508  if (!exported_as_expected) {
509  system(GBS_global_string("cp %s %s", outfile, expectedfile));
510  }
511 #else // !defined(TREEIO_AUTO_UPDATE_IF_EXPORT_DIFFERS)
512  TEST_EXPECT(exported_as_expected);
513 #endif
514 
515  // reimport exported tree
516  const char *reloaded_treename = "tree_reloaded";
517  {
518  char *comment = NULp;
519  char *warnings = NULp;
520  TreeNode *tree = TREE_load(expectedfile, new SimpleRoot, &comment, true, &warnings);
521  GB_ERROR load_error = tree ? NULp : GB_await_error();
522 
524  that(load_error).is_equal_to_NULL()));
525 
526  // IMPL: code here is quite similar to TREE_load_to_db (which additionally calls TREE_translate_labels). consider to directly use it here?
527 
528  // store tree in DB
529  {
530  GB_transaction ta(gb_main);
531  GB_ERROR store_error = GBT_write_tree_with_remark(gb_main, reloaded_treename, tree, comment);
532  TEST_EXPECT_NULL(store_error);
533  }
534  free(comment);
535 
536  if (save_groupnames) {
537  const char *quotedGroup = findFirstNameContaining(tree, "quoted");
538  const char *underscoreGroup = findFirstNameContaining(tree, "bs100");
539  TEST_EXPECT_EQUAL(quotedGroup, "quoted");
540  TEST_EXPECT_EQUAL(underscoreGroup, "__bs100");
541  }
542  const char *capsLeaf = findFirstNameContaining(tree, "Caps");
543  TEST_EXPECT_EQUAL(capsLeaf, "_MhuCaps");
544 
545  if (warnings) {
546  fprintf(stderr, "Tree reloaded with warnings: %s\n", warnings);
547  freenull(warnings);
548  }
549 
550  destroy(tree);
551  }
552 
553  // export again
554  {
555  Node_ID_Labeler labels_use_IDs;
556  GB_ERROR reexport_error = TREE_write_Newick(gb_main, reloaded_treename, labels_use_IDs, save_branchlengths, save_style, save_groupnames, pretty, quoteMode, outfile);
557  TEST_EXPECT_NULL(reexport_error);
558  }
559 
560  // eliminate comments added by loading/saving
561  char *outfile2 = GBS_global_string_copy("trees/%s2.tree", savename);
562  {
563  char *cmd = GBS_global_string_copy("cat %s"
564  " | grep -v 'Loaded from trees/.*_exp_'"
565  " | grep -v 'tree_reloaded saved to'"
566  " > %s", outfile, outfile2);
568  free(cmd);
569  }
570 
571  bool reexported_as_expected = arb_test::textfiles_have_difflines_ignoreDates(expectedfile, outfile2, 0);
572 
573 #if defined(TREEIO_AUTO_UPDATE_IF_REEXPORT_DIFFERS)
574  if (!reexported_as_expected) {
575  system(GBS_global_string("cp %s %s", outfile2, expectedfile));
576  }
577 #else // !defined(TREEIO_AUTO_UPDATE_IF_REEXPORT_DIFFERS)
578  TEST_EXPECT(reexported_as_expected);
579 #endif
580 
581  TEST_EXPECT_ZERO_OR_SHOW_ERRNO(unlink(outfile2));
582  free(outfile2);
583 #endif
584  free(expectedfile);
585  free(paramID);
586  }
587  }
588  }
589  }
590  }
591  TEST_ANNOTATE(NULp);
592 
593  GBS_free_hash(seenIDs);
594 
595  TEST_EXPECT_ZERO_OR_SHOW_ERRNO(unlink(outfile));
596  free(outfile);
597 
598 #if defined(TREEIO_AUTO_UPDATE_IF_EXPORT_DIFFERS)
599 # if defined(TREEIO_AUTO_UPDATE_IF_REEXPORT_DIFFERS)
600  TEST_REJECT(true); // defining both may hide import/export bugs (use with care!)
601 # endif
602 #endif
603 
604  GB_close(gb_main);
605 }
606 
607 void TEST_CONSENSUS_TREE_functionality() {
608  // functionality wanted in TreeNode (for use in library CONSENSUS_TREE)
609 
610  char *comment = NULp;
611 
612  SizeAwareTree *tree = DOWNCAST(SizeAwareTree*, TREE_load("trees/bg_exp_p_GrpLen_0.tree",
613  new SizeAwareRoot,
614  &comment, false, NULp));
615  // -> ../UNIT_TESTER/run/trees/bg_exp_p_GrpLen_0.tree
616 
617 #define ORG_1111 "(AticSea6,(RblAerol,RblMesop))"
618 #define TOP_1111 "((RblAerol,RblMesop),AticSea6)"
619 #define BOT_1111 ORG_1111
620 
621 #define ORG_11121 "((DnrShiba,RsbElon4),MmbAlkal)"
622 #define TOP_11121 ORG_11121
623 #define BOT_11121 "(MmbAlkal,(DnrShiba,RsbElon4))"
624 
625 #define ORG_11122 "((MabPelag,MabSalin),PaoMaris)"
626 #define TOP_11122 ORG_11122
627 #define BOT_11122 "(PaoMaris,(MabPelag,MabSalin))"
628 
629 #define ORG_1112 "(" ORG_11121 "," ORG_11122 ")"
630 #define TOP_1112 "(" TOP_11121 "," TOP_11122 ")"
631 #define BOT_1112 "(" BOT_11121 "," BOT_11122 ")"
632 #define EDG_1112 "(" TOP_11121 "," BOT_11122 ")"
633 
634 #define ORG_111 "(" ORG_1111 "," ORG_1112 ")"
635 #define TOP_111 "(" TOP_1112 "," TOP_1111 ")"
636 #define BOT_111 "(" BOT_1111 "," BOT_1112 ")"
637 #define EDG_111 "(" EDG_1112 "," BOT_1111 ")"
638 
639 #define ORG_112 "(OnlGran2,RsnAnta2)"
640 #define TOP_112 ORG_112
641 #define BOT_112 ORG_112
642 
643 #define ORG_11 "(" ORG_111 "," ORG_112 ")"
644 #define TOP_11 "(" TOP_111 "," TOP_112 ")"
645 #define BOT_11 "(" BOT_112 "," BOT_111 ")"
646 #define EDG_11 "(" EDG_111 "," BOT_112 ")"
647 
648 #define ORG_12 "(_MhuCaps,ThtNivea)"
649 #define TOP_12 "(ThtNivea,_MhuCaps)"
650 #define BOT_12 TOP_12
651 
652 #define ORG_1 "(" ORG_11 "," ORG_12 ")"
653 #define TOP_1 "(" TOP_11 "," TOP_12 ")"
654 #define BOT_1 "(" BOT_12 "," BOT_11 ")"
655 #define EDG_1 "(" EDG_11 "," BOT_12 ")"
656 
657 #define ORG_2 "((LbnMarin,LbnzAlb4),LbnAlexa)"
658 #define TOP_2 ORG_2
659 #define BOT_2 "(LbnAlexa,(LbnMarin,LbnzAlb4))"
660 
661  // test swap_sons
663  TEST_EXPECT_NEWICK(nSIMPLE, tree, "(" ORG_1 "," ORG_2 ");");
664  tree->swap_sons();
665  TEST_EXPECT_NEWICK(nSIMPLE, tree, "(" ORG_2 "," ORG_1 ");");
666 
667  // test reorder_tree
670 
671  for (size_t o1 = 0; o1<ARRAY_ELEMS(order); ++o1) {
672  TreeOrder to_order = order[o1];
673  for (size_t o2 = 0; o2<ARRAY_ELEMS(order); ++o2) {
674  TreeOrder from_order = order[o2];
675 
676  for (int rotate = 0; rotate<=1; ++rotate) {
677  tree->reorder_tree(from_order);
678  if (rotate) tree->rotate_subtree();
679  tree->reorder_tree(to_order);
680 
681  switch (to_order) {
682  case BIG_BRANCHES_TO_TOP: TEST_EXPECT_NEWICK(nSIMPLE, tree, "(" TOP_1 "," TOP_2 ");"); break;
683  case BIG_BRANCHES_TO_EDGE: TEST_EXPECT_NEWICK(nSIMPLE, tree, "(" EDG_1 "," BOT_2 ");"); break;
684  case BIG_BRANCHES_TO_BOTTOM: TEST_EXPECT_NEWICK(nSIMPLE, tree, "(" BOT_2 "," BOT_1 ");"); break;
685  default: TEST_REJECT(true); break;
686  }
687 
688  }
689  }
690  }
691 
692  // test rotate_subtree
694  tree->reorder_tree(BIG_BRANCHES_TO_TOP);
695  tree->rotate_subtree(); TEST_EXPECT_NEWICK(nSIMPLE, tree, "((LbnAlexa,(LbnzAlb4,LbnMarin)),((_MhuCaps,ThtNivea),((RsnAnta2,OnlGran2),((AticSea6,(RblMesop,RblAerol)),((PaoMaris,(MabSalin,MabPelag)),(MmbAlkal,(RsbElon4,DnrShiba)))))));");
696  tree->rotate_subtree(); TEST_EXPECT_NEWICK(nSIMPLE, tree, "(" TOP_1 "," TOP_2 ");");
697 
698 
699  // test set_root
701  TreeNode *AticSea6Grandpa = tree->findLeafNamed("AticSea6")->get_father()->get_father();
702  TEST_REJECT_NULL(AticSea6Grandpa);
703  TEST_EXPECT_VALID_TREE(AticSea6Grandpa);
704 
705  AticSea6Grandpa->set_root();
707  "((" ORG_1112 "," TOP_1111 ")," // AticSea6 is direct son of TOP_1111
708  "((" ORG_2 "," TOP_12 ")," ORG_112 "));");
709 
710  // test auto-detection of "best" root
712  tree->get_tree_root()->find_innermost_edge().set_root();
714  "((((LbnMarin:.019,LbnzAlb4:.003):.016,LbnAlexa:.032):.122,(ThtNivea:.23,_MhuCaps:.194):.427):.076,"
715  "(((((DnrShiba:.076,RsbElon4:.053):.034,MmbAlkal:.069):.016,((MabPelag:.001,MabSalin:.009):.095,PaoMaris:.092):.036):.03,((RblAerol:.085,RblMesop:.042):.238,AticSea6:.111):.018):.036,(OnlGran2:.057,RsnAnta2:.06):.021):.076);");
716 
718  destroy(tree);
719  free(comment);
720 }
721 
722 #endif // UNIT_TESTS
723 
724 // --------------------------------------------------------------------------------
GB_ERROR GBK_system(const char *system_command)
Definition: arb_msg.cxx:571
#define arb_assert(cond)
Definition: arb_assert.h:245
size_t GBT_count_leafs(const TreeNode *tree)
Definition: adtree.cxx:894
GBDATA * GB_open(const char *path, const char *opent)
Definition: ad_load.cxx:1363
#define TEST_WARNING(format, strarg)
Definition: test_unit.h:1068
void put(const char *elem)
Definition: arb_strarray.h:188
group_matcher all()
Definition: test_unit.h:1011
size_t size() const
Definition: arb_strarray.h:85
GB_ERROR GBT_write_tree_with_remark(GBDATA *gb_main, const char *tree_name, TreeNode *tree, const char *remark)
Definition: adtree.cxx:599
TreeNode * findLeafNamed(const char *wantedName)
Definition: TreeNode.cxx:276
virtual void set_root()
Definition: TreeNode.cxx:208
long GBS_incr_hash(GB_HASH *hs, const char *key)
Definition: adhash.cxx:467
int ARB_main(int argc, char *argv[])
char * get_tree_remark() const
Definition: CT_ctree.cxx:213
#define TEST_STDOUT_CONTAINS(cmd, part)
char * ARB_strdup(const char *str)
Definition: arb_string.h:27
const char * GBS_global_string(const char *templat,...)
Definition: arb_msg.cxx:203
STL namespace.
void GBS_free_hash(GB_HASH *hs)
Definition: adhash.cxx:538
char * ARB_strpartdup(const char *start, const char *end)
Definition: arb_string.h:51
#define EXIT_SUCCESS
Definition: arb_a2ps.c:154
int GB_unlink(const char *path)
Definition: arb_file.cxx:188
static GB_ERROR save_tree_as_newick(TreeNode *tree, const char *savename, const char *comment)
#define ARRAY_ELEMS(array)
Definition: arb_defs.h:19
bool empty() const
Definition: arb_strarray.h:86
GBT_LEN leftlen
Definition: TreeNode.h:224
#define DOWNCAST(totype, expr)
Definition: downcast.h:141
#define TEST_PUBLISH(testfunction)
Definition: test_unit.h:1517
#define TEST_EXPECT_CONTAINS(str, part)
Definition: test_unit.h:1316
GB_ERROR GB_await_error()
Definition: arb_msg.cxx:342
#define TEST_EXPECT(cond)
Definition: test_unit.h:1328
static int weight[maxsites+1]
void GB_warningf(const char *templat,...)
Definition: arb_msg.cxx:536
#define TEST_EXPECT_NEWICK(format, tree, expected_newick)
Definition: test_unit.h:1481
#define is_equal_to_NULL()
Definition: test_unit.h:1028
GB_ERROR TREE_write_Newick(GBDATA *gb_main, const char *tree_name, const TreeLabeler &labeler, bool save_branchlengths, BootstrapSaveStyle bootstrap_style, bool save_groupnames, bool pretty, LabelQuoting quoteMode, const char *path)
Definition: TreeWrite.cxx:406
void add(SizeAwareTree *&tree, const char *treename, double weight)
Definition: CT_ctree.hxx:65
#define TEST_REJECT(cond)
Definition: test_unit.h:1330
#define TEST_REJECT_NULL(n)
Definition: test_unit.h:1325
static void error(const char *msg)
Definition: mkptypes.cxx:96
TreeNode * TREE_load(const char *path, TreeRoot *troot, char **commentPtr, bool allow_length_scaling, char **warningPtr)
Definition: TreeRead.cxx:734
expectation_group & add(const expectation &e)
Definition: test_unit.h:812
#define that(thing)
Definition: test_unit.h:1043
#define TEST_EXPECT_VALID_TREE(tree)
Definition: TreeNode.h:727
#define TEST_OUTPUT_CONTAINS(cmd, expected_std, expected_err)
#define TEST_EXPECT_ZERO_OR_SHOW_ERRNO(iocond)
Definition: test_unit.h:1090
#define TEST_EXPECT_TEXTFILE_DIFFLINES_IGNORE_DATES(fgot, fwant, diff)
Definition: test_unit.h:1419
#define does_differ_from_NULL()
Definition: test_unit.h:1029
GBT_LEN rightlen
Definition: TreeNode.h:224
#define EXIT_FAILURE
Definition: arb_a2ps.c:157
#define is_equal_to(val)
Definition: test_unit.h:1025
#define TEST_EXPECTATION(EXPCTN)
Definition: test_unit.h:1048
bool is_leaf() const
Definition: TreeNode.h:263
#define textfiles_have_difflines_ignoreDates(f1, f2, ed)
Definition: test_unit.h:1438
#define TEST_EXPECT_NULL(n)
Definition: test_unit.h:1322
static list< LineAttachedMessage > warnings
#define fulfills(pred, arg)
Definition: test_unit.h:1037
SizeAwareTree * get(size_t &different_species, GB_ERROR &error)
Definition: CT_ctree.hxx:79
char * name
Definition: TreeNode.h:226
#define TEST_ERROR(format, strarg)
Definition: test_unit.h:1070
TreeOrder
Definition: TreeNode.h:35
#define TEST_EXPECT_NO_ERROR(call)
Definition: test_unit.h:1118
#define NULp
Definition: cxxforward.h:116
bool GB_is_regularfile(const char *path)
Definition: arb_file.cxx:76
LabelQuoting
Definition: TreeWrite.h:18
bool ARB_strBeginsWith(const char *str, const char *with)
Definition: arb_str.h:42
GB_transaction ta(gb_var)
void destroy(TreeNode *that)
Definition: TreeNode.h:667
GBDATA * gb_main
Definition: adname.cxx:32
static char * create_tree_name(const char *savename)
#define TEST_EXPECT_EQUAL(expr, want)
Definition: test_unit.h:1294
static TreeNode * build_consensus_tree(const CharPtrArray &input_trees, GB_ERROR &error, size_t &different_species, double weight, char *&comment)
char * GBS_global_string_copy(const char *templat,...)
Definition: arb_msg.cxx:194
void GB_close(GBDATA *gbd)
Definition: arbdb.cxx:655
BootstrapSaveStyle
Definition: TreeWrite.h:29
GB_HASH * GBS_create_hash(long estimated_elements, GB_CASE case_sens)
Definition: adhash.cxx:253
#define UNCOVERED()
Definition: arb_assert.h:380