21 #define tree_assert(cond) arb_assert(cond)
31 enum tr_lfmode { LF_UNKNOWN, LF_N, LF_R, LF_NR, LF_RN, };
41 double max_found_bootstrap;
48 #define MAX_READER_WARN 100 // maximum warnings show for each kind of separately counted warnings
51 int dropped_leaf_groups;
52 int dropped_leaf_remarks;
53 int dropped_redefined_groups;
54 int dropped_redefined_remarks;
55 Count() : dropped_leaf_groups(0), dropped_leaf_remarks(0), dropped_redefined_groups(0), dropped_redefined_remarks(0) {}
57 bool warnings_suppressed()
const {
return
63 void setError(
const char *
message);
64 void setErrorAt(
const char *
message);
65 void setExpectedError(
const char *expected);
71 char *content_ahead(
size_t how_many,
bool show_eof);
73 void drop_tree_char(
char expected);
75 void setBranchName_acceptingBootstrap(
TreeNode *
node,
char*& name);
81 char *eat_quoted_string();
100 add_warning(
"Length specified for root-node has been ignored");
105 add_warningf(
"Remark specified for root-node ('%s') has been ignored", remark);
111 if (last_character ==
';') read_tree_char();
112 if (last_character != EOF) {
113 char *unused_input = content_ahead(30,
false);
114 add_warningf(
"Unexpected input-data after tree: '%s'", unused_input);
144 : unnamed_counter(0),
145 tree_file_name(strdup(file_name)),
150 max_found_branchlen(-1),
151 max_found_bootstrap(-1),
162 free(tree_file_name);
165 void TreeReader::setError(
const char *
message) {
168 tree_file_name, line_cnt, message);
170 char *TreeReader::content_ahead(
size_t how_many,
bool show_eof) {
171 char show[how_many+1+4];
173 for (i = 0; i<how_many; ++i) {
174 show[i] = last_character;
175 if (show[i] == EOF) {
177 strcpy(show+i,
"<EOF>");
188 void TreeReader::setErrorAt(
const char *
message) {
189 if (last_character == EOF) {
193 char *show = content_ahead(30,
true);
199 void TreeReader::setExpectedError(
const char *expected) {
203 int TreeReader::get_char() {
213 case LF_UNKNOWN: lfmode = LF_N; inc = 1;
break;
214 case LF_N: inc = 1;
break;
215 case LF_R: lfmode = LF_RN; c = get_char();
break;
216 case LF_NR: c = get_char();
break;
217 case LF_RN: inc = 1;
break;
220 else if (c ==
'\r') {
222 case LF_UNKNOWN: lfmode = LF_R; inc = 1;
break;
223 case LF_R: inc = 1;
break;
224 case LF_N: lfmode = LF_NR; c = get_char();
break;
225 case LF_RN: c = get_char();
break;
226 case LF_NR: inc = 1;
break;
228 if (c ==
'\r') c =
'\n';
235 int TreeReader::read_tree_char() {
242 while (!done && !
error) {
244 if (c ==
' ' || c ==
'\t' || c ==
'\n') ;
246 int openBrackets = 1;
248 tree_comment.
put(
'\n');
251 while (openBrackets && !
error) {
255 setError(
"Reached end of file while reading comment");
259 if (openBrackets) tree_comment.
put(c);
277 int TreeReader::read_char() {
283 void TreeReader::eat_white() {
284 int c = last_character;
285 while ((c ==
' ') || (c ==
'\n') || (c ==
'\r') || (c ==
'\t')) {
293 int c = last_character;
295 while (((c<=
'9') && (c>=
'0')) || (c==
'.') || (c==
'-') || (c==
'+') || (c==
'e') || (c==
'E')) {
302 if (failed_to_convert) {
303 setErrorAt(failed_to_convert);
309 bool consumed_some_length = strng[0];
310 return consumed_some_length;
313 char *TreeReader::eat_quoted_string() {
325 char buffer[MAX_NAME_LEN+2];
327 int c = last_character;
329 #define NAME_TOO_LONG ((s-buffer)>MAX_NAME_LEN)
331 if (c ==
'\'' || c ==
'"') {
332 char found_quote = c;
335 while (c!=EOF && c!=found_quote) {
340 if (c == found_quote) c = read_tree_char();
348 while (c ==
'_') c = read_tree_char();
349 while (c ==
' ') c = read_tree_char();
351 while (c!=
':' && c!=EOF && c!=
',' && c!=
';' && c !=
')') {
354 c = read_tree_char();
359 setError(
GBS_global_string(
"Name '%s' is longer than %i bytes", buffer, MAX_NAME_LEN));
362 return strdup(buffer);
367 summary.
putlong(droppedAtLeaf);
368 summary.
cat(
" at leafs");
370 if (droppedAtLeaf && droppedRedefined) {
373 if (droppedRedefined) {
374 summary.
putlong(droppedRedefined);
375 summary.
cat(
" redefined");
380 if (count.warnings_suppressed()) {
382 summary.
cat(
"Drop summary: ");
384 bool dropped_remarks = count.dropped_leaf_remarks || count.dropped_redefined_remarks;
385 bool dropped_groups = count.dropped_leaf_groups || count.dropped_redefined_groups;
387 if (dropped_groups) {
388 summary.
cat(
"groups: ");
391 if (dropped_groups && dropped_remarks) {
394 if (dropped_remarks) {
395 summary.
cat(
"support-values/remarks: ");
404 void TreeReader::setBranchName_acceptingBootstrap(
TreeNode *
node,
char*& name) {
429 const char *group_part = name;
433 if (is_bootstrap || remark) {
439 add_warningf(
"Dropped support-value ('%g') specified for a single-node-subtree", bootstrap);
442 add_warningf(
"Dropped remark ('%s') specified for a single-node-subtree", remark);
446 add_warning(
"[Note: further warnings of this type will be suppressed]");
451 const char *existingRemark = node->
get_remark();
452 if (existingRemark) {
455 add_warningf(
"Dropped support-value redefinition '%g' (edge already labeled as '%s')", bootstrap, existingRemark);
458 add_warningf(
"Dropped remark redefinition '%s' (edge already labeled as '%s')", remark, existingRemark);
461 add_warning(
"[Note: further warnings of this type will be suppressed]");
468 bootstrap = bootstrap*100.0;
469 if (bootstrap > max_found_bootstrap) { max_found_bootstrap = bootstrap; }
480 if (group_part == name) reassign(group, name);
481 else group = strdup(group_part);
492 else if (node->
name) {
495 add_warningf(
"Dropped group name specified for a single-node-subtree ('%s')", group);
497 add_warning(
"[Note: further warnings of this type will be suppressed]");
504 add_warningf(
"Dropped group name redefinition '%s' (furcation already named '%s')",
507 add_warning(
"[Note: further warnings of this type will be suppressed]");
514 reassign(node->
name, group);
521 if (badNameError) setErrorAt(badNameError);
526 void TreeReader::drop_tree_char(
char expected) {
527 if (last_character != expected) {
533 bool TreeReader::eat_and_set_name_and_length(
TreeNode *node,
GBT_LEN& nodeLen) {
544 bool length_consumed =
false;
546 while (!done && !
error) {
547 switch (last_character) {
554 if (!
error && length_consumed) setErrorAt(
"Unexpected ':' (already read a branchlength)");
555 if (!
error) drop_tree_char(
':');
558 if (eat_number(foundlen)) {
566 max_found_branchlen =
std::max(max_found_branchlen, nodeLen);
570 if (!
error) setExpectedError(
"valid length");
573 length_consumed =
true;
581 char *branchName = eat_quoted_string();
584 if (branchName[0]) setBranchName_acceptingBootstrap(node, branchName);
585 else freenull(branchName);
615 if (last_character ==
'(') {
616 node = load_subtree(nodeLen);
620 char *name = eat_quoted_string();
623 if (!name[0]) freeset(name, unnamedNodeName());
630 if (node && !
error) {
631 if (!eat_and_set_name_and_length(node, nodeLen)) {
659 TreeNode *left = load_named_node(leftLen);
662 switch (last_character) {
673 while (last_character ==
',' && !
error) {
677 left = pair; leftLen = 0;
683 right = load_named_node(rightLen);
688 if (last_character ==
')') {
696 setExpectedError(
"one of ',)'");
714 setExpectedError(
"one of ',)'");
723 if (!
error) drop_tree_char(
')');
731 if (consumingReader) consumingReader->
add_warning(msg);
741 FILE *input = fopen(path,
"rt");
743 bool own_root =
true;
749 const char *
name_only = strrchr(path,
'/');
751 else name_only = path;
755 tree = reader.
load();
756 if (tree) own_root =
false;
761 else if (tree && tree->
is_leaf()) error =
"tree is too small (need at least 2 species)";
769 double bootstrap_scale = 1.0;
770 double branchlen_scale = 1.0;
773 bootstrap_scale = 0.01;
774 reader.add_warningf(
"Auto-scaling bootstrap values by factor %.2f (max. found bootstrap was %5.2f)",
778 if (allow_length_scaling) {
779 branchlen_scale = 0.01;
780 reader.add_warningf(
"Auto-scaling branchlengths by factor %.2f (max. found branchlength = %.2f)\n"
781 "(use ARB/Tree/Modify branches/Scale branchlengths with factor %.2f to undo auto-scaling)",
796 TreeNode *left_son = tree->get_leftson();
797 TreeNode *right_son = tree->get_rightson();
799 if (!left_son->is_leaf() && !right_son->
is_leaf()) {
800 const char *left_rem = left_son->
get_remark();
801 const char *right_rem = right_son->
get_remark();
803 if (left_rem != right_rem) {
804 TreeNode *son_with_remark = left_rem ? left_son : right_son;
805 TreeNode *other_son = left_rem ? right_son : left_son;
810 left_rem = left_son->get_remark();
820 if (wmsg) *warningPtr = strdup(wmsg);
832 *commentPtr = comment;
851 char *tree_comment =
NULp;
864 error = ta.
close(error);
883 static arb_test::match_expectation parsing_label_succeeds(
const char *
label,
bool result_expected,
const double bootstrap_expected,
const char *remark_expected,
const char *groupname_expected) {
887 const double UNCHANGED_VALUE = -666.66;
889 double bootstrap = UNCHANGED_VALUE;
890 char *remark = (
char*)
"xxx";
891 const char *groupname =
label;
898 const double EPSILON = 0.000001;
906 return all().ofgroup(expected);
909 const double ZERO = 0.0;
911 #define TEST_PARSES_LABEL_AS_EMPTY(label) TEST_EXPECTATION(parsing_label_succeeds(label, false, ZERO, NULp, NULp))
912 #define TEST_PARSES_LABEL_AS_PLAIN_GROUP(label,group) TEST_EXPECTATION(parsing_label_succeeds(label, false, ZERO, NULp, group))
913 #define TEST_PARSES_LABEL_AS_PLAIN_BS(label,bs) TEST_EXPECTATION(parsing_label_succeeds(label, true, bs, NULp, NULp))
914 #define TEST_PARSES_LABEL_AS_PLAIN_REM(label,rem) TEST_EXPECTATION(parsing_label_succeeds(label, false, ZERO, rem, NULp))
915 #define TEST_PARSES_LABEL_AS_BS_AND_GROUP(label,bs,group) TEST_EXPECTATION(parsing_label_succeeds(label, true, bs, NULp, group))
916 #define TEST_PARSES_LABEL_AS_REM_AND_GROUP(label,rem,group) TEST_EXPECTATION(parsing_label_succeeds(label, false, ZERO, rem, group))
918 #define TEST_PARSES_LABEL_AS_PLAIN_REM__BROKEN(label,rem) TEST_EXPECTATION__BROKEN_SIMPLE(parsing_label_succeeds(label, false, ZERO, rem, NULp))
919 #define TEST_PARSES_LABEL_AS_PLAIN_GROUP__BROKEN(label,group) TEST_EXPECTATION__BROKEN_SIMPLE(parsing_label_succeeds(label, false, ZERO, NULp, group))
920 #define TEST_PARSES_LABEL_AS_PLAIN_BS__BROKEN(label,bs) TEST_EXPECTATION__BROKEN_SIMPLE(parsing_label_succeeds(label, true, bs, NULp, NULp))
922 #define TEST_PARSES_LABEL_AS_REM_AND_GROUP__BROKEN(label,rem_wanted,group_wanted,rem_got,group_got) \
923 TEST_EXPECTATION__BROKEN(parsing_label_succeeds(label, false, ZERO, rem_wanted, group_wanted), \
924 parsing_label_succeeds(label, false, ZERO, rem_got, group_got))
926 void TEST_treelabel_parser() {
927 TEST_PARSES_LABEL_AS_PLAIN_GROUP(
"group",
"group");
928 TEST_PARSES_LABEL_AS_PLAIN_GROUP(
"77x",
"77x");
930 TEST_PARSES_LABEL_AS_PLAIN_BS(
"33%", 0.33);
931 TEST_PARSES_LABEL_AS_PLAIN_BS(
"0.123", 0.123);
932 TEST_PARSES_LABEL_AS_PLAIN_BS(
"0.456", 0.456);
933 TEST_PARSES_LABEL_AS_PLAIN_BS(
"93.27", 93.27);
934 TEST_PARSES_LABEL_AS_PLAIN_BS(
"93.27%", 0.9327);
935 TEST_PARSES_LABEL_AS_PLAIN_BS(
"93.27618%", 0.932762);
937 TEST_PARSES_LABEL_AS_BS_AND_GROUP (
"24%:group", 0.24,
"group");
938 TEST_PARSES_LABEL_AS_REM_AND_GROUP(
"remark:group",
"remark",
"group");
939 TEST_PARSES_LABEL_AS_REM_AND_GROUP(
"remark:456group",
"remark",
"456group");
941 TEST_PARSES_LABEL_AS_REM_AND_GROUP(
"123remark:group",
"123remark",
"group");
942 TEST_PARSES_LABEL_AS_REM_AND_GROUP(
"123remark:456group",
"123remark",
"456group");
944 TEST_PARSES_LABEL_AS_REM_AND_GROUP(
"a:b:c",
"a",
"b:c");
947 TEST_PARSES_LABEL_AS_PLAIN_REM(
"remark:",
"remark");
948 TEST_PARSES_LABEL_AS_PLAIN_REM(
"12.789remark:",
"12.789remark");
950 TEST_PARSES_LABEL_AS_PLAIN_BS(
"93.27%:", 0.9327);
951 TEST_PARSES_LABEL_AS_PLAIN_BS(
"14%:", 0.14);
952 TEST_PARSES_LABEL_AS_PLAIN_BS(
"81:", 81.0);
954 TEST_PARSES_LABEL_AS_PLAIN_GROUP(
":group",
"group");
956 TEST_PARSES_LABEL_AS_EMPTY(
":");
957 TEST_PARSES_LABEL_AS_EMPTY(
"");
960 static TreeNode *loadFromFileContaining(
const char *treeString,
char **warningsPtr) {
961 const char *filename =
"trees/tmp.tree";
962 FILE *out = fopen(filename,
"wt");
966 fputs(treeString, out);
986 return all().ofgroup(expected);
1001 return all().ofgroup(expected);
1004 #define TEST_EXPECT_TREELOAD_FAILED_WITH(tree,errpart) TEST_EXPECTATION(loading_tree_failed_with(tree, errpart))
1005 #define TEST_EXPECT_TREELOAD_FAILED_WITH__BROKEN(tree,errpart) TEST_EXPECTATION__BROKEN(loading_tree_failed_with(tree, errpart))
1007 #define TEST_EXPECT_TREELOAD(tree,newick) TEST_EXPECTATION(loading_tree_succeeds(tree,newick,expectedFormat))
1008 #define TEST_EXPECT_TREELOAD__BROKEN(tree,newick) TEST_EXPECTATION__BROKEN(loading_tree_succeeds(tree,newick,expectedFormat))
1010 #define TEST_EXPECT_TREEFILE_FAILS_WITH(name,errpart) do { \
1011 TreeNode *tree = TREE_load(name, new SimpleRoot, NULp, false, NULp); \
1012 TEST_EXPECT_TREELOAD_FAILED_WITH(tree, errpart); \
1015 #define TEST_EXPECT_TREESTRING_FAILS_WITH(treeString,errpart) do { \
1016 TreeNode *tree = loadFromFileContaining(treeString, NULp); \
1017 TEST_EXPECT_TREELOAD_FAILED_WITH(tree, errpart); \
1021 #define TEST_EXPECT_TREESTRING_FAILS_WITH__BROKEN(treeString,errpart,newick) do { \
1022 char *warnings = NULp; \
1023 TreeNode *tree = loadFromFileContaining(treeString, &warnings); \
1024 TEST_EXPECT_TREELOAD_FAILED_WITH__BROKEN(tree, errpart); \
1025 TEST_EXPECT_TREELOAD(tree, newick); \
1026 TEST_EXPECT_NULL(warnings); \
1031 #define TEST_EXPECT_TREESTRING_OK(treeString,newick) do { \
1032 char *warnings = NULp; \
1033 TreeNode *tree = loadFromFileContaining(treeString, &warnings); \
1034 TEST_EXPECT_TREELOAD(tree, newick); \
1035 TEST_EXPECT_NULL(warnings); \
1051 size_t reportCount = reportParts.
size();
1052 size_t warningCount = warningParts.
size();
1056 bool reportMatched[reportCount];
1057 bool warningMatched[warningCount];
1059 for (
int w = 0; warningParts[w]; ++w) {
1060 const char *warningPart = warningParts[w];
1061 warningMatched[w] =
false;
1063 for (
int r = 0; reportParts[r]; ++r) {
1064 if (!w) reportMatched[r] =
false;
1065 else if (reportMatched[r])
continue;
1066 const char *reportPart = reportParts[r];
1067 if (strstr(reportPart, warningPart)) {
1069 reportMatched[r] =
true;
1070 warningMatched[w] =
true;
1075 for (
int w = 0; warningParts[w]; ++w)
if (!warningMatched[w]) expected.
add(
that(warningParts[w]).
is_equal_to(
"<unreported>"));
1076 for (
int r = 0; reportParts[r]; ++r)
if (!reportMatched[r]) expected.
add(
that(reportParts[r]).
is_equal_to(
"<reported, but not expected>"));
1078 return all().ofgroup(expected);
1081 #define TEST_EXPECT_HAVE_WARNINGS(warningsReported, expectedWarnParts) do { \
1082 TEST_REJECT_NULL(warnings); \
1083 TEST_EXPECTATION(reports_these_warnings(warningsReported, expectedWarnParts)); \
1086 #define TEST_EXPECT_TREESTRING_OK_WITH_WARNINGS(treeString,newick,expectedWarnParts) do { \
1087 char *warnings = NULp; \
1088 TreeNode *tree = loadFromFileContaining(treeString, &warnings); \
1089 TEST_EXPECT_TREELOAD(tree, newick); \
1090 TEST_EXPECT_HAVE_WARNINGS(warnings, expectedWarnParts); \
1095 #define TEST_EXPECT_TREESTRING_OK__BROKEN(treeString,newick) do { \
1096 TreeNode *tree = loadFromFileContaining(treeString, NULp); \
1097 TEST_EXPECT_TREELOAD__BROKEN(tree, newick); \
1102 void TEST_load_tree() {
1110 char *comment =
NULp;
1114 TEST_EXPECT_TREELOAD(tree,
"(((s1,s2),(s3,s 4)),(s5,s-6));");
1119 "tree covering most of tree reader code\n"
1120 "comment contains [extra brackets] inside comment\n");
1123 ": Loaded from trees/test.tree\n");
1131 const char *treestring[] = {
1132 "(node1,node2)rootgroup;",
1133 "(node1:0.00,(node2, node3:0.57)):0;",
1134 "(((((a))single)), ((b, c)17%:0.2));",
1136 "((a,b)17,(c,d)33.3,(e,f)12.5:0.2);",
1137 "((a,b)G,(c,d)H,(e,f)I:0.2);",
1138 "((a,b)'17:G',(c,d)'33.3:H',(e,f)'12.5:I':0.2);",
1139 "((a,b)17G,(c,d)33.3H,(e,f)12.5I:0.2)",
1141 "((a,b)'17%:G',(c,d)'33.3%:H',(e,f)'12.5%:I':0.2);",
1142 "((a,b)'0.17:G',(c,d)'0.333:H',(e,f)'0.125:I':0.2);",
1145 const char *EXPECTED_NEWICK_3TO8 =
"(((a,b),(c,d)),(e,f));";
1147 const char *expected_newick[] = {
1149 "(node1,(node2,node3));",
1152 EXPECTED_NEWICK_3TO8,
1153 EXPECTED_NEWICK_3TO8,
1154 EXPECTED_NEWICK_3TO8,
1155 EXPECTED_NEWICK_3TO8,
1157 EXPECTED_NEWICK_3TO8,
1158 EXPECTED_NEWICK_3TO8,
1160 const char *expected_warnings[] = {
1163 "Dropped group name specified for a single-node-subtree" LF
"Ignored invalid bootstrap '17%' at root edge leading to a leaf",
1165 "Auto-scaling bootstrap values by factor 0.01" LF
"Using comment '1250%' on both sides of root-edge",
1167 "Auto-scaling bootstrap values by factor 0.01" LF
"Using comment '1250%' on both sides of root-edge",
1170 "Using comment '13%' on both sides of root-edge",
1171 "Using comment '13%' on both sides of root-edge",
1177 for (
size_t i = 0; i<
ARRAY_ELEMS(treestring); ++i) {
1180 TreeNode *tree = loadFromFileContaining(treestring[i], &warnings);
1181 TEST_EXPECT_TREELOAD(tree, expected_newick[i]);
1268 if (expected_warnings[i]) {
1269 TEST_EXPECT_HAVE_WARNINGS(warnings, expected_warnings[i]);
1278 TEST_ANNOTATE(
NULp);
1284 TEST_EXPECT_TREESTRING_OK(
"(,);",
"(unnamed1,unnamed2);");
1285 TEST_EXPECT_TREESTRING_OK(
"((a,)); ",
"(a,unnamed1);");
1286 TEST_EXPECT_TREESTRING_OK(
"((,b)); ",
"(unnamed1,b);");
1288 TEST_EXPECT_TREESTRING_OK(
"( a, (b,(c),d), (e,(f)) );",
"((a,((b,c),d)),(e,f));");
1289 TEST_EXPECT_TREESTRING_OK(
"(((((a)))), ((b, c)));",
"(a,(b,c));");
1294 TEST_EXPECT_TREESTRING_OK_WITH_WARNINGS(
"( (a), ((((b),(c),(d))'rem:')'duprem:group')dupgroup, ((e),(f)) );",
1295 "((a,((b,c),d)'rem:group'),(e,f));",
1296 "Dropped remark redefinition 'duprem' (edge already labeled as 'rem')" LF
1297 "Dropped group name redefinition 'dupgroup' (furcation already named 'group')");
1302 TEST_EXPECT_TREESTRING_OK (
"(a,b);",
"(a,b);");
1303 TEST_EXPECT_TREESTRING_OK_WITH_WARNINGS(
"(a,b)17%;",
"(a,b);",
"Remark specified for root-node ('17%') has been ignored");
1305 TEST_EXPECT_TREESTRING_OK (
"((a,b),c);",
"((a,b),c);");
1306 TEST_EXPECT_TREESTRING_OK_WITH_WARNINGS(
"((a,b)23%,c);",
"((a,b),c);",
"Ignored invalid bootstrap '23%' at root edge leading to a leaf");
1307 TEST_EXPECT_TREESTRING_OK_WITH_WARNINGS(
"(c,(a,b)23%);",
"(c,(a,b));",
"Ignored invalid bootstrap '23%' at root edge leading to a leaf");
1308 TEST_EXPECT_TREESTRING_OK_WITH_WARNINGS(
"(c,(a,b)23%)99%;",
"(c,(a,b));",
1309 "Remark specified for root-node ('99%') has been ignored" LF
1310 "Ignored invalid bootstrap '23%' at root edge leading to a leaf");
1312 TEST_EXPECT_TREESTRING_OK (
"((a,b),(c,d));",
"((a,b),(c,d));");
1314 TEST_EXPECT_TREESTRING_OK_WITH_WARNINGS(
"((a,b)17%,(c,d)39%);",
"((a,b)'28%',(c,d)'28%');",
"Root-edge has conflicting support values '17%' and '39%' -> using average '28%'");
1315 TEST_EXPECT_TREESTRING_OK (
"((a,b)0.333,(c,d)0.334);",
"((a,b)'33%',(c,d)'33%');");
1316 TEST_EXPECT_TREESTRING_OK_WITH_WARNINGS(
"((a,b)0.321,(c,d)0.345);",
"((a,b)'34%',(c,d)'34%');",
"Root-edge has conflicting support values '32%' and '35%' -> using average '34%'");
1318 TEST_EXPECT_TREESTRING_OK_WITH_WARNINGS(
"((a,b)17%,(c,d));",
"((a,b)'17%',(c,d)'17%');",
"Using comment '17%' on both sides of root-edge");
1319 TEST_EXPECT_TREESTRING_OK_WITH_WARNINGS(
"((a,b),(c,d)17%);",
"((a,b)'17%',(c,d)'17%');",
"Using comment '17%' on both sides of root-edge");
1320 TEST_EXPECT_TREESTRING_OK_WITH_WARNINGS(
"((a,b)17%,(c,d)100%);",
"((a,b)'17%',(c,d)'17%');",
"Using comment '17%' on both sides of root-edge");
1321 TEST_EXPECT_TREESTRING_OK_WITH_WARNINGS(
"((a,b)100%,(c,d)17%);",
"((a,b)'17%',(c,d)'17%');",
"Using comment '17%' on both sides of root-edge");
1322 TEST_EXPECT_TREESTRING_OK (
"((a,b)100%,(c,d)100%);",
"((a,b),(c,d));");
1324 TEST_EXPECT_TREESTRING_OK_WITH_WARNINGS(
"((a,(b,c))32%,d);",
"((a,(b,c)),d);",
"Ignored invalid bootstrap '32%' at root edge leading to a leaf");
1325 TEST_EXPECT_TREESTRING_OK (
"((a,(b,c)41%),d);",
"((a,(b,c)'41%'),d);");
1328 TEST_EXPECT_TREESTRING_OK_WITH_WARNINGS(
"(((a)41%,(b,c)),d);",
"((a,(b,c)),d);",
"Dropped support-value ('0.41') specified for a single-node-subtree");
1329 TEST_EXPECT_TREESTRING_OK_WITH_WARNINGS(
"(((b,c),(a)0.41),d);",
"(((b,c),a),d);",
"Dropped support-value ('0.41') specified for a single-node-subtree");
1331 TEST_EXPECT_TREESTRING_OK_WITH_WARNINGS(
"(((b,c),(a)name),d);",
"(((b,c),a),d);",
"Dropped group name specified for a single-node-subtree ('name')");
1332 TEST_EXPECT_TREESTRING_OK_WITH_WARNINGS(
"(((a)'name',(b,c)),d);",
"((a,(b,c)),d);",
"Dropped group name specified for a single-node-subtree ('name')");
1333 TEST_EXPECT_TREESTRING_OK_WITH_WARNINGS(
"(((b,c),(a)'35%:name'),d);",
"(((b,c),a),d);",
"Dropped group name specified for a single-node-subtree ('name')" LF
"Dropped support-value ('0.35') specified for a single-node-subtree");
1334 TEST_EXPECT_TREESTRING_OK_WITH_WARNINGS(
"(((a)'remark:name',(b,c)),d);",
"((a,(b,c)),d);",
"Dropped group name specified for a single-node-subtree ('name')" LF
"Dropped remark ('remark') specified for a single-node-subtree");
1335 TEST_EXPECT_TREESTRING_OK_WITH_WARNINGS(
"(((b,c),(a)'remark:'),d);",
"(((b,c),a),d);",
"Dropped remark ('remark') specified for a single-node-subtree");
1337 TEST_EXPECT_TREESTRING_OK_WITH_WARNINGS(
"((a, b)25)20;",
"(a,b);",
1338 "Dropped support-value redefinition '20' (edge already labeled as '2500%')" LF
1339 "Remark specified for root-node ('2500%') has been ignored" LF
1340 "Auto-scaling bootstrap values by factor 0.01 (max. found bootstrap was 2500.00)");
1341 expectedFormat =
nALL;
1344 TEST_EXPECT_TREESTRING_OK(
"((a:0,b:5):-.9,(c:.3, d:-.0001));",
"((a:0,b:5):-0.9,(c:.3,d:0):.1);");
1345 TEST_EXPECT_TREESTRING_OK(
"(((a,b),(c,d)),((e,f),(g,h)));",
"(((a:.1,b:.1):.1,(c:.1,d:.1):.1):.1,((e:.1,f:.1):.1,(g:.1,h:.1):.1):.1);");
1347 TEST_EXPECT_TREESTRING_OK(
"(((a,b)'0.25:G':.6,(c,d)'17%':.5),((e,f)'r:g':.2,(g,h)g:.3));",
1348 "(((a:.1,b:.1)'25%:G':.6,(c:.1,d:.1)'17%':.5):.1,((e:.1,f:.1)'r:g':.2,(g:.1,h:.1)'g':.3):.1);");
1350 TEST_EXPECT_TREESTRING_OK(
"(:.2,:2);",
"(unnamed1:.2,unnamed2:2);");
1355 const char *tooSmallTree[] = {
1365 for (
size_t i = 0; i<
ARRAY_ELEMS(tooSmallTree); ++i) {
1367 TreeNode *tree = loadFromFileContaining(tooSmallTree[i],
NULp);
1368 TEST_EXPECT_TREELOAD_FAILED_WITH(tree,
"tree is too small");
1370 TEST_ANNOTATE(
NULp);
1374 TEST_EXPECT_TREESTRING_FAILS_WITH(
"(;);",
"Expected one of ',)'");
1376 TEST_EXPECT_TREESTRING_FAILS_WITH(
"(17",
"Expected one of ',)' while end-of-file was reached");
1377 TEST_EXPECT_TREESTRING_FAILS_WITH(
"((((",
"Expected one of ',)' while end-of-file was reached");
1378 TEST_EXPECT_TREESTRING_FAILS_WITH(
"(a, 'b",
"Expected one of ',)' while end-of-file was reached");
1379 TEST_EXPECT_TREESTRING_FAILS_WITH(
"((a, ",
"Expected one of ',)' while end-of-file was reached");
1380 TEST_EXPECT_TREESTRING_FAILS_WITH(
"((a,'b ",
"Expected one of ',)' while end-of-file was reached");
1382 TEST_EXPECT_TREESTRING_FAILS_WITH(
"(a, b:5::::",
"Unexpected ':' (already read a branchlength) while looking at '::::<EOF>'");
1383 TEST_EXPECT_TREESTRING_FAILS_WITH(
"(a, b:5:c:d",
"Unexpected ':' (already read a branchlength) while looking at ':c:d<EOF>'");
1384 TEST_EXPECT_TREESTRING_FAILS_WITH(
"(a, b:5:c:d)",
"Unexpected ':' (already read a branchlength) while looking at ':c:d)<EOF>'");
1386 TEST_EXPECT_TREESTRING_FAILS_WITH(
"[unclosed\ncomment",
"while reading comment");
1387 TEST_EXPECT_TREESTRING_FAILS_WITH(
"[unclosed\ncomment [ bla ]",
"while reading comment");
1389 TEST_EXPECT_TREESTRING_FAILS_WITH(
"(a, b:d)",
"Expected valid length while looking at 'd)<EOF>'");
1391 TEST_EXPECT_TREESTRING_FAILS_WITH(
"((a:0,b:.),c:.3);",
"cannot convert '.' to float while looking at '),c:.3);<EOF>'");
1393 expectedFormat =
nALL;
1396 #pragma GCC diagnostic push
1397 #pragma GCC diagnostic ignored "-Wmissing-field-initializers"
1398 #define FAILS_WITH(err) NULp, NULp, err
1403 const char *failsWith;
1410 {
"grp",
":.1",
"'grp':.1" },
1411 {
":2",
":.1",
":2" },
1412 {
"'rem:'",
"'rem:':.1" },
1414 {
"17.2",
"'17%':.1" },
1415 {
"17.2%",
"'17%':.1" },
1416 {
"32.7",
"'33%':.1" },
1417 {
"32.7%",
"'33%':.1" },
1418 {
"0.572",
"'57%':.1" },
1419 {
"0.725",
"'73%':.1" },
1422 {
"grp:2",
":.1",
"'grp':2" },
1423 {
"'grp':2",
":.1",
"'grp':2" },
1424 {
"'rem:':2",
"'rem:':.1",
"'rem:':2" },
1425 {
"0.725:2",
"'73%':.1",
"'73%':2" },
1426 {
"'0.725':2",
"'73%':.1",
"'73%':2" },
1428 {
"'rem:grp'",
"'rem:':.1",
"'rem:grp':.1" },
1429 {
"'3%:grp'",
"'3%':.1",
"'3%:grp':.1" },
1431 {
"'3%:grp':2",
"'3%':.1",
"'3%:grp':2" },
1432 {
"':grp':2",
":.1",
"'grp':2" },
1433 {
"'3%:':2",
"'3%':.1",
"'3%':2" },
1434 {
"':':2",
":.1",
":2" },
1438 {
"'95/80:group'",
"'95/80:':.1",
"'95/80:group':.1" },
1439 {
"'95/80:'",
"'95/80:':.1" },
1440 {
"'98.6 / 89.3:group'",
"'98.6 / 89.3:':.1",
"'98.6 / 89.3:group':.1" },
1441 {
"'98.6 / 89.3:'",
"'98.6 / 89.3:':.1" },
1442 {
"'98.6% / 89.3%:group'",
"'98.6% / 89.3%:':.1",
"'98.6% / 89.3%:group':.1" },
1443 {
"'98.6% / 89.3%:'",
"'98.6% / 89.3%:':.1", },
1446 {
"grp:", FAILS_WITH(
"Expected valid length while looking at ');<EOF>'") },
1447 {
"2%:", FAILS_WITH(
"Expected valid length while looking at ');<EOF>'") },
1448 {
".2:", FAILS_WITH(
"Expected valid length while looking at ');<EOF>'") },
1449 {
"'rem:grp':", FAILS_WITH(
"Expected valid length while looking at ');<EOF>'") },
1450 {
"'rem:':", FAILS_WITH(
"Expected valid length while looking at ');<EOF>'") },
1451 {
"'':", FAILS_WITH(
"Expected valid length while looking at ');<EOF>'") },
1453 {
"'2%:4%'", FAILS_WITH(
"Invalid group name '4%' (would be misinterpreted as plain inner node with a support value of 4% if re-imported from newick file) while looking at ');<EOF>'") },
1454 {
"'2:4'", FAILS_WITH(
"Invalid group name '4' (would be misinterpreted as plain inner node with a support value of 4% if re-imported from newick file) while looking at ');<EOF>'") },
1455 {
"'.2:.4'", FAILS_WITH(
"Invalid group name '.4' (would be misinterpreted as plain inner node with a support value of 40% if re-imported from newick file) while looking at ');<EOF>'") },
1457 {
"'rem:grp:bad'", FAILS_WITH(
"Invalid group name 'grp:bad' (would be misinterpreted as group named 'bad' with remark 'grp' if re-imported from newick file) while looking at ');<EOF>'") },
1460 {
"'.55:'",
"'55%':.1" },
1461 {
"'66%:'",
"'66%':.1" },
1462 {
"'.77:':.2",
"'77%':.1",
"'77%':.2" },
1463 {
"'88%:':.3",
"'88%':.1",
"'88%':.3" },
1466 #pragma GCC diagnostic pop
1468 for (
size_t i = 0; i<
ARRAY_ELEMS(labelTest); ++i) {
1469 const NodeInfo& nodeInfo = labelTest[i];
1473 TreeNode *tree = loadFromFileContaining(treeString,
NULp);
1475 if (nodeInfo.failsWith) {
1476 TEST_EXPECT_TREELOAD_FAILED_WITH(tree, nodeInfo.failsWith);
1479 const char *leftNodeInfo = nodeInfo.left;
1480 const char *rightNodeInfo = nodeInfo.right ? nodeInfo.right : leftNodeInfo;
1482 if (nodeInfo.right && strcmp(leftNodeInfo, rightNodeInfo) == 0) {
1483 const char *UNWANTED_CONDITION =
GBS_global_string(
"Only specify one, if both node-infos contain the same string: <%s>", leftNodeInfo);
1488 TEST_EXPECT_TREELOAD(tree, newick);
1494 TEST_ANNOTATE(
NULp);
1497 expectedFormat =
nALL;
1500 TEST_EXPECT_TREESTRING_OK(
"((((A:1,B:1)'95/80:INT1':1,C:2)'99/98:INT2':0.5),(D:1,E:1)'30/40:INT3':3)'100/100';",
1501 "(((A:1,B:1)'95/80:INT1':1,C:2)'99/98:INT2':.5,(D:1,E:1)'99/98:INT3':3)'100/100';");
1503 TEST_EXPECT_TREESTRING_OK(
"((((A:1,B:1)'95.7% / 80.3%:':1,C:2)'99.9 / 98.5:':0.5)g1,(D:1,E:1)'30.4/40.8:g2':3);",
1504 "(((A:1,B:1)'95.7% / 80.3%:':1,C:2)'99.9 / 98.5:g1':.5,(D:1,E:1)'99.9 / 98.5:g2':3);");
1508 TEST_EXPECT_TREESTRING_OK_WITH_WARNINGS(
"(a,b):0.5",
"(a,b);",
"Length specified for root-node has been ignored");
1509 TEST_EXPECT_TREESTRING_OK_WITH_WARNINGS(
"(a, b))",
"(a,b);",
"Unexpected input-data after tree: ')'");
1513 TEST_EXPECT_TREESTRING_OK(
"(a*,b%);",
"(a*,b%);");
1514 TEST_EXPECT_TREESTRING_OK(
"(a, b:5)",
"(a,b);");
1515 TEST_EXPECT_TREESTRING_OK(
"(c,(b,'a:b:':.2));",
"(c,(b,a:b:));");
1518 TEST_EXPECT_TREEFILE_FAILS_WITH(
"trees/nosuch.tree",
"No such file");
1519 TEST_EXPECT_TREEFILE_FAILS_WITH(
"trees/corrupted.tree",
"Error reading");
1524 #endif // UNIT_TESTS
GB_ERROR get_warnings() const
void set_bootstrap(double bootstrap)
AliDataPtr format(AliDataPtr data, const size_t wanted_len, GB_ERROR &error)
GB_ERROR GBT_write_tree_with_remark(GBDATA *gb_main, const char *tree_name, TreeNode *tree, const char *remark)
#define implicated(hypothesis, conclusion)
const TreeNode * get_root_node() const
static void warningToConsumingReader(const char *msg)
char * GBT_tree_2_newick(const TreeNode *tree, NewickFormat format, bool compact)
bool is_marked_as_default_len(GBT_LEN len)
GB_ERROR GB_export_IO_error(const char *action, const char *filename)
#define DEFAULT_BRANCH_LENGTH
const char * GBS_global_string(const char *templat,...)
#define FORWARD_FORMATTED(receiver, format)
void cat(const char *from)
int GB_unlink(const char *path)
void append_leaf_redef_details(GBS_strstruct &summary, int droppedAtLeaf, int droppedRedefined)
void use_as_remark(const SmartCharPtr &newRemark)
#define ARRAY_ELEMS(array)
char buffer[MESSAGE_BUFFERSIZE]
void TREE_scale(TreeNode *tree, double length_scale, double bootstrap_scale, WarningConsumer warn)
double get_max_found_bootstrap() const
__ATTR__FORMAT(2) void add_warningf(const char *format
bool has_valid_root_remarks() const
static TreeReader * consumingReader
#define DEFAULT_BRANCH_LENGTH_MARKER
#define TEST_EXPECT_CONTAINS(str, part)
GB_ERROR GB_await_error()
#define tree_assert(cond)
#define is_equal_to_NULL()
virtual TreeNode * makeNode() const =0
TreeReader(FILE *input, const char *file_name, TreeRoot *troot_)
void message(char *errortext)
#define TEST_REJECT(cond)
#define TEST_REJECT_NULL(n)
static void error(const char *msg)
TreeNode * TREE_load(const char *path, TreeRoot *troot, char **commentPtr, bool allow_length_scaling, char **warningPtr)
expectation_group & add(const expectation &e)
bool parse_treelabel(const char *&label, double &bootstrap, char *&remark)
#define TEST_EXPECT_ZERO_OR_SHOW_ERRNO(iocond)
static SearchTree * tree[SEARCH_PATTERNS]
NOT4PERL GB_ERROR GB_safe_atof(const char *str, float *res)
char * GBS_log_action_to(const char *comment, const char *action, bool stamp)
#define does_differ_from_NULL()
GBT_LEN get_max_found_branchlen() const
#define does_contain(val)
fputs(TRACE_PREFIX, stderr)
GB_ERROR GB_export_errorf(const char *templat,...)
GB_ERROR TREE_translate_labels(GBDATA *gb_main, TreeNode *tree, const LabelTranslator &translator)
#define TEST_EXPECT_NULL(n)
static list< LineAttachedMessage > warnings
GB_ERROR close(GB_ERROR error)
bool is_inner_node_with_remark() const
#define fulfills(pred, arg)
GB_ERROR TREE_load_to_db(GBDATA *gb_main, const char *treefile, const char *tree_name, const LabelTranslator &translator)
const char * name_only(const char *fullpath)
#define __ATTR__USERESULT
void announce_tree_constructed()
static TreeNode * createLinkedTreeNode(const TreeRoot &nodeMaker, TreeNode *left, GBT_LEN leftlen, TreeNode *right, GBT_LEN rightlen)
void GBT_message(GBDATA *gb_main, const char *msg)
void add_drop_summary_warning_if_suppressed()
void set_remark(const char *newRemark)
void add_warning(const char *msg)
void GBT_split_string(ConstStrArray &dest, const char *namelist, const char *separator, SplitMode mode)
const char * get_data() const
#define is_more_or_equal(val)
GB_transaction ta(gb_var)
GB_ERROR GBT_check_valid_group_name(const char *new_group_name)
void destroy(TreeNode *that)
const char * get_remark() const
const SmartCharPtr & get_remark_ptr() const
#define STATIC_ASSERT(const_expression)
#define TEST_EXPECT_EQUAL(expr, want)
size_t get_position() const
char * GBS_global_string_copy(const char *templat,...)
GB_write_int const char s