csv: Add a reader.
[nit.git] / lib / csv / test_csv.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # This file is free software, which comes along with NIT. This software is
4 # distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
5 # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
6 # PARTICULAR PURPOSE. You can modify it is you want, provided this header
7 # is kept unaltered, and a notification of the changes is added.
8 # You are allowed to redistribute it and sell it, alone or is a part of
9 # another product.
10
11 # Tests for `csv`.
12 module test_csv is test_suite
13
14 import test_suite
15 import csv
16
17 class TestCsvWriter
18 super TestSuite
19
20 # The custom CSV format used in the tests.
21 private var custom_format = new CsvFormat('/', ':', "#")
22
23 # Expect to write `row` as `expected_rfc4180` and as `expected_custom`.
24 #
25 # Parameters:
26 #
27 # * `always_escape`: value of the `always_escape` option.
28 # * `row`: row to write.
29 # * `expected_rfc4180`: expected result in RFC 4180.
30 # * `expected_custom`: expected result in the custom CSV format.
31 private fun expect(always_escape: Bool, row: SequenceRead[String],
32 expected_rfc4180: String,
33 expected_custom: String) do
34 var out = new StringOStream
35 var writer = new CsvWriter(out)
36
37 writer.always_escape = always_escape
38 writer.write_sequence(row)
39 assert out.to_s == expected_rfc4180 else
40 sys.stderr.write "\nFormat: RFC 4180\n"
41 sys.stderr.write "Expecting: \"{expected_rfc4180.escape_to_nit}\"\n"
42 sys.stderr.write "Got: \"{out.to_s.escape_to_nit}\"\n"
43 end
44 writer.close
45
46 out = new StringOStream
47 writer = new CsvWriter.with_format(out, custom_format)
48 writer.always_escape = always_escape
49 writer.write_sequence(row)
50 assert out.to_s == expected_custom else
51 sys.stderr.write "\nFormat: {custom_format.delimiter}"
52 sys.stderr.write " {custom_format.separator}"
53 sys.stderr.write " {custom_format.eol.escape_to_nit}\n"
54 sys.stderr.write "Expecting: \"{expected_custom.escape_to_nit}\"\n"
55 sys.stderr.write "Got: \"{out.to_s.escape_to_nit}\"\n"
56 end
57 writer.close
58 end
59
60 fun test_empty do expect(true, new Array[String], "\r\n", "#")
61
62 fun test_one_cell do expect(true, ["foo/\"\r\n,"],
63 "\"foo/\"\"\r\n,\"\r\n",
64 "/foo//\"\r\n,/#")
65
66 fun test_optimize_size_escaped do expect(false, ["foo/\"\r\n,"],
67 "\"foo/\"\"\r\n,\"\r\n",
68 "/foo//\"\r\n,/#")
69
70 fun test_optimize_size_eol do expect(false, ["foo\r#\n"],
71 "\"foo\r#\n\"\r\n",
72 "/foo\r#\n/#")
73
74 fun test_optimize_size_unescaped do expect(false, ["foo"],
75 "foo\r\n",
76 "foo#")
77
78 fun test_multiple_cells do expect(true, ["1", "", "/"],
79 "\"1\",,\"/\"\r\n",
80 "/1/::////#")
81
82 fun test_multiple_cells_optimize_size do expect(false, ["1", "", "/"],
83 "1,,/\r\n",
84 "1::////#")
85 end
86
87 class TestCsvReader
88 super TestSuite
89
90 # The custom CSV format used in the tests.
91 private var custom_format = new CsvFormat('/', ':', "#")
92
93 # Expect to read `expected`.
94 #
95 # Parameters:
96 #
97 # * `skip_empty`: value of the `skip_empty` option.
98 # * `modal_escaping`: value of the `modal_escaping` option.
99 # * `input_rfc4180`: input in the RFC 4180 format.
100 # * `input_custom`: input in the custom CSV format.
101 # * `expected`: expected resulting table.
102 private fun expect(skip_empty: Bool,
103 input_rfc4180: String,
104 input_custom: String,
105 expected: SequenceRead[SequenceRead[String]]) do
106 var istream: IStream
107 var reader: CsvReader
108 var i = 0
109
110 istream = new StringIStream(input_rfc4180)
111 reader = new CsvReader(istream)
112 reader.skip_empty = skip_empty
113 assert_table_equals("RFC 4180", reader, expected.iterator)
114
115 istream = new StringIStream(input_custom)
116 reader = new CsvReader.with_format(istream, custom_format)
117 reader.skip_empty = skip_empty
118 assert_table_equals("{custom_format.delimiter} " +
119 "{custom_format.separator} " +
120 "{custom_format.eol.escape_to_nit}", reader, expected.iterator)
121 end
122
123 # Check if tables are equal.
124 private fun assert_table_equals(format: String,
125 actual: Iterator[SequenceRead[String]],
126 expected: Iterator[SequenceRead[String]]) do
127 var i = 0
128
129 for actual_row in actual do
130 assert expected.is_ok else fail(format,"Too many rows.")
131 var expected_row = expected.item
132 assert_row_equals(format, i, actual_row, expected_row)
133 expected.next
134 i += 1
135 end
136 assert not expected.is_ok else fail(format, "Not enough rows.")
137 expected.finish
138 end
139
140 # Check if rows are equal.
141 private fun assert_row_equals(format: String,
142 row_index: Int,
143 actual: SequenceRead[String],
144 expected: SequenceRead[String]) do
145 assert actual == expected else
146 fail(format, """
147 At row {{{row_index}}}.
148 Expecting: {{{expected.join("|")}}}
149 Got: {{{actual.join("|")}}}""")
150 end
151 end
152
153 # Output an error message with an indication of the format used.
154 private fun fail(format: Text, message: Text) do
155 sys.stderr.write "\nFormat: {format}\n"
156 sys.stderr.write message
157 sys.stderr.write "\n"
158 end
159
160 fun test_empty do expect(false, "", "", new Array[Array[String]])
161
162 fun test_empty_eol do expect(false, "\r\n", "#", [[""]])
163
164 fun test_empty_skip do expect(true, "", "", new Array[Array[String]])
165
166 fun test_empty_skip1 do expect(true, "\r\n", "#", new Array[Array[String]])
167
168 fun test_empty_skip2 do expect(true, "\r\n\r\n", "##", new Array[Array[String]])
169
170 fun test_escaped do expect(false, "\"foo/\"\"\r\n,\"\r\n",
171 "/foo//\"\r\n,/#", [["foo/\"\r\n,"]])
172
173 fun test_unescaped do expect(false, "foo bar\r\n",
174 "foo bar#", [["foo bar"]])
175
176 fun test_escaped_no_eol do expect(false, "\"foo/\"\"\r\n,\"",
177 "/foo//\"\r\n,/", [["foo/\"\r\n,"]])
178
179 fun test_unescaped_no_eol do expect(false, "foo bar",
180 "foo bar", [["foo bar"]])
181
182 fun test_multiple_cells do expect(false, "\"1\",,\"/\"\r\n",
183 "/1/::////#", [["1", "", "/"]])
184
185 fun test_multiple_cells_unescaped do expect(false, "1,,/\r\n",
186 "1::////#", [["1", "", "/"]])
187
188 fun test_modal_escaping do expect(false, """a"b""/c","d"e""",
189 """/ab"///c:d/e/""", [["""ab"/c""", "de"]])
190
191 fun test_skip_start do expect(true, "\r\n1,,/\r\n",
192 "#1::////#", [["1", "", "/"]])
193
194 fun test_dont_skip_empty_delimited do expect(true, "\"\"\r\n",
195 "//#", [[""]])
196
197 fun test_dont_skip_multiple_empty_cells do expect(true, ",\r\n",
198 ":#", [["",""]])
199
200 fun test_mutiple_rows do expect(false, "\"a\r\nb#\",c\r\nd,\r\n,e\r\n",
201 "/a\r\nb#/:c#d:#:e#", [["a\r\nb#", "c"], ["d", ""], ["", "e"]])
202 end