1#!/usr/bin/env ruby -w
2# encoding: UTF-8
3
4# tc_interface.rb
5#
6#  Created by James Edward Gray II on 2005-10-31.
7#  Copyright 2005 James Edward Gray II. You can redistribute or modify this code
8#  under the terms of Ruby's license.
9
10require_relative "base"
11require "tempfile"
12
13class TestCSV::Interface < TestCSV
14  extend DifferentOFS
15
16  def setup
17    super
18    @tempfile = Tempfile.new(%w"temp .csv")
19    @tempfile.close
20    @path = @tempfile.path
21
22    File.open(@path, "wb") do |file|
23      file << "1\t2\t3\r\n"
24      file << "4\t5\r\n"
25    end
26
27    @expected = [%w{1 2 3}, %w{4 5}]
28  end
29
30  def teardown
31    @tempfile.close(true)
32    super
33  end
34
35  ### Test Read Interface ###
36
37  def test_foreach
38    CSV.foreach(@path, col_sep: "\t", row_sep: "\r\n") do |row|
39      assert_equal(@expected.shift, row)
40    end
41  end
42
43  def test_open_and_close
44    csv = CSV.open(@path, "r+", col_sep: "\t", row_sep: "\r\n")
45    assert_not_nil(csv)
46    assert_instance_of(CSV, csv)
47    assert_equal(false, csv.closed?)
48    csv.close
49    assert(csv.closed?)
50
51    ret = CSV.open(@path) do |new_csv|
52      csv = new_csv
53      assert_instance_of(CSV, new_csv)
54      "Return value."
55    end
56    assert(csv.closed?)
57    assert_equal("Return value.", ret)
58  end
59
60  def test_parse
61    data = File.binread(@path)
62    assert_equal( @expected,
63                  CSV.parse(data, col_sep: "\t", row_sep: "\r\n") )
64
65    CSV.parse(data, col_sep: "\t", row_sep: "\r\n") do |row|
66      assert_equal(@expected.shift, row)
67    end
68  end
69
70  def test_parse_line
71    row = CSV.parse_line("1;2;3", col_sep: ";")
72    assert_not_nil(row)
73    assert_instance_of(Array, row)
74    assert_equal(%w{1 2 3}, row)
75
76    # shortcut interface
77    row = "1;2;3".parse_csv(col_sep: ";")
78    assert_not_nil(row)
79    assert_instance_of(Array, row)
80    assert_equal(%w{1 2 3}, row)
81  end
82
83  def test_parse_line_with_empty_lines
84    assert_equal(nil,       CSV.parse_line(""))  # to signal eof
85    assert_equal(Array.new, CSV.parse_line("\n1,2,3"))
86  end
87
88  def test_read_and_readlines
89    assert_equal( @expected,
90                  CSV.read(@path, col_sep: "\t", row_sep: "\r\n") )
91    assert_equal( @expected,
92                  CSV.readlines(@path, col_sep: "\t", row_sep: "\r\n") )
93
94
95    data = CSV.open(@path, col_sep: "\t", row_sep: "\r\n") do |csv|
96      csv.read
97    end
98    assert_equal(@expected, data)
99    data = CSV.open(@path, col_sep: "\t", row_sep: "\r\n") do |csv|
100      csv.readlines
101    end
102    assert_equal(@expected, data)
103  end
104
105  def test_table
106    table = CSV.table(@path, col_sep: "\t", row_sep: "\r\n")
107    assert_instance_of(CSV::Table, table)
108    assert_equal([[:"1", :"2", :"3"], [4, 5, nil]], table.to_a)
109  end
110
111  def test_shift  # aliased as gets() and readline()
112    CSV.open(@path, "rb+", col_sep: "\t", row_sep: "\r\n") do |csv|
113      assert_equal(@expected.shift, csv.shift)
114      assert_equal(@expected.shift, csv.shift)
115      assert_equal(nil, csv.shift)
116    end
117  end
118
119  def test_enumerators_are_supported
120    CSV.open(@path, col_sep: "\t", row_sep: "\r\n") do |csv|
121      enum = csv.each
122      assert_instance_of(Enumerator, enum)
123      assert_equal(@expected.shift, enum.next)
124    end
125  end
126
127  ### Test Write Interface ###
128
129  def test_generate
130    str = CSV.generate do |csv|  # default empty String
131      assert_instance_of(CSV, csv)
132      assert_equal(csv, csv << [1, 2, 3])
133      assert_equal(csv, csv << [4, nil, 5])
134    end
135    assert_not_nil(str)
136    assert_instance_of(String, str)
137    assert_equal("1,2,3\n4,,5\n", str)
138
139    CSV.generate(str) do |csv|   # appending to a String
140      assert_equal(csv, csv << ["last", %Q{"row"}])
141    end
142    assert_equal(%Q{1,2,3\n4,,5\nlast,"""row"""\n}, str)
143  end
144
145  def test_generate_line
146    line = CSV.generate_line(%w{1 2 3}, col_sep: ";")
147    assert_not_nil(line)
148    assert_instance_of(String, line)
149    assert_equal("1;2;3\n", line)
150
151    # shortcut interface
152    line = %w{1 2 3}.to_csv(col_sep: ";")
153    assert_not_nil(line)
154    assert_instance_of(String, line)
155    assert_equal("1;2;3\n", line)
156  end
157
158  def test_write_header_detection
159    File.unlink(@path)
160
161    headers = %w{a b c}
162    CSV.open(@path, "w", headers: true) do |csv|
163      csv << headers
164      csv << %w{1 2 3}
165      assert_equal(headers, csv.instance_variable_get(:@headers))
166    end
167  end
168
169  def test_write_lineno
170    File.unlink(@path)
171
172    CSV.open(@path, "w") do |csv|
173      lines = 20
174      lines.times { csv << %w{a b c} }
175      assert_equal(lines, csv.lineno)
176    end
177  end
178
179  def test_write_hash
180    File.unlink(@path)
181
182    lines = [{a: 1, b: 2, c: 3}, {a: 4, b: 5, c: 6}]
183    CSV.open( @path, "wb", headers:           true,
184                           header_converters: :symbol ) do |csv|
185      csv << lines.first.keys
186      lines.each { |line| csv << line }
187    end
188    CSV.open( @path, "rb", headers:           true,
189                           converters:        :all,
190                           header_converters: :symbol ) do |csv|
191      csv.each { |line| assert_equal(lines.shift, line.to_hash) }
192    end
193  end
194
195  def test_write_hash_with_headers_array
196    File.unlink(@path)
197
198    lines = [{a: 1, b: 2, c: 3}, {a: 4, b: 5, c: 6}]
199    CSV.open(@path, "wb", headers: [:b, :a, :c]) do |csv|
200      lines.each { |line| csv << line }
201    end
202
203    # test writing fields in the correct order
204    File.open(@path, "rb") do |f|
205      assert_equal("2,1,3", f.gets.strip)
206      assert_equal("5,4,6", f.gets.strip)
207    end
208
209    # test reading CSV with headers
210    CSV.open( @path, "rb", headers:    [:b, :a, :c],
211                           converters: :all ) do |csv|
212      csv.each { |line| assert_equal(lines.shift, line.to_hash) }
213    end
214  end
215
216  def test_write_hash_with_headers_string
217    File.unlink(@path)
218
219    lines = [{"a" => 1, "b" => 2, "c" => 3}, {"a" => 4, "b" => 5, "c" => 6}]
220    CSV.open(@path, "wb", headers: "b|a|c", col_sep: "|") do |csv|
221      lines.each { |line| csv << line }
222    end
223
224    # test writing fields in the correct order
225    File.open(@path, "rb") do |f|
226      assert_equal("2|1|3", f.gets.strip)
227      assert_equal("5|4|6", f.gets.strip)
228    end
229
230    # test reading CSV with headers
231    CSV.open( @path, "rb", headers:    "b|a|c",
232                           col_sep:    "|",
233                           converters: :all ) do |csv|
234      csv.each { |line| assert_equal(lines.shift, line.to_hash) }
235    end
236  end
237
238  def test_write_headers
239    File.unlink(@path)
240
241    lines = [{"a" => 1, "b" => 2, "c" => 3}, {"a" => 4, "b" => 5, "c" => 6}]
242    CSV.open( @path, "wb", headers:       "b|a|c",
243                           write_headers: true,
244                           col_sep:       "|" ) do |csv|
245      lines.each { |line| csv << line }
246    end
247
248    # test writing fields in the correct order
249    File.open(@path, "rb") do |f|
250      assert_equal("b|a|c", f.gets.strip)
251      assert_equal("2|1|3", f.gets.strip)
252      assert_equal("5|4|6", f.gets.strip)
253    end
254
255    # test reading CSV with headers
256    CSV.open( @path, "rb", headers:    true,
257                           col_sep:    "|",
258                           converters: :all ) do |csv|
259      csv.each { |line| assert_equal(lines.shift, line.to_hash) }
260    end
261  end
262
263  def test_append  # aliased add_row() and puts()
264    File.unlink(@path)
265
266    CSV.open(@path, "wb", col_sep: "\t", row_sep: "\r\n") do |csv|
267      @expected.each { |row| csv << row }
268    end
269
270    test_shift
271
272    # same thing using CSV::Row objects
273    File.unlink(@path)
274
275    CSV.open(@path, "wb", col_sep: "\t", row_sep: "\r\n") do |csv|
276      @expected.each { |row| csv << CSV::Row.new(Array.new, row) }
277    end
278
279    test_shift
280  end
281
282  ### Test Read and Write Interface ###
283
284  def test_filter
285    assert_respond_to(CSV, :filter)
286
287    expected = [[1, 2, 3], [4, 5]]
288    CSV.filter( "1;2;3\n4;5\n", (result = String.new),
289                in_col_sep: ";", out_col_sep: ",",
290                converters: :all ) do |row|
291      assert_equal(row, expected.shift)
292      row.map! { |n| n * 2 }
293      row << "Added\r"
294    end
295    assert_equal("2,4,6,\"Added\r\"\n8,10,\"Added\r\"\n", result)
296  end
297
298  def test_instance
299    csv = String.new
300
301    first = nil
302    assert_nothing_raised(Exception) do
303      first =  CSV.instance(csv, col_sep: ";")
304      first << %w{a b c}
305    end
306
307    assert_equal("a;b;c\n", csv)
308
309    second = nil
310    assert_nothing_raised(Exception) do
311      second =  CSV.instance(csv, col_sep: ";")
312      second << [1, 2, 3]
313    end
314
315    assert_equal(first.object_id, second.object_id)
316    assert_equal("a;b;c\n1;2;3\n", csv)
317
318    # shortcuts
319    assert_equal(STDOUT, CSV.instance.instance_eval { @io })
320    assert_equal(STDOUT, CSV { |new_csv| new_csv.instance_eval { @io } })
321  end
322
323  def test_options_are_not_modified
324    opt = {}.freeze
325    assert_nothing_raised {  CSV.foreach(@path, opt)       }
326    assert_nothing_raised {  CSV.open(@path, opt){}        }
327    assert_nothing_raised {  CSV.parse("", opt)            }
328    assert_nothing_raised {  CSV.parse_line("", opt)       }
329    assert_nothing_raised {  CSV.read(@path, opt)          }
330    assert_nothing_raised {  CSV.readlines(@path, opt)     }
331    assert_nothing_raised {  CSV.table(@path, opt)         }
332    assert_nothing_raised {  CSV.generate(opt){}           }
333    assert_nothing_raised {  CSV.generate_line([], opt)    }
334    assert_nothing_raised {  CSV.filter("", "", opt){}     }
335    assert_nothing_raised {  CSV.instance("", opt)         }
336  end
337end
338