1#!/usr/bin/env ruby -w
2# encoding: UTF-8
3
4# tc_table.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"
11
12class TestCSV::Table < TestCSV
13  extend DifferentOFS
14
15  def setup
16    super
17    @rows  = [ CSV::Row.new(%w{A B C}, [1, 2, 3]),
18               CSV::Row.new(%w{A B C}, [4, 5, 6]),
19               CSV::Row.new(%w{A B C}, [7, 8, 9]) ]
20    @table = CSV::Table.new(@rows)
21
22    @header_table = CSV::Table.new(
23      [CSV::Row.new(%w{A B C}, %w{A B C}, true)] + @rows
24    )
25  end
26
27  def test_initialze
28    assert_not_nil(@table)
29    assert_instance_of(CSV::Table, @table)
30  end
31
32  def test_modes
33    assert_equal(:col_or_row, @table.mode)
34
35    # non-destructive changes, intended for one shot calls
36    cols = @table.by_col
37    assert_equal(:col_or_row, @table.mode)
38    assert_equal(:col, cols.mode)
39    assert_equal(@table, cols)
40
41    rows = @table.by_row
42    assert_equal(:col_or_row, @table.mode)
43    assert_equal(:row, rows.mode)
44    assert_equal(@table, rows)
45
46    # destructive mode changing calls
47    assert_equal(@table, @table.by_row!)
48    assert_equal(:row, @table.mode)
49    assert_equal(@table, @table.by_col_or_row!)
50    assert_equal(:col_or_row, @table.mode)
51  end
52
53  def test_headers
54    assert_equal(@rows.first.headers, @table.headers)
55  end
56
57  def test_index
58    ##################
59    ### Mixed Mode ###
60    ##################
61    # by row
62    @rows.each_index { |i| assert_equal(@rows[i], @table[i]) }
63    assert_equal(nil, @table[100])  # empty row
64
65    # by col
66    @rows.first.headers.each do |header|
67      assert_equal(@rows.map { |row| row[header] }, @table[header])
68    end
69    assert_equal([nil] * @rows.size, @table["Z"])  # empty col
70
71    # by cell, row then col
72    assert_equal(2, @table[0][1])
73    assert_equal(6, @table[1]["C"])
74
75    # by cell, col then row
76    assert_equal(5, @table["B"][1])
77    assert_equal(9, @table["C"][2])
78
79    # with headers (by col)
80    assert_equal(["B", 2, 5, 8], @header_table["B"])
81
82    ###################
83    ### Column Mode ###
84    ###################
85    @table.by_col!
86
87    assert_equal([2, 5, 8], @table[1])
88    assert_equal([2, 5, 8], @table["B"])
89
90    ################
91    ### Row Mode ###
92    ################
93    @table.by_row!
94
95    assert_equal(@rows[1], @table[1])
96    assert_raise(TypeError) { @table["B"] }
97
98    ############################
99    ### One Shot Mode Change ###
100    ############################
101    assert_equal(@rows[1], @table[1])
102    assert_equal([2, 5, 8], @table.by_col[1])
103    assert_equal(@rows[1], @table[1])
104  end
105
106  def test_set_row_or_column
107    ##################
108    ### Mixed Mode ###
109    ##################
110    # set row
111    @table[2] = [10, 11, 12]
112    assert_equal([%w[A B C], [1, 2, 3], [4, 5, 6], [10, 11, 12]], @table.to_a)
113
114    @table[3] = CSV::Row.new(%w[A B C], [13, 14, 15])
115    assert_equal( [%w[A B C], [1, 2, 3], [4, 5, 6], [10, 11, 12], [13, 14, 15]],
116                  @table.to_a )
117
118    # set col
119    @table["Type"] = "data"
120    assert_equal( [ %w[A B C Type],
121                    [1, 2, 3, "data"],
122                    [4, 5, 6, "data"],
123                    [10, 11, 12, "data"],
124                    [13, 14, 15, "data"] ],
125                  @table.to_a )
126
127    @table["Index"] = [1, 2, 3]
128    assert_equal( [ %w[A B C Type Index],
129                    [1, 2, 3, "data", 1],
130                    [4, 5, 6, "data", 2],
131                    [10, 11, 12, "data", 3],
132                    [13, 14, 15, "data", nil] ],
133                  @table.to_a )
134
135    @table["B"] = [100, 200]
136    assert_equal( [ %w[A B C Type Index],
137                    [1, 100, 3, "data", 1],
138                    [4, 200, 6, "data", 2],
139                    [10, nil, 12, "data", 3],
140                    [13, nil, 15, "data", nil] ],
141                  @table.to_a )
142
143    # verify resulting table
144    assert_equal(<<-END_RESULT.gsub(/^\s+/, ""), @table.to_csv)
145    A,B,C,Type,Index
146    1,100,3,data,1
147    4,200,6,data,2
148    10,,12,data,3
149    13,,15,data,
150    END_RESULT
151
152    # with headers
153    @header_table["Type"] = "data"
154    assert_equal(%w[Type data data data], @header_table["Type"])
155
156    ###################
157    ### Column Mode ###
158    ###################
159    @table.by_col!
160
161    @table[1] = [2, 5, 11, 14]
162    assert_equal( [ %w[A B C Type Index],
163                    [1, 2, 3, "data", 1],
164                    [4, 5, 6, "data", 2],
165                    [10, 11, 12, "data", 3],
166                    [13, 14, 15, "data", nil] ],
167                  @table.to_a )
168
169    @table["Extra"] = "new stuff"
170    assert_equal( [ %w[A B C Type Index Extra],
171                    [1, 2, 3, "data", 1, "new stuff"],
172                    [4, 5, 6, "data", 2, "new stuff"],
173                    [10, 11, 12, "data", 3, "new stuff"],
174                    [13, 14, 15, "data", nil, "new stuff"] ],
175                  @table.to_a )
176
177    ################
178    ### Row Mode ###
179    ################
180    @table.by_row!
181
182    @table[1] = (1..6).to_a
183    assert_equal( [ %w[A B C Type Index Extra],
184                    [1, 2, 3, "data", 1, "new stuff"],
185                    [1, 2, 3, 4, 5, 6],
186                    [10, 11, 12, "data", 3, "new stuff"],
187                    [13, 14, 15, "data", nil, "new stuff"] ],
188                  @table.to_a )
189
190    assert_raise(TypeError) { @table["Extra"] = nil }
191  end
192
193  def test_each
194    ######################
195    ### Mixed/Row Mode ###
196    ######################
197    i = 0
198    @table.each do |row|
199      assert_equal(@rows[i], row)
200      i += 1
201    end
202
203    # verify that we can chain the call
204    assert_equal(@table, @table.each { })
205
206    ###################
207    ### Column Mode ###
208    ###################
209    @table.by_col!
210
211    headers = @table.headers
212    @table.each do |header, column|
213      assert_equal(headers.shift, header)
214      assert_equal(@table[header], column)
215    end
216
217    ############################
218    ### One Shot Mode Change ###
219    ############################
220    @table.by_col_or_row!
221
222    @table.each { |row| assert_instance_of(CSV::Row, row) }
223    @table.by_col.each { |tuple| assert_instance_of(Array, tuple) }
224    @table.each { |row| assert_instance_of(CSV::Row, row) }
225  end
226
227  def test_enumerable
228    assert_equal( @rows.values_at(0, 2),
229                  @table.select { |row| (row["B"] % 2).zero? } )
230
231    assert_equal(@rows[1], @table.find { |row| row["C"] > 5 })
232  end
233
234  def test_to_a
235    assert_equal([%w[A B C], [1, 2, 3], [4, 5, 6], [7, 8, 9]], @table.to_a)
236
237    # with headers
238    assert_equal( [%w[A B C], [1, 2, 3], [4, 5, 6], [7, 8, 9]],
239                  @header_table.to_a )
240  end
241
242  def test_to_csv
243    csv = <<-END_CSV.gsub(/^\s+/, "")
244    A,B,C
245    1,2,3
246    4,5,6
247    7,8,9
248    END_CSV
249
250    # normal conversion
251    assert_equal(csv, @table.to_csv)
252    assert_equal(csv, @table.to_s)  # alias
253
254    # with options
255    assert_equal( csv.gsub(",", "|").gsub("\n", "\r\n"),
256                  @table.to_csv(col_sep: "|", row_sep: "\r\n") )
257    assert_equal( csv.lines.to_a[1..-1].join(''),
258                  @table.to_csv(:write_headers => false) )
259
260    # with headers
261    assert_equal(csv, @header_table.to_csv)
262  end
263
264  def test_append
265    # verify that we can chain the call
266    assert_equal(@table, @table << [10, 11, 12])
267
268    # Array append
269    assert_equal(CSV::Row.new(%w[A B C], [10, 11, 12]), @table[-1])
270
271    # Row append
272    assert_equal(@table, @table << CSV::Row.new(%w[A B C], [13, 14, 15]))
273    assert_equal(CSV::Row.new(%w[A B C], [13, 14, 15]), @table[-1])
274  end
275
276  def test_delete_mixed
277    ##################
278    ### Mixed Mode ###
279    ##################
280    # delete a row
281    assert_equal(@rows[1], @table.delete(1))
282
283    # delete a col
284    assert_equal(@rows.map { |row| row["A"] }, @table.delete("A"))
285
286    # verify resulting table
287    assert_equal(<<-END_RESULT.gsub(/^\s+/, ""), @table.to_csv)
288    B,C
289    2,3
290    8,9
291    END_RESULT
292  end
293
294  def test_delete_column
295    ###################
296    ### Column Mode ###
297    ###################
298    @table.by_col!
299
300    assert_equal(@rows.map { |row| row[0] }, @table.delete(0))
301    assert_equal(@rows.map { |row| row["C"] }, @table.delete("C"))
302
303    # verify resulting table
304    assert_equal(<<-END_RESULT.gsub(/^\s+/, ""), @table.to_csv)
305    B
306    2
307    5
308    8
309    END_RESULT
310  end
311
312  def test_delete_row
313    ################
314    ### Row Mode ###
315    ################
316    @table.by_row!
317
318    assert_equal(@rows[1], @table.delete(1))
319    assert_raise(TypeError) { @table.delete("C") }
320
321    # verify resulting table
322    assert_equal(<<-END_RESULT.gsub(/^\s+/, ""), @table.to_csv)
323    A,B,C
324    1,2,3
325    7,8,9
326    END_RESULT
327  end
328
329  def test_delete_with_blank_rows
330    data = "col1,col2\nra1,ra2\n\nrb1,rb2"
331    table = CSV.parse(data, :headers => true)
332    assert_equal(["ra2", nil, "rb2"], table.delete("col2"))
333  end
334
335  def test_delete_if_row
336    ######################
337    ### Mixed/Row Mode ###
338    ######################
339    # verify that we can chain the call
340    assert_equal(@table, @table.delete_if { |row| (row["B"] % 2).zero? })
341
342    # verify resulting table
343    assert_equal(<<-END_RESULT.gsub(/^\s+/, ""), @table.to_csv)
344    A,B,C
345    4,5,6
346    END_RESULT
347  end
348
349  def test_delete_if_column
350    ###################
351    ### Column Mode ###
352    ###################
353    @table.by_col!
354
355    assert_equal(@table, @table.delete_if { |h, v| h > "A" })
356    assert_equal(<<-END_RESULT.gsub(/^\s+/, ""), @table.to_csv)
357    A
358    1
359    4
360    7
361    END_RESULT
362  end
363
364  def test_values_at
365    ##################
366    ### Mixed Mode ###
367    ##################
368    # rows
369    assert_equal(@rows.values_at(0, 2), @table.values_at(0, 2))
370    assert_equal(@rows.values_at(1..2), @table.values_at(1..2))
371
372    # cols
373    assert_equal([[1, 3], [4, 6], [7, 9]], @table.values_at("A", "C"))
374    assert_equal([[2, 3], [5, 6], [8, 9]], @table.values_at("B".."C"))
375
376    ###################
377    ### Column Mode ###
378    ###################
379    @table.by_col!
380
381    assert_equal([[1, 3], [4, 6], [7, 9]], @table.values_at(0, 2))
382    assert_equal([[1, 3], [4, 6], [7, 9]], @table.values_at("A", "C"))
383
384    ################
385    ### Row Mode ###
386    ################
387    @table.by_row!
388
389    assert_equal(@rows.values_at(0, 2), @table.values_at(0, 2))
390    assert_raise(TypeError) { @table.values_at("A", "C") }
391
392    ############################
393    ### One Shot Mode Change ###
394    ############################
395    assert_equal(@rows.values_at(0, 2), @table.values_at(0, 2))
396    assert_equal([[1, 3], [4, 6], [7, 9]], @table.by_col.values_at(0, 2))
397    assert_equal(@rows.values_at(0, 2), @table.values_at(0, 2))
398  end
399
400  def test_array_delegation
401    assert(!@table.empty?, "Table was empty.")
402
403    assert_equal(@rows.size, @table.size)
404  end
405
406  def test_inspect_shows_current_mode
407    str = @table.inspect
408    assert(str.include?("mode:#{@table.mode}"), "Mode not shown.")
409
410    @table.by_col!
411    str = @table.inspect
412    assert(str.include?("mode:#{@table.mode}"), "Mode not shown.")
413  end
414
415  def test_inspect_encoding_is_ascii_compatible
416    assert( Encoding.compatible?( Encoding.find("US-ASCII"),
417                                  @table.inspect.encoding ),
418            "inspect() was not ASCII compatible." )
419  end
420end
421