1 //-----------------------------------------------------------------------------
2 /** @file libboardgame_base/tests/TreeReaderTest.cpp
3     @author Markus Enzenberger
4     @copyright GNU General Public License version 3 or later */
5 //-----------------------------------------------------------------------------
6 
7 #include "libboardgame_base/TreeReader.h"
8 
9 #include <sstream>
10 #include "libboardgame_base/TreeWriter.h"
11 #include "libboardgame_test/Test.h"
12 
13 using namespace std;
14 using namespace libboardgame_base;
15 
16 //-----------------------------------------------------------------------------
17 
LIBBOARDGAME_TEST_CASE(sgf_tree_reader_basic)18 LIBBOARDGAME_TEST_CASE(sgf_tree_reader_basic)
19 {
20     istringstream in("(;B[aa];W[bb])");
21     TreeReader reader;
22     reader.read(in);
23     auto& root = reader.get_tree();
24     LIBBOARDGAME_CHECK(root.has_property("B"));
25     LIBBOARDGAME_CHECK(root.has_single_child());
26     auto& child = root.get_child();
27     LIBBOARDGAME_CHECK(child.has_property("W"));
28     LIBBOARDGAME_CHECK(! child.has_children());
29 }
30 
LIBBOARDGAME_TEST_CASE(sgf_tree_reader_basic_2)31 LIBBOARDGAME_TEST_CASE(sgf_tree_reader_basic_2)
32 {
33     istringstream in("(;C[1](;C[2.1])(;C[2.2]))");
34     TreeReader reader;
35     reader.read(in);
36     auto& root = reader.get_tree();
37     LIBBOARDGAME_CHECK_EQUAL(root.get_property("C"), "1");
38     LIBBOARDGAME_CHECK_EQUAL(root.get_nu_children(), 2u);
39     LIBBOARDGAME_CHECK_EQUAL(root.get_child(0).get_property("C"), "2.1");
40     LIBBOARDGAME_CHECK_EQUAL(root.get_child(1).get_property("C"), "2.2");
41 }
42 
LIBBOARDGAME_TEST_CASE(sgf_tree_reader_multiprop_with_whitespace)43 LIBBOARDGAME_TEST_CASE(sgf_tree_reader_multiprop_with_whitespace)
44 {
45     istringstream in("(;A [1]\n[2] [3]\t[4]\r\n[5])");
46     TreeReader reader;
47     reader.read(in);
48     auto& root = reader.get_tree();
49     auto values = root.get_multi_property("A");
50     LIBBOARDGAME_CHECK_EQUAL(values.size(), 5u);
51     LIBBOARDGAME_CHECK_EQUAL(values[0], "1");
52     LIBBOARDGAME_CHECK_EQUAL(values[1], "2");
53     LIBBOARDGAME_CHECK_EQUAL(values[2], "3");
54     LIBBOARDGAME_CHECK_EQUAL(values[3], "4");
55     LIBBOARDGAME_CHECK_EQUAL(values[4], "5");
56 }
57 
58 /** Test that a property value with a unicode character is preserved after
59     reading and writing.
60     In previous versions this was broken because of a bug in the replacement
61     of non-newline whitespaces (as required by SGF) by the writer. (The bug
62     occurred only on some platforms depending on the std::isspace()
63     implementation.) */
LIBBOARDGAME_TEST_CASE(sgf_tree_reader_unicode)64 LIBBOARDGAME_TEST_CASE(sgf_tree_reader_unicode)
65 {
66     SgfNode root;
67     const char* id = "C";
68     const char* value = u8"ü";
69     root.set_property(id, value);
70     ostringstream out;
71     TreeWriter writer(out, root);
72     writer.write();
73     istringstream in(out.str());
74     TreeReader reader;
75     reader.read(in);
76     LIBBOARDGAME_CHECK_EQUAL(reader.get_tree().get_property(id), value);
77 }
78 
LIBBOARDGAME_TEST_CASE(sgf_tree_reader_property_after_newline)79 LIBBOARDGAME_TEST_CASE(sgf_tree_reader_property_after_newline)
80 {
81     istringstream in("(;FF[4]\n"
82                      "CA[UTF-8])");
83     TreeReader reader;
84     reader.read(in);
85     auto& root = reader.get_tree();
86     LIBBOARDGAME_CHECK(root.has_property("FF"));
87     LIBBOARDGAME_CHECK(root.has_property("CA"));
88 }
89 
90 /** Test cross-platform handling of property values containing newlines.
91     The reader should convert all platform-dependent newline sequences (LF,
92     CR+LF, CR) into LF, such that property values containing newlines are
93     independent on the platform that was used to write the file. */
LIBBOARDGAME_TEST_CASE(sgf_tree_reader_newline)94 LIBBOARDGAME_TEST_CASE(sgf_tree_reader_newline)
95 {
96     {
97         istringstream in("(;C[1\n2])");
98         TreeReader reader;
99         reader.read(in);
100         auto& root = reader.get_tree();
101         LIBBOARDGAME_CHECK_EQUAL(root.get_property("C"), "1\n2");
102     }
103     {
104         istringstream in("(;C[1\r\n2])");
105         TreeReader reader;
106         reader.read(in);
107         auto& root = reader.get_tree();
108         LIBBOARDGAME_CHECK_EQUAL(root.get_property("C"), "1\n2");
109     }
110     {
111         istringstream in("(;C[1\r2])");
112         TreeReader reader;
113         reader.read(in);
114         auto& root = reader.get_tree();
115         LIBBOARDGAME_CHECK_EQUAL(root.get_property("C"), "1\n2");
116     }
117 }
118 
LIBBOARDGAME_TEST_CASE(sgf_tree_reader_property_without_value)119 LIBBOARDGAME_TEST_CASE(sgf_tree_reader_property_without_value)
120 {
121     istringstream in("(;B)");
122     TreeReader reader;
123     LIBBOARDGAME_CHECK_THROW(reader.read(in), TreeReader::ReadError);
124 }
125 
LIBBOARDGAME_TEST_CASE(sgf_tree_reader_text_before_node)126 LIBBOARDGAME_TEST_CASE(sgf_tree_reader_text_before_node)
127 {
128     istringstream in("(B;)");
129     TreeReader reader;
130     LIBBOARDGAME_CHECK_THROW(reader.read(in), TreeReader::ReadError);
131 }
132 
133 //-----------------------------------------------------------------------------
134