diff options
| author | Jari Vetoniemi <jari.vetoniemi@indooratlas.com> | 2020-03-16 18:49:26 +0900 | 
|---|---|---|
| committer | Jari Vetoniemi <jari.vetoniemi@indooratlas.com> | 2020-03-30 00:39:06 +0900 | 
| commit | fcbf63e62c627deae76c1b8cb8c0876c536ed811 (patch) | |
| tree | 64cb17de3f41a2b6fef2368028fbd00349946994 /jni/ruby/test/csv | |
Fresh start
Diffstat (limited to 'jni/ruby/test/csv')
| -rw-r--r-- | jni/ruby/test/csv/base.rb | 8 | ||||
| -rw-r--r-- | jni/ruby/test/csv/line_endings.gz | bin | 0 -> 59 bytes | |||
| -rwxr-xr-x | jni/ruby/test/csv/test_csv_parsing.rb | 221 | ||||
| -rwxr-xr-x | jni/ruby/test/csv/test_csv_writing.rb | 97 | ||||
| -rwxr-xr-x | jni/ruby/test/csv/test_data_converters.rb | 263 | ||||
| -rwxr-xr-x | jni/ruby/test/csv/test_encodings.rb | 337 | ||||
| -rwxr-xr-x | jni/ruby/test/csv/test_features.rb | 327 | ||||
| -rwxr-xr-x | jni/ruby/test/csv/test_headers.rb | 297 | ||||
| -rwxr-xr-x | jni/ruby/test/csv/test_interface.rb | 368 | ||||
| -rwxr-xr-x | jni/ruby/test/csv/test_row.rb | 355 | ||||
| -rwxr-xr-x | jni/ruby/test/csv/test_table.rb | 434 | ||||
| -rw-r--r-- | jni/ruby/test/csv/ts_all.rb | 20 | 
12 files changed, 2727 insertions, 0 deletions
| diff --git a/jni/ruby/test/csv/base.rb b/jni/ruby/test/csv/base.rb new file mode 100644 index 0000000..621569e --- /dev/null +++ b/jni/ruby/test/csv/base.rb @@ -0,0 +1,8 @@ +require "test/unit" + +require "csv" + +require_relative "../lib/with_different_ofs.rb" + +class TestCSV < Test::Unit::TestCase +end diff --git a/jni/ruby/test/csv/line_endings.gz b/jni/ruby/test/csv/line_endings.gzBinary files differ new file mode 100644 index 0000000..39e1729 --- /dev/null +++ b/jni/ruby/test/csv/line_endings.gz diff --git a/jni/ruby/test/csv/test_csv_parsing.rb b/jni/ruby/test/csv/test_csv_parsing.rb new file mode 100755 index 0000000..319f3f3 --- /dev/null +++ b/jni/ruby/test/csv/test_csv_parsing.rb @@ -0,0 +1,221 @@ +#!/usr/bin/env ruby -w +# encoding: UTF-8 + +# tc_csv_parsing.rb +# +#  Created by James Edward Gray II on 2005-10-31. +#  Copyright 2005 James Edward Gray II. You can redistribute or modify this code +#  under the terms of Ruby's license. + +require "timeout" + +require_relative "base" + +# +# Following tests are my interpretation of the +# {CSV RCF}[http://www.ietf.org/rfc/rfc4180.txt].  I only deviate from that +# document in one place (intentionally) and that is to make the default row +# separator <tt>$/</tt>. +# +class TestCSV::Parsing < TestCSV +  extend DifferentOFS + +  BIG_DATA = "123456789\n" * 1024 + +  def test_mastering_regex_example +    ex = %Q{Ten Thousand,10000, 2710 ,,"10,000","It's ""10 Grand"", baby",10K} +    assert_equal( [ "Ten Thousand", "10000", " 2710 ", nil, "10,000", +                    "It's \"10 Grand\", baby", "10K" ], +                  CSV.parse_line(ex) ) +  end + +  # Old Ruby 1.8 CSV library tests. +  def test_std_lib_csv +    [ ["\t", ["\t"]], +      ["foo,\"\"\"\"\"\",baz", ["foo", "\"\"", "baz"]], +      ["foo,\"\"\"bar\"\"\",baz", ["foo", "\"bar\"", "baz"]], +      ["\"\"\"\n\",\"\"\"\n\"", ["\"\n", "\"\n"]], +      ["foo,\"\r\n\",baz", ["foo", "\r\n", "baz"]], +      ["\"\"", [""]], +      ["foo,\"\"\"\",baz", ["foo", "\"", "baz"]], +      ["foo,\"\r.\n\",baz", ["foo", "\r.\n", "baz"]], +      ["foo,\"\r\",baz", ["foo", "\r", "baz"]], +      ["foo,\"\",baz", ["foo", "", "baz"]], +      ["\",\"", [","]], +      ["foo", ["foo"]], +      [",,", [nil, nil, nil]], +      [",", [nil, nil]], +      ["foo,\"\n\",baz", ["foo", "\n", "baz"]], +      ["foo,,baz", ["foo", nil, "baz"]], +      ["\"\"\"\r\",\"\"\"\r\"", ["\"\r", "\"\r"]], +      ["\",\",\",\"", [",", ","]], +      ["foo,bar,", ["foo", "bar", nil]], +      [",foo,bar", [nil, "foo", "bar"]], +      ["foo,bar", ["foo", "bar"]], +      [";", [";"]], +      ["\t,\t", ["\t", "\t"]], +      ["foo,\"\r\n\r\",baz", ["foo", "\r\n\r", "baz"]], +      ["foo,\"\r\n\n\",baz", ["foo", "\r\n\n", "baz"]], +      ["foo,\"foo,bar\",baz", ["foo", "foo,bar", "baz"]], +      [";,;", [";", ";"]] ].each do |csv_test| +      assert_equal(csv_test.last, CSV.parse_line(csv_test.first)) +    end + +    [ ["foo,\"\"\"\"\"\",baz", ["foo", "\"\"", "baz"]], +      ["foo,\"\"\"bar\"\"\",baz", ["foo", "\"bar\"", "baz"]], +      ["foo,\"\r\n\",baz", ["foo", "\r\n", "baz"]], +      ["\"\"", [""]], +      ["foo,\"\"\"\",baz", ["foo", "\"", "baz"]], +      ["foo,\"\r.\n\",baz", ["foo", "\r.\n", "baz"]], +      ["foo,\"\r\",baz", ["foo", "\r", "baz"]], +      ["foo,\"\",baz", ["foo", "", "baz"]], +      ["foo", ["foo"]], +      [",,", [nil, nil, nil]], +      [",", [nil, nil]], +      ["foo,\"\n\",baz", ["foo", "\n", "baz"]], +      ["foo,,baz", ["foo", nil, "baz"]], +      ["foo,bar", ["foo", "bar"]], +      ["foo,\"\r\n\n\",baz", ["foo", "\r\n\n", "baz"]], +      ["foo,\"foo,bar\",baz", ["foo", "foo,bar", "baz"]] ].each do |csv_test| +      assert_equal(csv_test.last, CSV.parse_line(csv_test.first)) +    end +  end + +  # From:  http://ruby-talk.org/cgi-bin/scat.rb/ruby/ruby-core/6496 +  def test_aras_edge_cases +    [ [%Q{a,b},               ["a", "b"]], +      [%Q{a,"""b"""},         ["a", "\"b\""]], +      [%Q{a,"""b"},           ["a", "\"b"]], +      [%Q{a,"b"""},           ["a", "b\""]], +      [%Q{a,"\nb"""},         ["a", "\nb\""]], +      [%Q{a,"""\nb"},         ["a", "\"\nb"]], +      [%Q{a,"""\nb\n"""},     ["a", "\"\nb\n\""]], +      [%Q{a,"""\nb\n""",\nc}, ["a", "\"\nb\n\"", nil]], +      [%Q{a,,,},              ["a", nil, nil, nil]], +      [%Q{,},                 [nil, nil]], +      [%Q{"",""},             ["", ""]], +      [%Q{""""},              ["\""]], +      [%Q{"""",""},           ["\"",""]], +      [%Q{,""},               [nil,""]], +      [%Q{,"\r"},             [nil,"\r"]], +      [%Q{"\r\n,"},           ["\r\n,"]], +      [%Q{"\r\n,",},          ["\r\n,", nil]] ].each do |edge_case| +        assert_equal(edge_case.last, CSV.parse_line(edge_case.first)) +      end +  end + +  def test_james_edge_cases +    # A read at eof? should return nil. +    assert_equal(nil, CSV.parse_line("")) +    # +    # With Ruby 1.8 CSV it's impossible to tell an empty line from a line +    # containing a single +nil+ field.  The old CSV library returns +    # <tt>[nil]</tt> in these cases, but <tt>Array.new</tt> makes more sense to +    # me. +    # +    assert_equal(Array.new, CSV.parse_line("\n1,2,3\n")) +  end + +  def test_rob_edge_cases +    [ [%Q{"a\nb"},                         ["a\nb"]], +      [%Q{"\n\n\n"},                       ["\n\n\n"]], +      [%Q{a,"b\n\nc"},                     ['a', "b\n\nc"]], +      [%Q{,"\r\n"},                        [nil,"\r\n"]], +      [%Q{,"\r\n."},                       [nil,"\r\n."]], +      [%Q{"a\na","one newline"},           ["a\na", 'one newline']], +      [%Q{"a\n\na","two newlines"},        ["a\n\na", 'two newlines']], +      [%Q{"a\r\na","one CRLF"},            ["a\r\na", 'one CRLF']], +      [%Q{"a\r\n\r\na","two CRLFs"},       ["a\r\n\r\na", 'two CRLFs']], +      [%Q{with blank,"start\n\nfinish"\n}, ['with blank', "start\n\nfinish"]], +    ].each do |edge_case| +      assert_equal(edge_case.last, CSV.parse_line(edge_case.first)) +    end +  end + +  def test_non_regex_edge_cases +    # An early version of the non-regex parser fails this test +    [ [ "foo,\"foo,bar,baz,foo\",\"foo\"", +        ["foo", "foo,bar,baz,foo", "foo"] ] ].each do |edge_case| +      assert_equal(edge_case.last, CSV.parse_line(edge_case.first)) +    end + +    assert_raise(CSV::MalformedCSVError) do +      CSV.parse_line("1,\"23\"4\"5\", 6") +    end +  end + +  def test_malformed_csv +    assert_raise(CSV::MalformedCSVError) do +      CSV.parse_line("1,2\r,3", row_sep: "\n") +    end + +    bad_data = <<-END_DATA.gsub(/^ +/, "") +    line,1,abc +    line,2,"def\nghi" + +    line,4,some\rjunk +    line,5,jkl +    END_DATA +    lines = bad_data.lines.to_a +    assert_equal(6, lines.size) +    assert_match(/\Aline,4/, lines.find { |l| l =~ /some\rjunk/ }) + +    csv = CSV.new(bad_data) +    begin +      loop do +        assert_not_nil(csv.shift) +        assert_send([csv.lineno, :<, 4]) +      end +    rescue CSV::MalformedCSVError +      assert_equal( "Unquoted fields do not allow \\r or \\n (line 4).", +                    $!.message ) +    end + +    assert_raise(CSV::MalformedCSVError) { CSV.parse_line('1,2,"3...') } + +    bad_data = <<-END_DATA.gsub(/^ +/, "") +    line,1,abc +    line,2,"def\nghi" + +    line,4,8'10" +    line,5,jkl +    END_DATA +    lines = bad_data.lines.to_a +    assert_equal(6, lines.size) +    assert_match(/\Aline,4/, lines.find { |l| l =~ /8'10"/ }) + +    csv = CSV.new(bad_data) +    begin +      loop do +        assert_not_nil(csv.shift) +        assert_send([csv.lineno, :<, 4]) +      end +    rescue CSV::MalformedCSVError +      assert_equal("Illegal quoting in line 4.", $!.message) +    end +  end + +  def test_the_parse_fails_fast_when_it_can_for_unquoted_fields +    assert_parse_errors_out('valid,fields,bad start"' + BIG_DATA) +  end + +  def test_the_parse_fails_fast_when_it_can_for_unescaped_quotes +    assert_parse_errors_out('valid,fields,"bad start"unescaped' + BIG_DATA) +  end + +  def test_field_size_limit_controls_lookahead +    assert_parse_errors_out( 'valid,fields,"' + BIG_DATA + '"', +                             field_size_limit: 2048 ) +  end + +  private + +  def assert_parse_errors_out(*args) +    assert_raise(CSV::MalformedCSVError) do +      Timeout.timeout(0.2) do +        CSV.parse(*args) +        fail("Parse didn't error out") +      end +    end +  end +end diff --git a/jni/ruby/test/csv/test_csv_writing.rb b/jni/ruby/test/csv/test_csv_writing.rb new file mode 100755 index 0000000..704c1d7 --- /dev/null +++ b/jni/ruby/test/csv/test_csv_writing.rb @@ -0,0 +1,97 @@ +#!/usr/bin/env ruby -w +# encoding: UTF-8 + +# tc_csv_writing.rb +# +#  Created by James Edward Gray II on 2005-10-31. +#  Copyright 2005 James Edward Gray II. You can redistribute or modify this code +#  under the terms of Ruby's license. + +require_relative "base" + +class TestCSV::Writing < TestCSV +  extend DifferentOFS + +  def test_writing +    [ ["\t",                      ["\t"]], +      ["foo,\"\"\"\"\"\",baz",    ["foo", "\"\"", "baz"]], +      ["foo,\"\"\"bar\"\"\",baz", ["foo", "\"bar\"", "baz"]], +      ["\"\"\"\n\",\"\"\"\n\"",   ["\"\n", "\"\n"]], +      ["foo,\"\r\n\",baz",        ["foo", "\r\n", "baz"]], +      ["\"\"",                    [""]], +      ["foo,\"\"\"\",baz",        ["foo", "\"", "baz"]], +      ["foo,\"\r.\n\",baz",       ["foo", "\r.\n", "baz"]], +      ["foo,\"\r\",baz",          ["foo", "\r", "baz"]], +      ["foo,\"\",baz",            ["foo", "", "baz"]], +      ["\",\"",                   [","]], +      ["foo",                     ["foo"]], +      [",,",                      [nil, nil, nil]], +      [",",                       [nil, nil]], +      ["foo,\"\n\",baz",          ["foo", "\n", "baz"]], +      ["foo,,baz",                ["foo", nil, "baz"]], +      ["\"\"\"\r\",\"\"\"\r\"",   ["\"\r", "\"\r"]], +      ["\",\",\",\"",             [",", ","]], +      ["foo,bar,",                ["foo", "bar", nil]], +      [",foo,bar",                [nil, "foo", "bar"]], +      ["foo,bar",                 ["foo", "bar"]], +      [";",                       [";"]], +      ["\t,\t",                   ["\t", "\t"]], +      ["foo,\"\r\n\r\",baz",      ["foo", "\r\n\r", "baz"]], +      ["foo,\"\r\n\n\",baz",      ["foo", "\r\n\n", "baz"]], +      ["foo,\"foo,bar\",baz",     ["foo", "foo,bar", "baz"]], +      [";,;",                     [";", ";"]], +      ["foo,\"\"\"\"\"\",baz",    ["foo", "\"\"", "baz"]], +      ["foo,\"\"\"bar\"\"\",baz", ["foo", "\"bar\"", "baz"]], +      ["foo,\"\r\n\",baz",        ["foo", "\r\n", "baz"]], +      ["\"\"",                    [""]], +      ["foo,\"\"\"\",baz",        ["foo", "\"", "baz"]], +      ["foo,\"\r.\n\",baz",       ["foo", "\r.\n", "baz"]], +      ["foo,\"\r\",baz",          ["foo", "\r", "baz"]], +      ["foo,\"\",baz",            ["foo", "", "baz"]], +      ["foo",                     ["foo"]], +      [",,",                      [nil, nil, nil]], +      [",",                       [nil, nil]], +      ["foo,\"\n\",baz",          ["foo", "\n", "baz"]], +      ["foo,,baz",                ["foo", nil, "baz"]], +      ["foo,bar",                 ["foo", "bar"]], +      ["foo,\"\r\n\n\",baz",      ["foo", "\r\n\n", "baz"]], +      ["foo,\"foo,bar\",baz",     ["foo", "foo,bar", "baz"]], +      [%Q{a,b},                   ["a", "b"]], +      [%Q{a,"""b"""},             ["a", "\"b\""]], +      [%Q{a,"""b"},               ["a", "\"b"]], +      [%Q{a,"b"""},               ["a", "b\""]], +      [%Q{a,"\nb"""},             ["a", "\nb\""]], +      [%Q{a,"""\nb"},             ["a", "\"\nb"]], +      [%Q{a,"""\nb\n"""},         ["a", "\"\nb\n\""]], +      [%Q{a,"""\nb\n""",},        ["a", "\"\nb\n\"", nil]], +      [%Q{a,,,},                  ["a", nil, nil, nil]], +      [%Q{,},                     [nil, nil]], +      [%Q{"",""},                 ["", ""]], +      [%Q{""""},                  ["\""]], +      [%Q{"""",""},               ["\"",""]], +      [%Q{,""},                   [nil,""]], +      [%Q{,"\r"},                 [nil,"\r"]], +      [%Q{"\r\n,"},               ["\r\n,"]], +      [%Q{"\r\n,",},              ["\r\n,", nil]] ].each do |test_case| +        assert_equal(test_case.first + $/, CSV.generate_line(test_case.last)) +      end +  end + +  def test_col_sep +    assert_equal( "a;b;;c\n", CSV.generate_line( ["a", "b", nil, "c"], +                                                 col_sep: ";" ) ) +    assert_equal( "a\tb\t\tc\n", CSV.generate_line( ["a", "b", nil, "c"], +                                                    col_sep: "\t" ) ) +  end + +  def test_row_sep +    assert_equal( "a,b,,c\r\n", CSV.generate_line( ["a", "b", nil, "c"], +                                                   row_sep: "\r\n" ) ) +  end + +  def test_force_quotes +    assert_equal( %Q{"1","b","","already ""quoted"""\n}, +                  CSV.generate_line( [1, "b", nil, %Q{already "quoted"}], +                                     force_quotes: true ) ) +  end +end diff --git a/jni/ruby/test/csv/test_data_converters.rb b/jni/ruby/test/csv/test_data_converters.rb new file mode 100755 index 0000000..89b6dd1 --- /dev/null +++ b/jni/ruby/test/csv/test_data_converters.rb @@ -0,0 +1,263 @@ +#!/usr/bin/env ruby -w +# encoding: UTF-8 + +# tc_data_converters.rb +# +#  Created by James Edward Gray II on 2005-10-31. +#  Copyright 2005 James Edward Gray II. You can redistribute or modify this code +#  under the terms of Ruby's license. + +require_relative "base" + +class TestCSV::DataConverters < TestCSV +  extend DifferentOFS + +  def setup +    super +    @data   = "Numbers,:integer,1,:float,3.015" +    @parser = CSV.new(@data) + +    @custom = lambda { |field| field =~ /\A:(\S.*?)\s*\Z/ ? $1.to_sym : field } + +    @win_safe_time_str = Time.now.strftime("%a %b %d %H:%M:%S %Y") +  end + +  def test_builtin_integer_converter +    # does convert +    [-5, 1, 10000000000].each do |n| +      assert_equal(n, CSV::Converters[:integer][n.to_s]) +    end + +    # does not convert +    (%w{junk 1.0} + [""]).each do |str| +      assert_equal(str, CSV::Converters[:integer][str]) +    end +  end + +  def test_builtin_float_converter +    # does convert +    [-5.1234, 0, 2.3e-11].each do |n| +      assert_equal(n, CSV::Converters[:float][n.to_s]) +    end + +    # does not convert +    (%w{junk 1..0 .015F} + [""]).each do |str| +      assert_equal(str, CSV::Converters[:float][str]) +    end +  end + +  def test_builtin_date_converter +    # does convert +    assert_instance_of( +      Date, +      CSV::Converters[:date][@win_safe_time_str.sub(/\d+:\d+:\d+ /, "")] +    ) + +    # does not convert +    assert_instance_of(String, CSV::Converters[:date]["junk"]) +  end + +  def test_builtin_date_time_converter +    # does convert +    assert_instance_of( DateTime, +                        CSV::Converters[:date_time][@win_safe_time_str] ) + +    # does not convert +    assert_instance_of(String, CSV::Converters[:date_time]["junk"]) +  end + +  def test_convert_with_builtin_integer +    # setup parser... +    assert_respond_to(@parser, :convert) +    assert_nothing_raised(Exception) { @parser.convert(:integer) } + +    # and use +    assert_equal(["Numbers", ":integer", 1, ":float", "3.015"], @parser.shift) +  end + +  def test_convert_with_builtin_float +    # setup parser... +    assert_respond_to(@parser, :convert) +    assert_nothing_raised(Exception) { @parser.convert(:float) } + +    # and use +    assert_equal(["Numbers", ":integer", 1.0, ":float", 3.015], @parser.shift) +  end + +  def test_convert_order_float_integer +    # floats first, then integers... +    assert_nothing_raised(Exception) do +      @parser.convert(:float) +      @parser.convert(:integer) +    end + +    # gets us nothing but floats +    assert_equal( [String, String, Float, String, Float], +                  @parser.shift.map { |field| field.class } ) +  end + +  def test_convert_order_integer_float +    # integers have precendance... +    assert_nothing_raised(Exception) do +      @parser.convert(:integer) +      @parser.convert(:float) +    end + +    # gives us proper number conversion +    assert_equal( [String, String, Fixnum, String, Float], +                  @parser.shift.map { |field| field.class } ) +  end + +  def test_builtin_numeric_combo_converter +    # setup parser... +    assert_nothing_raised(Exception) { @parser.convert(:numeric) } + +    # and use +    assert_equal( [String, String, Fixnum, String, Float], +                  @parser.shift.map { |field| field.class } ) +  end + +  def test_builtin_all_nested_combo_converter +    # setup parser... +    @data   << ",#{@win_safe_time_str}"        # add a DateTime field +    @parser =  CSV.new(@data)                  # reset parser +    assert_nothing_raised(Exception) { @parser.convert(:all) } + +    # and use +    assert_equal( [String, String, Fixnum, String, Float, DateTime], +                  @parser.shift.map { |field| field.class } ) +  end + +  def test_convert_with_custom_code +    # define custom converter... +    assert_nothing_raised(Exception) do +      @parser.convert { |field| field =~ /\A:(\S.*?)\s*\Z/ ? $1.to_sym : field } +    end + +    # and use +    assert_equal(["Numbers", :integer, "1", :float, "3.015"], @parser.shift) +  end + +  def test_convert_with_custom_code_mix +    # mix built-in and custom... +    assert_nothing_raised(Exception) { @parser.convert(:numeric) } +    assert_nothing_raised(Exception) { @parser.convert(&@custom) } + +    # and use +    assert_equal(["Numbers", :integer, 1, :float, 3.015], @parser.shift) +  end + +  def test_convert_with_custom_code_using_field_info +    # define custom converter that uses field information... +    assert_nothing_raised(Exception) do +      @parser.convert do |field, info| +        assert_equal(1, info.line) +        info.index == 4 ? Float(field).floor : field +      end +    end + +    # and use +    assert_equal(["Numbers", ":integer", "1", ":float", 3], @parser.shift) +  end + +  def test_convert_with_custom_code_using_field_info_header +    @parser = CSV.new(@data, headers: %w{one two three four five}) + +    # define custom converter that uses field header information... +    assert_nothing_raised(Exception) do +      @parser.convert do |field, info| +        info.header == "three" ? Integer(field) * 100 : field +      end +    end + +    # and use +    assert_equal( ["Numbers", ":integer", 100, ":float", "3.015"], +                  @parser.shift.fields ) +  end + +  def test_shortcut_interface +    assert_equal( ["Numbers", ":integer", 1, ":float", 3.015], +                  CSV.parse_line(@data, converters: :numeric) ) + +    assert_equal( ["Numbers", ":integer", 1, ":float", 3.015], +                  CSV.parse_line(@data, converters: [:integer, :float]) ) + +    assert_equal( ["Numbers", :integer, 1, :float, 3.015], +                  CSV.parse_line(@data, converters: [:numeric, @custom]) ) +  end + +  def test_unconverted_fields +    [ [ @data, +        ["Numbers", :integer, 1, :float, 3.015], +        %w{Numbers :integer 1 :float 3.015} ], +      ["\n", Array.new, Array.new] ].each do |test, fields, unconverted| +      row = nil +      assert_nothing_raised(Exception) do +        row = CSV.parse_line( test, +                              converters:         [:numeric, @custom], +                              unconverted_fields: true ) +      end +      assert_not_nil(row) +      assert_equal(fields, row) +      assert_respond_to(row, :unconverted_fields) +      assert_equal(unconverted, row.unconverted_fields) +    end + +    data = <<-END_CSV.gsub(/^\s+/, "") +    first,second,third +    1,2,3 +    END_CSV +    row = nil +    assert_nothing_raised(Exception) do +      row = CSV.parse_line( data, +                            converters:         :numeric, +                            unconverted_fields: true, +                            headers:            :first_row ) +    end +    assert_not_nil(row) +    assert_equal([["first", 1], ["second", 2], ["third", 3]], row.to_a) +    assert_respond_to(row, :unconverted_fields) +    assert_equal(%w{1 2 3}, row.unconverted_fields) + +    assert_nothing_raised(Exception) do +      row = CSV.parse_line( data, +                            converters:         :numeric, +                            unconverted_fields: true, +                            headers:            :first_row, +                            return_headers:     true ) +    end +    assert_not_nil(row) +    assert_equal( [%w{first first}, %w{second second}, %w{third third}], +                  row.to_a ) +    assert_respond_to(row, :unconverted_fields) +    assert_equal(%w{first second third}, row.unconverted_fields) + +    assert_nothing_raised(Exception) do +      row = CSV.parse_line( data, +                            converters:         :numeric, +                            unconverted_fields: true, +                            headers:            :first_row, +                            return_headers:     true, +                            header_converters:  :symbol ) +    end +    assert_not_nil(row) +    assert_equal( [[:first, "first"], [:second, "second"], [:third, "third"]], +                  row.to_a ) +    assert_respond_to(row, :unconverted_fields) +    assert_equal(%w{first second third}, row.unconverted_fields) + +    assert_nothing_raised(Exception) do +      row = CSV.parse_line( data, +                            converters:         :numeric, +                            unconverted_fields: true, +                            headers:            %w{my new headers}, +                            return_headers:     true, +                            header_converters:  :symbol ) +    end +    assert_not_nil(row) +    assert_equal( [[:my, "my"], [:new, "new"], [:headers, "headers"]], +                  row.to_a ) +    assert_respond_to(row, :unconverted_fields) +    assert_equal(Array.new, row.unconverted_fields) +  end +end diff --git a/jni/ruby/test/csv/test_encodings.rb b/jni/ruby/test/csv/test_encodings.rb new file mode 100755 index 0000000..dc45692 --- /dev/null +++ b/jni/ruby/test/csv/test_encodings.rb @@ -0,0 +1,337 @@ +#!/usr/bin/env ruby -w +# encoding: UTF-8 + +# tc_encodings.rb +# +#  Created by James Edward Gray II on 2008-09-13. +#  Copyright 2008 James Edward Gray II. You can redistribute or modify this code +#  under the terms of Ruby's license. + +require_relative "base" + +class TestCSV::Encodings < TestCSV +  extend DifferentOFS + +  def setup +    super +    require 'tempfile' +    @temp_csv_file = Tempfile.new(%w"test_csv. .csv") +    @temp_csv_path = @temp_csv_file.path +    @temp_csv_file.close +  end + +  def teardown +    @temp_csv_file.close! +    super +  end + +  ######################################## +  ### Hand Test Some Popular Encodings ### +  ######################################## + +  def test_parses_utf8_encoding +    assert_parses( [ %w[ one two … ], +                     %w[ 1   …   3 ], +                     %w[ …   5   6 ] ], "UTF-8" ) +  end + +  def test_parses_latin1_encoding +    assert_parses( [ %w[ one    two    Résumé ], +                     %w[ 1      Résumé 3      ], +                     %w[ Résumé 5      6      ] ], "ISO-8859-1" ) +  end + +  def test_parses_utf16be_encoding +    assert_parses( [ %w[ one two … ], +                     %w[ 1   …   3 ], +                     %w[ …   5   6 ] ], "UTF-16BE" ) +  end + +  def test_parses_shift_jis_encoding +    assert_parses( [ %w[ 一 二 三 ], +                     %w[ 四 五 六 ], +                     %w[ 七 八 九 ] ], "Shift_JIS" ) +  end + +  ########################################################### +  ### Try Simple Reading for All Non-dummy Ruby Encodings ### +  ########################################################### + +  def test_reading_with_most_encodings +    each_encoding do |encoding| +      begin +        assert_parses( [ %w[ abc def ], +                         %w[ ghi jkl ] ], encoding ) +      rescue Encoding::ConverterNotFoundError +        fail("Failed to support #{encoding.name}.") +      end +    end +  end + +  def test_regular_expression_escaping +    each_encoding do |encoding| +      begin +        assert_parses( [ %w[ abc def ], +                         %w[ ghi jkl ] ], encoding, col_sep: "|" ) +      rescue Encoding::ConverterNotFoundError +        fail("Failed to properly escape #{encoding.name}.") +      end +    end +  end + +  def test_read_with_default_encoding +    data             = "abc" +    default_external = Encoding.default_external +    each_encoding do |encoding| +      File.open(@temp_csv_path, "wb", encoding: encoding) {|f| f << data} +      begin +        no_warnings do +          Encoding.default_external = encoding +        end +        result = CSV.read(@temp_csv_path)[0][0] +      ensure +        no_warnings do +          Encoding.default_external = default_external +        end +      end +      assert_equal(encoding, result.encoding) +    end +  end + +  ####################################################################### +  ### Stress Test ASCII Compatible and Non-ASCII Compatible Encodings ### +  ####################################################################### + +  def test_auto_line_ending_detection +    # arrange data to place a \r at the end of CSV's read ahead point +    encode_for_tests([["a" * 509]], row_sep: "\r\n") do |data| +      assert_equal("\r\n".encode(data.encoding), CSV.new(data).row_sep) +    end +  end + +  def test_csv_chars_are_transcoded +    encode_for_tests([%w[abc def]]) do |data| +      %w[col_sep row_sep quote_char].each do |csv_char| +        assert_equal( "|".encode(data.encoding), +                      CSV.new(data, csv_char.to_sym => "|").send(csv_char) ) +      end +    end +  end + +  def test_parser_works_with_encoded_headers +    encode_for_tests([%w[one two three], %w[1 2 3]]) do |data| +      parsed = CSV.parse(data, headers: true) +      assert_all?(parsed.headers, "Wrong data encoding.") {|h| h.encoding == data.encoding} +      parsed.each do |row| +        assert_all?(row.fields, "Wrong data encoding.") {|f| f.encoding == data.encoding} +      end +    end +  end + +  def test_built_in_converters_transcode_to_utf_8_then_convert +    encode_for_tests([%w[one two three], %w[1 2 3]]) do |data| +      parsed = CSV.parse(data, converters: :integer) +      assert_all?(parsed[0], "Wrong data encoding.") {|f| f.encoding == data.encoding} +      assert_equal([1, 2, 3], parsed[1]) +    end +  end + +  def test_built_in_header_converters_transcode_to_utf_8_then_convert +    encode_for_tests([%w[one two three], %w[1 2 3]]) do |data| +      parsed = CSV.parse( data, headers:           true, +                                header_converters: :downcase ) +      assert_all?(parsed.headers, "Wrong data encoding.") {|h| h.encoding.name == "UTF-8"} +      assert_all?(parsed[0].fields, "Wrong data encoding.") {|f| f.encoding == data.encoding} +    end +  end + +  def test_open_allows_you_to_set_encodings +    encode_for_tests([%w[abc def]]) do |data| +      # read and write in encoding +      File.open(@temp_csv_path, "wb:#{data.encoding.name}") { |f| f << data } +      CSV.open(@temp_csv_path, "rb:#{data.encoding.name}") do |csv| +        csv.each do |row| +          assert_all?(row, "Wrong data encoding.") {|f| f.encoding == data.encoding} +        end +      end + +      # read and write with transcoding +      File.open(@temp_csv_path, "wb:UTF-32BE:#{data.encoding.name}") do |f| +        f << data +      end +      CSV.open(@temp_csv_path, "rb:UTF-32BE:#{data.encoding.name}") do |csv| +        csv.each do |row| +          assert_all?(row, "Wrong data encoding.") {|f| f.encoding == data.encoding} +        end +      end +    end +  end + +  def test_foreach_allows_you_to_set_encodings +    encode_for_tests([%w[abc def]]) do |data| +      # read and write in encoding +      File.open(@temp_csv_path, "wb", encoding: data.encoding) { |f| f << data } +      CSV.foreach(@temp_csv_path, encoding: data.encoding) do |row| +        row.each {|f| assert_equal(f.encoding, data.encoding)} +      end + +      # read and write with transcoding +      File.open(@temp_csv_path, "wb:UTF-32BE:#{data.encoding.name}") do |f| +        f << data +      end +      CSV.foreach( @temp_csv_path, +                   encoding: "UTF-32BE:#{data.encoding.name}" ) do |row| +        assert_all?(row, "Wrong data encoding.") {|f| f.encoding == data.encoding} +      end +    end +  end + +  def test_read_allows_you_to_set_encodings +    encode_for_tests([%w[abc def]]) do |data| +      # read and write in encoding +      File.open(@temp_csv_path, "wb:#{data.encoding.name}") { |f| f << data } +      rows = CSV.read(@temp_csv_path, encoding: data.encoding.name) +      assert_all?(rows.flatten, "Wrong data encoding.") {|f| f.encoding == data.encoding} + +      # read and write with transcoding +      File.open(@temp_csv_path, "wb:UTF-32BE:#{data.encoding.name}") do |f| +        f << data +      end +      rows = CSV.read( @temp_csv_path, +                       encoding: "UTF-32BE:#{data.encoding.name}" ) +      assert_all?(rows.flatten, "Wrong data encoding.") {|f| f.encoding == data.encoding} +    end +  end + +  ################################# +  ### Write CSV in any Encoding ### +  ################################# + +  def test_can_write_csv_in_any_encoding +    each_encoding do |encoding| +      # test generate_line with encoding hint +      begin +        csv = %w[abc d|ef].map { |f| f.encode(encoding) }. +          to_csv(col_sep: "|", encoding: encoding.name) +      rescue Encoding::ConverterNotFoundError +        next +      end +      assert_equal(encoding, csv.encoding) + +      # test generate_line with encoding guessing from fields +      csv = %w[abc d|ef].map { |f| f.encode(encoding) }.to_csv(col_sep: "|") +      assert_equal(encoding, csv.encoding) + +      # writing to files +      data = encode_ary([%w[abc d,ef], %w[123 456 ]], encoding) +      CSV.open(@temp_csv_path, "wb:#{encoding.name}") do |f| +        data.each { |row| f << row } +      end +      assert_equal(data, CSV.read(@temp_csv_path, encoding: encoding.name)) +    end +  end + +  def test_encoding_is_upgraded_during_writing_as_needed +    data = ["foo".force_encoding("US-ASCII"), "\u3042"] +    assert_equal("US-ASCII", data.first.encoding.name) +    assert_equal("UTF-8",    data.last.encoding.name) +    assert_equal("UTF-8",    data.join('').encoding.name) +    assert_equal("UTF-8",    data.to_csv.encoding.name) +  end + +  def test_encoding_is_upgraded_for_ascii_content_during_writing_as_needed +    data = ["foo".force_encoding("ISO-8859-1"), "\u3042"] +    assert_equal("ISO-8859-1", data.first.encoding.name) +    assert_equal("UTF-8",      data.last.encoding.name) +    assert_equal("UTF-8",      data.join('').encoding.name) +    assert_equal("UTF-8",      data.to_csv.encoding.name) +  end + +  def test_explicit_encoding +    bug9766 = '[ruby-core:62113] [Bug #9766]' +    s = CSV.generate(encoding: "Windows-31J") do |csv| +      csv << ["foo".force_encoding("ISO-8859-1"), "\u3042"] +    end +    assert_equal(["foo,\u3042\n".encode(Encoding::Windows_31J), Encoding::Windows_31J], [s, s.encoding], bug9766) +  end + +  private + +  def assert_parses(fields, encoding, options = { }) +    encoding = Encoding.find(encoding) unless encoding.is_a? Encoding +    orig_fields = fields +    fields   = encode_ary(fields, encoding) +    data = ary_to_data(fields, options) +    parsed   = CSV.parse(data, options) +    assert_equal(fields, parsed) +    parsed.flatten.each_with_index do |field, i| +      assert_equal(encoding, field.encoding, "Field[#{i + 1}] was transcoded.") +    end +    File.open(@temp_csv_path, "wb") {|f| f.print(data)} +    CSV.open(@temp_csv_path, "rb:#{encoding}", options) do |csv| +      csv.each_with_index do |row, i| +        assert_equal(fields[i], row) +      end +    end +    begin +      CSV.open(@temp_csv_path, "rb:#{encoding}:#{__ENCODING__}", options) do |csv| +        csv.each_with_index do |row, i| +          assert_equal(orig_fields[i], row) +        end +      end unless encoding == __ENCODING__ +    rescue Encoding::ConverterNotFoundError +    end +    options[:encoding] = encoding.name +    CSV.open(@temp_csv_path, options) do |csv| +      csv.each_with_index do |row, i| +        assert_equal(fields[i], row) +      end +    end +    options.delete(:encoding) +    options[:external_encoding] = encoding.name +    options[:internal_encoding] = __ENCODING__.name +    begin +      CSV.open(@temp_csv_path, options) do |csv| +        csv.each_with_index do |row, i| +          assert_equal(orig_fields[i], row) +        end +      end unless encoding == __ENCODING__ +    rescue Encoding::ConverterNotFoundError +    end +  end + +  def encode_ary(ary, encoding) +    ary.map { |row| row.map { |field| field.encode(encoding) } } +  end + +  def ary_to_data(ary, options = { }) +    encoding   = ary.flatten.first.encoding +    quote_char = (options[:quote_char] || '"').encode(encoding) +    col_sep    = (options[:col_sep]    || ",").encode(encoding) +    row_sep    = (options[:row_sep]    || "\n").encode(encoding) +    ary.map { |row| +      row.map { |field| +        [quote_char, field.encode(encoding), quote_char].join('') +      }.join(col_sep) + row_sep +    }.join('').encode(encoding) +  end + +  def encode_for_tests(data, options = { }) +    yield ary_to_data(encode_ary(data, "UTF-8"),    options) +    yield ary_to_data(encode_ary(data, "UTF-16BE"), options) +  end + +  def each_encoding +    Encoding.list.each do |encoding| +      next if encoding.dummy?  # skip "dummy" encodings +      yield encoding +    end +  end + +  def no_warnings +    old_verbose, $VERBOSE = $VERBOSE, nil +    yield +  ensure +    $VERBOSE = old_verbose +  end +end diff --git a/jni/ruby/test/csv/test_features.rb b/jni/ruby/test/csv/test_features.rb new file mode 100755 index 0000000..e314657 --- /dev/null +++ b/jni/ruby/test/csv/test_features.rb @@ -0,0 +1,327 @@ +#!/usr/bin/env ruby -w +# encoding: UTF-8 + +# tc_features.rb +# +#  Created by James Edward Gray II on 2005-10-31. +#  Copyright 2005 James Edward Gray II. You can redistribute or modify this code +#  under the terms of Ruby's license. + +begin +  require "zlib" +rescue LoadError +end + +require_relative "base" +require "tempfile" + +class TestCSV::Features < TestCSV +  extend DifferentOFS + +  TEST_CASES = [ [%Q{a,b},               ["a", "b"]], +                 [%Q{a,"""b"""},         ["a", "\"b\""]], +                 [%Q{a,"""b"},           ["a", "\"b"]], +                 [%Q{a,"b"""},           ["a", "b\""]], +                 [%Q{a,"\nb"""},         ["a", "\nb\""]], +                 [%Q{a,"""\nb"},         ["a", "\"\nb"]], +                 [%Q{a,"""\nb\n"""},     ["a", "\"\nb\n\""]], +                 [%Q{a,"""\nb\n""",\nc}, ["a", "\"\nb\n\"", nil]], +                 [%Q{a,,,},              ["a", nil, nil, nil]], +                 [%Q{,},                 [nil, nil]], +                 [%Q{"",""},             ["", ""]], +                 [%Q{""""},              ["\""]], +                 [%Q{"""",""},           ["\"",""]], +                 [%Q{,""},               [nil,""]], +                 [%Q{,"\r"},             [nil,"\r"]], +                 [%Q{"\r\n,"},           ["\r\n,"]], +                 [%Q{"\r\n,",},          ["\r\n,", nil]] ] + +  def setup +    super +    @sample_data = <<-END_DATA.gsub(/^ +/, "") +    line,1,abc +    line,2,"def\nghi" + +    line,4,jkl +    END_DATA +    @csv = CSV.new(@sample_data) +  end + +  def test_col_sep +    [";", "\t"].each do |sep| +      TEST_CASES.each do |test_case| +        assert_equal( test_case.last.map { |t| t.tr(",", sep) unless t.nil? }, +                      CSV.parse_line( test_case.first.tr(",", sep), +                                      col_sep: sep ) ) +      end +    end +    assert_equal([",,,", nil], CSV.parse_line(",,,;", col_sep: ";")) +  end + +  def test_row_sep +    assert_raise(CSV::MalformedCSVError) do +        CSV.parse_line("1,2,3\n,4,5\r\n", row_sep: "\r\n") +    end +    assert_equal( ["1", "2", "3\n", "4", "5"], +                  CSV.parse_line(%Q{1,2,"3\n",4,5\r\n}, row_sep: "\r\n")) +  end + +  def test_quote_char +    TEST_CASES.each do |test_case| +      assert_equal( test_case.last.map { |t| t.tr('"', "'") unless t.nil? }, +                    CSV.parse_line( test_case.first.tr('"', "'"), +                                    quote_char: "'" ) ) +    end +  end + +  def test_bug_8405 +    TEST_CASES.each do |test_case| +      assert_equal( test_case.last.map { |t| t.tr('"', "|") unless t.nil? }, +                    CSV.parse_line( test_case.first.tr('"', "|"), +                                    quote_char: "|" ) ) +    end +  end + +  def test_csv_char_readers +    %w[col_sep row_sep quote_char].each do |reader| +      csv = CSV.new("abc,def", reader.to_sym => "|") +      assert_equal("|", csv.send(reader)) +    end +  end + +  def test_row_sep_auto_discovery +    ["\r\n", "\n", "\r"].each do |line_end| +      data       = "1,2,3#{line_end}4,5#{line_end}" +      discovered = CSV.new(data).row_sep +      assert_equal(line_end, discovered) +    end + +    assert_equal("\n", CSV.new("\n\r\n\r").row_sep) + +    assert_equal($/, CSV.new("").row_sep) + +    assert_equal($/, CSV.new(STDERR).row_sep) +  end + +  def test_lineno +    assert_equal(5, @sample_data.lines.to_a.size) + +    4.times do |line_count| +      assert_equal(line_count, @csv.lineno) +      assert_not_nil(@csv.shift) +      assert_equal(line_count + 1, @csv.lineno) +    end +    assert_nil(@csv.shift) +  end + +  def test_readline +    test_lineno + +    @csv.rewind + +    test_lineno +  end + +  def test_unknown_options +    assert_raise_with_message(ArgumentError, /unknown/) { +      CSV.new(@sample_data, unknown: :error) +    } +  end + +  def test_skip_blanks +    assert_equal(4, @csv.to_a.size) + +    @csv  = CSV.new(@sample_data, skip_blanks: true) + +    count = 0 +    @csv.each do |row| +      count += 1 +      assert_equal("line", row.first) +    end +    assert_equal(3, count) +  end + +  def test_csv_behavior_readers +    %w[ unconverted_fields return_headers write_headers +        skip_blanks        force_quotes ].each do |behavior| +      assert_not_predicate(CSV.new("abc,def"), "#{behavior}?", "Behavior defaulted to on.") +      csv = CSV.new("abc,def", behavior.to_sym => true) +      assert_predicate(csv, "#{behavior}?", "Behavior change now registered.") +    end +  end + +  def test_converters_reader +    # no change +    assert_equal( [:integer], +                  CSV.new("abc,def", converters: [:integer]).converters ) + +    # just one +    assert_equal( [:integer], +                  CSV.new("abc,def", converters: :integer).converters ) + +    # expanded +    assert_equal( [:integer, :float], +                  CSV.new("abc,def", converters: :numeric).converters ) + +    # custom +    csv = CSV.new("abc,def", converters: [:integer, lambda {  }]) +    assert_equal(2, csv.converters.size) +    assert_equal(:integer, csv.converters.first) +    assert_instance_of(Proc, csv.converters.last) +  end + +  def test_header_converters_reader +    # no change +    hc = :header_converters +    assert_equal([:downcase], CSV.new("abc,def", hc => [:downcase]).send(hc)) + +    # just one +    assert_equal([:downcase], CSV.new("abc,def", hc => :downcase).send(hc)) + +    # custom +    csv = CSV.new("abc,def", hc => [:symbol, lambda {  }]) +    assert_equal(2, csv.send(hc).size) +    assert_equal(:symbol, csv.send(hc).first) +    assert_instance_of(Proc, csv.send(hc).last) +  end + +  # reported by Kev Jackson +  def test_failing_to_escape_col_sep_bug_fix +    assert_nothing_raised(Exception) { CSV.new(String.new, col_sep: "|") } +  end + +  # reported by Chris Roos +  def test_failing_to_reset_headers_in_rewind_bug_fix +    csv = CSV.new("forename,surname", headers: true, return_headers: true) +    csv.each {|row| assert_predicate row, :header_row?} +    csv.rewind +    csv.each {|row| assert_predicate row, :header_row?} +  end + +  # reported by Dave Burt +  def test_leading_empty_fields_with_multibyte_col_sep_bug_fix +    data = <<-END_DATA.gsub(/^\s+/, "") +    <=><=>A<=>B<=>C +    1<=>2<=>3 +    END_DATA +    parsed = CSV.parse(data, col_sep: "<=>") +    assert_equal([[nil, nil, "A", "B", "C"], ["1", "2", "3"]], parsed) +  end + +  def test_gzip_reader_bug_fix +    zipped = nil +    assert_nothing_raised(NoMethodError) do +      zipped = CSV.new( +                 Zlib::GzipReader.open( +                   File.join(File.dirname(__FILE__), "line_endings.gz") +                 ) +               ) +    end +    assert_equal("\r\n", zipped.row_sep) +  ensure +    zipped.close +  end if defined?(Zlib::GzipReader) + +  def test_gzip_writer_bug_fix +    Tempfile.create(%w"temp .gz") {|tempfile| +      tempfile.close +      file = tempfile.path +      zipped = nil +      assert_nothing_raised(NoMethodError) do +        zipped = CSV.new(Zlib::GzipWriter.open(file)) +      end +      zipped << %w[one two three] +      zipped << [1, 2, 3] +      zipped.close + +      assert_include(Zlib::GzipReader.open(file) {|f| f.read}, +                     $INPUT_RECORD_SEPARATOR, "@row_sep did not default") +    } +  end if defined?(Zlib::GzipWriter) + +  def test_inspect_is_smart_about_io_types +    str = CSV.new("string,data").inspect +    assert_include(str, "io_type:StringIO", "IO type not detected.") + +    str = CSV.new($stderr).inspect +    assert_include(str, "io_type:$stderr", "IO type not detected.") + +    Tempfile.create(%w"temp .csv") {|tempfile| +      tempfile.close +      path = tempfile.path +      File.open(path, "w") { |csv| csv << "one,two,three\n1,2,3\n" } +      str  = CSV.open(path) { |csv| csv.inspect } +      assert_include(str, "io_type:File", "IO type not detected.") +    } +  end + +  def test_inspect_shows_key_attributes +    str = @csv.inspect +    %w[lineno col_sep row_sep quote_char].each do |attr_name| +      assert_match(/\b#{attr_name}:[^\s>]+/, str) +    end +  end + +  def test_inspect_shows_headers_when_available +    CSV.new("one,two,three\n1,2,3\n", headers: true) do |csv| +      assert_include(csv.inspect, "headers:true", "Header hint not shown.") +      csv.shift  # load headers +      assert_match(/headers:\[[^\]]+\]/, csv.inspect) +    end +  end + +  def test_inspect_encoding_is_ascii_compatible +    CSV.new("one,two,three\n1,2,3\n".encode("UTF-16BE")) do |csv| +      assert_send([Encoding, :compatible?, +                   Encoding.find("US-ASCII"), csv.inspect.encoding], +                  "inspect() was not ASCII compatible.") +    end +  end + +  def test_version +    assert_not_nil(CSV::VERSION) +    assert_instance_of(String, CSV::VERSION) +    assert_predicate(CSV::VERSION, :frozen?) +    assert_match(/\A\d\.\d\.\d\Z/, CSV::VERSION) +  end + +  def test_accepts_comment_skip_lines_option +    assert_nothing_raised(ArgumentError) do +      CSV.new(@sample_data, :skip_lines => /\A\s*#/) +    end +  end + +  def test_accepts_comment_defaults_to_nil +    c = CSV.new(@sample_data) +    assert_nil(c.skip_lines) +  end + +  class RegexStub +  end + +  def test_requires_skip_lines_to_call_match +    regex_stub = RegexStub.new +    assert_raise_with_message(ArgumentError, /skip_lines/) do +      CSV.new(@sample_data, :skip_lines => regex_stub) +    end +  end + +  def test_comment_rows_are_ignored +    sample_data = "line,1,a\n#not,a,line\nline,2,b\n   #also,no,line" +    c = CSV.new sample_data, :skip_lines => /\A\s*#/ +    assert_equal [["line", "1", "a"], ["line", "2", "b"]], c.each.to_a +  end + +  def test_quoted_skip_line_markers_are_ignored +    sample_data = "line,1,a\n\"#not\",a,line\nline,2,b" +    c = CSV.new sample_data, :skip_lines => /\A\s*#/ +    assert_equal [["line", "1", "a"], ["#not", "a", "line"], ["line", "2", "b"]], c.each.to_a +  end + +  def test_string_works_like_a_regexp +    sample_data = "line,1,a\n#(not,a,line\nline,2,b\n   also,#no,line" +    c = CSV.new sample_data, :skip_lines => "#" +    assert_equal [["line", "1", "a"], ["line", "2", "b"]], c.each.to_a +  end + +end diff --git a/jni/ruby/test/csv/test_headers.rb b/jni/ruby/test/csv/test_headers.rb new file mode 100755 index 0000000..79ccd20 --- /dev/null +++ b/jni/ruby/test/csv/test_headers.rb @@ -0,0 +1,297 @@ +#!/usr/bin/env ruby -w +# encoding: UTF-8 + +# tc_headers.rb +# +#  Created by James Edward Gray II on 2005-10-31. +#  Copyright 2005 James Edward Gray II. You can redistribute or modify this code +#  under the terms of Ruby's license. + +require_relative "base" + +class TestCSV::Headers < TestCSV +  extend DifferentOFS + +  def setup +    super +    @data = <<-END_CSV.gsub(/^\s+/, "") +    first,second,third +    A,B,C +    1,2,3 +    END_CSV +  end + +  def test_first_row +    [:first_row, true].each do |setting|  # two names for the same setting +      # activate headers +      csv = nil +      assert_nothing_raised(Exception) do +        csv = CSV.parse(@data, headers: setting) +      end + +      # first data row - skipping headers +      row = csv[0] +      assert_not_nil(row) +      assert_instance_of(CSV::Row, row) +      assert_equal([%w{first A}, %w{second B}, %w{third C}], row.to_a) + +      # second data row +      row = csv[1] +      assert_not_nil(row) +      assert_instance_of(CSV::Row, row) +      assert_equal([%w{first 1}, %w{second 2}, %w{third 3}], row.to_a) + +      # empty +      assert_nil(csv[2]) +    end +  end + +  def test_array_of_headers +    # activate headers +    csv = nil +    assert_nothing_raised(Exception) do +      csv = CSV.parse(@data, headers: [:my, :new, :headers]) +    end + +    # first data row - skipping headers +    row = csv[0] +    assert_not_nil(row) +    assert_instance_of(CSV::Row, row) +    assert_equal( [[:my, "first"], [:new, "second"], [:headers, "third"]], +                  row.to_a ) + +    # second data row +    row = csv[1] +    assert_not_nil(row) +    assert_instance_of(CSV::Row, row) +    assert_equal([[:my, "A"], [:new, "B"], [:headers, "C"]], row.to_a) + +    # third data row +    row = csv[2] +    assert_not_nil(row) +    assert_instance_of(CSV::Row, row) +    assert_equal([[:my, "1"], [:new, "2"], [:headers, "3"]], row.to_a) + +    # empty +    assert_nil(csv[3]) + +    # with return and convert +    assert_nothing_raised(Exception) do +      csv = CSV.parse( @data, headers:           [:my, :new, :headers], +                              return_headers:    true, +                              header_converters: lambda { |h| h.to_s } ) +    end +    row = csv[0] +    assert_not_nil(row) +    assert_instance_of(CSV::Row, row) +    assert_equal([["my", :my], ["new", :new], ["headers", :headers]], row.to_a) +    assert_predicate(row, :header_row?) +    assert_not_predicate(row, :field_row?) +  end + +  def test_csv_header_string +    # activate headers +    csv = nil +    assert_nothing_raised(Exception) do +      csv = CSV.parse(@data, headers: "my,new,headers") +    end + +    # first data row - skipping headers +    row = csv[0] +    assert_not_nil(row) +    assert_instance_of(CSV::Row, row) +    assert_equal([%w{my first}, %w{new second}, %w{headers third}], row.to_a) + +    # second data row +    row = csv[1] +    assert_not_nil(row) +    assert_instance_of(CSV::Row, row) +    assert_equal([%w{my A}, %w{new B}, %w{headers C}], row.to_a) + +    # third data row +    row = csv[2] +    assert_not_nil(row) +    assert_instance_of(CSV::Row, row) +    assert_equal([%w{my 1}, %w{new 2}, %w{headers 3}], row.to_a) + +    # empty +    assert_nil(csv[3]) + +    # with return and convert +    assert_nothing_raised(Exception) do +      csv = CSV.parse( @data, headers:           "my,new,headers", +                              return_headers:    true, +                              header_converters: :symbol ) +    end +    row = csv[0] +    assert_not_nil(row) +    assert_instance_of(CSV::Row, row) +    assert_equal([[:my, "my"], [:new, "new"], [:headers, "headers"]], row.to_a) +    assert_predicate(row, :header_row?) +    assert_not_predicate(row, :field_row?) +  end + +  def test_csv_header_string_inherits_separators +    # parse with custom col_sep +    csv = nil +    assert_nothing_raised(Exception) do +      csv = CSV.parse( @data.tr(",", "|"), col_sep: "|", +                                           headers: "my|new|headers" ) +    end + +    # verify headers were recognized +    row = csv[0] +    assert_not_nil(row) +    assert_instance_of(CSV::Row, row) +    assert_equal([%w{my first}, %w{new second}, %w{headers third}], row.to_a) +  end + +  def test_return_headers +    # activate headers and request they are returned +    csv = nil +    assert_nothing_raised(Exception) do +      csv = CSV.parse(@data, headers: true, return_headers: true) +    end + +    # header row +    row = csv[0] +    assert_not_nil(row) +    assert_instance_of(CSV::Row, row) +    assert_equal( [%w{first first}, %w{second second}, %w{third third}], +                  row.to_a ) +    assert_predicate(row, :header_row?) +    assert_not_predicate(row, :field_row?) + +    # first data row - skipping headers +    row = csv[1] +    assert_not_nil(row) +    assert_instance_of(CSV::Row, row) +    assert_equal([%w{first A}, %w{second B}, %w{third C}], row.to_a) +    assert_not_predicate(row, :header_row?) +    assert_predicate(row, :field_row?) + +    # second data row +    row = csv[2] +    assert_not_nil(row) +    assert_instance_of(CSV::Row, row) +    assert_equal([%w{first 1}, %w{second 2}, %w{third 3}], row.to_a) +    assert_not_predicate(row, :header_row?) +    assert_predicate(row, :field_row?) + +    # empty +    assert_nil(csv[3]) +  end + +  def test_converters +    # create test data where headers and fields look alike +    data = <<-END_MATCHING_CSV.gsub(/^\s+/, "") +    1,2,3 +    1,2,3 +    END_MATCHING_CSV + +    # normal converters do not affect headers +    csv = CSV.parse( data, headers:        true, +                           return_headers: true, +                           converters:     :numeric ) +    assert_equal([%w{1 1}, %w{2 2}, %w{3 3}], csv[0].to_a) +    assert_equal([["1", 1], ["2", 2], ["3", 3]], csv[1].to_a) +    assert_nil(csv[2]) + +    # header converters do affect headers (only) +    assert_nothing_raised(Exception) do +      csv = CSV.parse( data, headers:           true, +                             return_headers:    true, +                             converters:        :numeric, +                             header_converters: :symbol ) +    end +    assert_equal([[:"1", "1"], [:"2", "2"], [:"3", "3"]], csv[0].to_a) +    assert_equal([[:"1", 1], [:"2", 2], [:"3", 3]], csv[1].to_a) +    assert_nil(csv[2]) +  end + +  def test_builtin_downcase_converter +    csv = CSV.parse( "One,TWO Three", headers:           true, +                                      return_headers:    true, +                                      header_converters: :downcase ) +    assert_equal(%w{one two\ three}, csv.headers) +  end + +  def test_builtin_symbol_converter +    # Note that the trailing space is intentional +    csv = CSV.parse( "One,TWO Three ", headers:           true, +                                       return_headers:    true, +                                       header_converters: :symbol ) +    assert_equal([:one, :two_three], csv.headers) +  end + +  def test_builtin_converters_with_blank_header +    csv = CSV.parse( "one,,three", headers:           true, +                                   return_headers:    true, +                                   header_converters: [:downcase, :symbol] ) +    assert_equal([:one, nil, :three], csv.headers) +  end + +  def test_custom_converter +    converter = lambda { |header| header.tr(" ", "_") } +    csv       = CSV.parse( "One,TWO Three", +                           headers:           true, +                           return_headers:    true, +                           header_converters: converter ) +    assert_equal(%w{One TWO_Three}, csv.headers) +  end + +  def test_table_support +    csv = nil +    assert_nothing_raised(Exception) do +      csv = CSV.parse(@data, headers: true) +    end + +    assert_instance_of(CSV::Table, csv) +  end + +  def test_skip_blanks +    @data = <<-END_CSV.gsub(/^ +/, "") + + +    A,B,C + +    1,2,3 + + + +    END_CSV + +    expected = [%w[1 2 3]] +    CSV.parse(@data, headers: true, skip_blanks: true) do |row| +      assert_equal(expected.shift, row.fields) +    end + +    expected = [%w[A B C], %w[1 2 3]] +    CSV.parse( @data, +               headers:        true, +               return_headers: true, +               skip_blanks:    true ) do |row| +      assert_equal(expected.shift, row.fields) +    end +  end + +  def test_headers_reader +    # no headers +    assert_nil(CSV.new(@data).headers) + +    # headers +    csv = CSV.new(@data, headers: true) +    assert_equal(true, csv.headers)                    # before headers are read +    csv.shift                                          # set headers +    assert_equal(%w[first second third], csv.headers)  # after headers are read +  end + +  def test_blank_row_bug_fix +    @data += "\n#{@data}"  # add a blank row + +    # ensure that everything returned is a Row object +    CSV.parse(@data, headers: true) do |row| +      assert_instance_of(CSV::Row, row) +    end +  end +end diff --git a/jni/ruby/test/csv/test_interface.rb b/jni/ruby/test/csv/test_interface.rb new file mode 100755 index 0000000..d6bf470 --- /dev/null +++ b/jni/ruby/test/csv/test_interface.rb @@ -0,0 +1,368 @@ +#!/usr/bin/env ruby -w +# encoding: UTF-8 + +# tc_interface.rb +# +#  Created by James Edward Gray II on 2005-10-31. +#  Copyright 2005 James Edward Gray II. You can redistribute or modify this code +#  under the terms of Ruby's license. + +require_relative "base" +require "tempfile" + +class TestCSV::Interface < TestCSV +  extend DifferentOFS + +  def setup +    super +    @tempfile = Tempfile.new(%w"temp .csv") +    @tempfile.close +    @path = @tempfile.path + +    File.open(@path, "wb") do |file| +      file << "1\t2\t3\r\n" +      file << "4\t5\r\n" +    end + +    @expected = [%w{1 2 3}, %w{4 5}] +  end + +  def teardown +    @tempfile.close(true) +    super +  end + +  ### Test Read Interface ### + +  def test_foreach +    CSV.foreach(@path, col_sep: "\t", row_sep: "\r\n") do |row| +      assert_equal(@expected.shift, row) +    end +  end + +  def test_foreach_enum +    CSV.foreach(@path, col_sep: "\t", row_sep: "\r\n").zip(@expected) do |row, exp| +      assert_equal(exp, row) +    end +  end + +  def test_open_and_close +    csv = CSV.open(@path, "r+", col_sep: "\t", row_sep: "\r\n") +    assert_not_nil(csv) +    assert_instance_of(CSV, csv) +    assert_not_predicate(csv, :closed?) +    csv.close +    assert_predicate(csv, :closed?) + +    ret = CSV.open(@path) do |new_csv| +      csv = new_csv +      assert_instance_of(CSV, new_csv) +      "Return value." +    end +    assert_predicate(csv, :closed?) +    assert_equal("Return value.", ret) +  end + +  def test_parse +    data = File.binread(@path) +    assert_equal( @expected, +                  CSV.parse(data, col_sep: "\t", row_sep: "\r\n") ) + +    CSV.parse(data, col_sep: "\t", row_sep: "\r\n") do |row| +      assert_equal(@expected.shift, row) +    end +  end + +  def test_parse_line +    row = CSV.parse_line("1;2;3", col_sep: ";") +    assert_not_nil(row) +    assert_instance_of(Array, row) +    assert_equal(%w{1 2 3}, row) + +    # shortcut interface +    row = "1;2;3".parse_csv(col_sep: ";") +    assert_not_nil(row) +    assert_instance_of(Array, row) +    assert_equal(%w{1 2 3}, row) +  end + +  def test_parse_line_with_empty_lines +    assert_equal(nil,       CSV.parse_line(""))  # to signal eof +    assert_equal(Array.new, CSV.parse_line("\n1,2,3")) +  end + +  def test_read_and_readlines +    assert_equal( @expected, +                  CSV.read(@path, col_sep: "\t", row_sep: "\r\n") ) +    assert_equal( @expected, +                  CSV.readlines(@path, col_sep: "\t", row_sep: "\r\n") ) + + +    data = CSV.open(@path, col_sep: "\t", row_sep: "\r\n") do |csv| +      csv.read +    end +    assert_equal(@expected, data) +    data = CSV.open(@path, col_sep: "\t", row_sep: "\r\n") do |csv| +      csv.readlines +    end +    assert_equal(@expected, data) +  end + +  def test_table +    table = CSV.table(@path, col_sep: "\t", row_sep: "\r\n") +    assert_instance_of(CSV::Table, table) +    assert_equal([[:"1", :"2", :"3"], [4, 5, nil]], table.to_a) +  end + +  def test_shift  # aliased as gets() and readline() +    CSV.open(@path, "rb+", col_sep: "\t", row_sep: "\r\n") do |csv| +      assert_equal(@expected.shift, csv.shift) +      assert_equal(@expected.shift, csv.shift) +      assert_equal(nil, csv.shift) +    end +  end + +  def test_enumerators_are_supported +    CSV.open(@path, col_sep: "\t", row_sep: "\r\n") do |csv| +      enum = csv.each +      assert_instance_of(Enumerator, enum) +      assert_equal(@expected.shift, enum.next) +    end +  end + +  def test_nil_is_not_acceptable +    assert_raise_with_message ArgumentError, "Cannot parse nil as CSV" do +      CSV.new(nil) +    end +  end + +  ### Test Write Interface ### + +  def test_generate +    str = CSV.generate do |csv|  # default empty String +      assert_instance_of(CSV, csv) +      assert_equal(csv, csv << [1, 2, 3]) +      assert_equal(csv, csv << [4, nil, 5]) +    end +    assert_not_nil(str) +    assert_instance_of(String, str) +    assert_equal("1,2,3\n4,,5\n", str) + +    CSV.generate(str) do |csv|   # appending to a String +      assert_equal(csv, csv << ["last", %Q{"row"}]) +    end +    assert_equal(%Q{1,2,3\n4,,5\nlast,"""row"""\n}, str) +  end + +  def test_generate_line +    line = CSV.generate_line(%w{1 2 3}, col_sep: ";") +    assert_not_nil(line) +    assert_instance_of(String, line) +    assert_equal("1;2;3\n", line) + +    # shortcut interface +    line = %w{1 2 3}.to_csv(col_sep: ";") +    assert_not_nil(line) +    assert_instance_of(String, line) +    assert_equal("1;2;3\n", line) +  end + +  def test_write_header_detection +    File.unlink(@path) + +    headers = %w{a b c} +    CSV.open(@path, "w", headers: true) do |csv| +      csv << headers +      csv << %w{1 2 3} +      assert_equal(headers, csv.instance_variable_get(:@headers)) +    end +  end + +  def test_write_lineno +    File.unlink(@path) + +    CSV.open(@path, "w") do |csv| +      lines = 20 +      lines.times { csv << %w{a b c} } +      assert_equal(lines, csv.lineno) +    end +  end + +  def test_write_hash +    File.unlink(@path) + +    lines = [{a: 1, b: 2, c: 3}, {a: 4, b: 5, c: 6}] +    CSV.open( @path, "wb", headers:           true, +                           header_converters: :symbol ) do |csv| +      csv << lines.first.keys +      lines.each { |line| csv << line } +    end +    CSV.open( @path, "rb", headers:           true, +                           converters:        :all, +                           header_converters: :symbol ) do |csv| +      csv.each { |line| assert_equal(lines.shift, line.to_hash) } +    end +  end + +  def test_write_hash_with_string_keys +    File.unlink(@path) + +    lines = [{a: 1, b: 2, c: 3}, {a: 4, b: 5, c: 6}] +    CSV.open( @path, "wb", headers: true ) do |csv| +      csv << lines.first.keys +      lines.each { |line| csv << line } +    end +    CSV.open( @path, "rb", headers: true ) do |csv| +      csv.each do |line| +        csv.headers.each_with_index do |header, h| +          keys = line.to_hash.keys +          assert_instance_of(String, keys[h]) +          assert_same(header, keys[h]) +        end +      end +    end +  end + +  def test_write_hash_with_headers_array +    File.unlink(@path) + +    lines = [{a: 1, b: 2, c: 3}, {a: 4, b: 5, c: 6}] +    CSV.open(@path, "wb", headers: [:b, :a, :c]) do |csv| +      lines.each { |line| csv << line } +    end + +    # test writing fields in the correct order +    File.open(@path, "rb") do |f| +      assert_equal("2,1,3", f.gets.strip) +      assert_equal("5,4,6", f.gets.strip) +    end + +    # test reading CSV with headers +    CSV.open( @path, "rb", headers:    [:b, :a, :c], +                           converters: :all ) do |csv| +      csv.each { |line| assert_equal(lines.shift, line.to_hash) } +    end +  end + +  def test_write_hash_with_headers_string +    File.unlink(@path) + +    lines = [{"a" => 1, "b" => 2, "c" => 3}, {"a" => 4, "b" => 5, "c" => 6}] +    CSV.open(@path, "wb", headers: "b|a|c", col_sep: "|") do |csv| +      lines.each { |line| csv << line } +    end + +    # test writing fields in the correct order +    File.open(@path, "rb") do |f| +      assert_equal("2|1|3", f.gets.strip) +      assert_equal("5|4|6", f.gets.strip) +    end + +    # test reading CSV with headers +    CSV.open( @path, "rb", headers:    "b|a|c", +                           col_sep:    "|", +                           converters: :all ) do |csv| +      csv.each { |line| assert_equal(lines.shift, line.to_hash) } +    end +  end + +  def test_write_headers +    File.unlink(@path) + +    lines = [{"a" => 1, "b" => 2, "c" => 3}, {"a" => 4, "b" => 5, "c" => 6}] +    CSV.open( @path, "wb", headers:       "b|a|c", +                           write_headers: true, +                           col_sep:       "|" ) do |csv| +      lines.each { |line| csv << line } +    end + +    # test writing fields in the correct order +    File.open(@path, "rb") do |f| +      assert_equal("b|a|c", f.gets.strip) +      assert_equal("2|1|3", f.gets.strip) +      assert_equal("5|4|6", f.gets.strip) +    end + +    # test reading CSV with headers +    CSV.open( @path, "rb", headers:    true, +                           col_sep:    "|", +                           converters: :all ) do |csv| +      csv.each { |line| assert_equal(lines.shift, line.to_hash) } +    end +  end + +  def test_append  # aliased add_row() and puts() +    File.unlink(@path) + +    CSV.open(@path, "wb", col_sep: "\t", row_sep: "\r\n") do |csv| +      @expected.each { |row| csv << row } +    end + +    test_shift + +    # same thing using CSV::Row objects +    File.unlink(@path) + +    CSV.open(@path, "wb", col_sep: "\t", row_sep: "\r\n") do |csv| +      @expected.each { |row| csv << CSV::Row.new(Array.new, row) } +    end + +    test_shift +  end + +  ### Test Read and Write Interface ### + +  def test_filter +    assert_respond_to(CSV, :filter) + +    expected = [[1, 2, 3], [4, 5]] +    CSV.filter( "1;2;3\n4;5\n", (result = String.new), +                in_col_sep: ";", out_col_sep: ",", +                converters: :all ) do |row| +      assert_equal(row, expected.shift) +      row.map! { |n| n * 2 } +      row << "Added\r" +    end +    assert_equal("2,4,6,\"Added\r\"\n8,10,\"Added\r\"\n", result) +  end + +  def test_instance +    csv = String.new + +    first = nil +    assert_nothing_raised(Exception) do +      first =  CSV.instance(csv, col_sep: ";") +      first << %w{a b c} +    end + +    assert_equal("a;b;c\n", csv) + +    second = nil +    assert_nothing_raised(Exception) do +      second =  CSV.instance(csv, col_sep: ";") +      second << [1, 2, 3] +    end + +    assert_equal(first.object_id, second.object_id) +    assert_equal("a;b;c\n1;2;3\n", csv) + +    # shortcuts +    assert_equal(STDOUT, CSV.instance.instance_eval { @io }) +    assert_equal(STDOUT, CSV { |new_csv| new_csv.instance_eval { @io } }) +  end + +  def test_options_are_not_modified +    opt = {}.freeze +    assert_nothing_raised {  CSV.foreach(@path, opt)       } +    assert_nothing_raised {  CSV.open(@path, opt){}        } +    assert_nothing_raised {  CSV.parse("", opt)            } +    assert_nothing_raised {  CSV.parse_line("", opt)       } +    assert_nothing_raised {  CSV.read(@path, opt)          } +    assert_nothing_raised {  CSV.readlines(@path, opt)     } +    assert_nothing_raised {  CSV.table(@path, opt)         } +    assert_nothing_raised {  CSV.generate(opt){}           } +    assert_nothing_raised {  CSV.generate_line([], opt)    } +    assert_nothing_raised {  CSV.filter("", "", opt){}     } +    assert_nothing_raised {  CSV.instance("", opt)         } +  end +end diff --git a/jni/ruby/test/csv/test_row.rb b/jni/ruby/test/csv/test_row.rb new file mode 100755 index 0000000..3acceea --- /dev/null +++ b/jni/ruby/test/csv/test_row.rb @@ -0,0 +1,355 @@ +#!/usr/bin/env ruby -w +# encoding: UTF-8 + +# tc_row.rb +# +#  Created by James Edward Gray II on 2005-10-31. +#  Copyright 2005 James Edward Gray II. You can redistribute or modify this code +#  under the terms of Ruby's license. + +require_relative "base" + +class TestCSV::Row < TestCSV +  extend DifferentOFS + +  def setup +    super +    @row = CSV::Row.new(%w{A B C A A}, [1, 2, 3, 4]) +  end + +  def test_initialize +    # basic +    row = CSV::Row.new(%w{A B C}, [1, 2, 3]) +    assert_not_nil(row) +    assert_instance_of(CSV::Row, row) +    assert_equal([["A", 1], ["B", 2], ["C", 3]], row.to_a) + +    # missing headers +    row = CSV::Row.new(%w{A}, [1, 2, 3]) +    assert_not_nil(row) +    assert_instance_of(CSV::Row, row) +    assert_equal([["A", 1], [nil, 2], [nil, 3]], row.to_a) + +    # missing fields +    row = CSV::Row.new(%w{A B C}, [1, 2]) +    assert_not_nil(row) +    assert_instance_of(CSV::Row, row) +    assert_equal([["A", 1], ["B", 2], ["C", nil]], row.to_a) +  end + +  def test_row_type +    # field rows +    row = CSV::Row.new(%w{A B C}, [1, 2, 3])         # implicit +    assert_not_predicate(row, :header_row?) +    assert_predicate(row, :field_row?) +    row = CSV::Row.new(%w{A B C}, [1, 2, 3], false)  # explicit +    assert_not_predicate(row, :header_row?) +    assert_predicate(row, :field_row?) + +    # header row +    row = CSV::Row.new(%w{A B C}, [1, 2, 3], true) +    assert_predicate(row, :header_row?) +    assert_not_predicate(row, :field_row?) +  end + +  def test_headers +    assert_equal(%w{A B C A A}, @row.headers) +  end + +  def test_field +    # by name +    assert_equal(2, @row.field("B")) +    assert_equal(2, @row["B"])  # alias + +    # by index +    assert_equal(3, @row.field(2)) + +    # missing +    assert_nil(@row.field("Missing")) +    assert_nil(@row.field(10)) + +    # minimum index +    assert_equal(1, @row.field("A")) +    assert_equal(1, @row.field("A", 0)) +    assert_equal(4, @row.field("A", 1)) +    assert_equal(4, @row.field("A", 2)) +    assert_equal(4, @row.field("A", 3)) +    assert_equal(nil, @row.field("A", 4)) +    assert_equal(nil, @row.field("A", 5)) +  end + +  def test_fetch +    # only by name +    assert_equal(2, @row.fetch('B')) + +    # missing header raises KeyError +    assert_raise KeyError do +      @row.fetch('foo') +    end + +    # missing header yields itself to block +    assert_equal 'bar', @row.fetch('foo') { |header| +      header == 'foo' ? 'bar' : false } + +    # missing header returns the given default value +    assert_equal 'bar', @row.fetch('foo', 'bar') + +    # more than one vararg raises ArgumentError +    assert_raise ArgumentError do +      @row.fetch('foo', 'bar', 'baz') +    end +  end + +  def test_has_key? +    assert_equal(true, @row.has_key?('B')) +    assert_equal(false, @row.has_key?('foo')) +  end + +  def test_set_field +    # set field by name +    assert_equal(100, @row["A"] = 100) + +    # set field by index +    assert_equal(300, @row[3] = 300) + +    # set field by name and minimum index +    assert_equal([:a, :b, :c], @row["A", 4] = [:a, :b, :c]) + +    # verify the changes +    assert_equal( [ ["A", 100], +                    ["B", 2], +                    ["C", 3], +                    ["A", 300], +                    ["A", [:a, :b, :c]] ], @row.to_a ) + +    # assigning an index past the end +    assert_equal("End", @row[10] = "End") +    assert_equal( [ ["A", 100], +                    ["B", 2], +                    ["C", 3], +                    ["A", 300], +                    ["A", [:a, :b, :c]], +                    [nil, nil], +                    [nil, nil], +                    [nil, nil], +                    [nil, nil], +                    [nil, nil], +                    [nil, "End"] ], @row.to_a ) + +    # assigning a new field by header +    assert_equal("New", @row[:new] = "New") +    assert_equal( [ ["A", 100], +                    ["B", 2], +                    ["C", 3], +                    ["A", 300], +                    ["A", [:a, :b, :c]], +                    [nil, nil], +                    [nil, nil], +                    [nil, nil], +                    [nil, nil], +                    [nil, nil], +                    [nil, "End"], +                    [:new, "New"] ], @row.to_a ) +  end + +  def test_append +    # add a value +    assert_equal(@row, @row << "Value") +    assert_equal( [ ["A", 1], +                    ["B", 2], +                    ["C", 3], +                    ["A", 4], +                    ["A", nil], +                    [nil, "Value"] ], @row.to_a ) + +    # add a pair +    assert_equal(@row, @row << %w{Header Field}) +    assert_equal( [ ["A", 1], +                    ["B", 2], +                    ["C", 3], +                    ["A", 4], +                    ["A", nil], +                    [nil, "Value"], +                    %w{Header Field} ], @row.to_a ) + +    # a pair with Hash syntax +    assert_equal(@row, @row << {key: :value}) +    assert_equal( [ ["A", 1], +                    ["B", 2], +                    ["C", 3], +                    ["A", 4], +                    ["A", nil], +                    [nil, "Value"], +                    %w{Header Field}, +                    [:key, :value] ], @row.to_a ) + +    # multiple fields at once +    assert_equal(@row, @row.push(100, 200, [:last, 300])) +    assert_equal( [ ["A", 1], +                    ["B", 2], +                    ["C", 3], +                    ["A", 4], +                    ["A", nil], +                    [nil, "Value"], +                    %w{Header Field}, +                    [:key, :value], +                    [nil, 100], +                    [nil, 200], +                    [:last, 300] ], @row.to_a ) +  end + +  def test_delete +    # by index +    assert_equal(["B", 2], @row.delete(1)) + +    # by header +    assert_equal(["C", 3], @row.delete("C")) + +    # using a block +    assert_equal(@row, @row.delete_if { |h, f| h == "A" and not f.nil? }) +    assert_equal([["A", nil]], @row.to_a) +  end + +  def test_fields +    # all fields +    assert_equal([1, 2, 3, 4, nil], @row.fields) + +    # by header +    assert_equal([1, 3], @row.fields("A", "C")) + +    # by index +    assert_equal([2, 3, nil], @row.fields(1, 2, 10)) + +    # by both +    assert_equal([2, 3, 4], @row.fields("B", "C", 3)) + +    # with minimum indices +    assert_equal([2, 3, 4], @row.fields("B", "C", ["A", 3])) + +    # by header range +    assert_equal([2, 3], @row.values_at("B".."C")) +  end + +  def test_index +    # basic usage +    assert_equal(0, @row.index("A")) +    assert_equal(1, @row.index("B")) +    assert_equal(2, @row.index("C")) +    assert_equal(nil, @row.index("Z")) + +    # with minimum index +    assert_equal(0, @row.index("A")) +    assert_equal(0, @row.index("A", 0)) +    assert_equal(3, @row.index("A", 1)) +    assert_equal(3, @row.index("A", 2)) +    assert_equal(3, @row.index("A", 3)) +    assert_equal(4, @row.index("A", 4)) +    assert_equal(nil, @row.index("A", 5)) +  end + +  def test_queries +    # headers +    assert_send([@row, :header?, "A"]) +    assert_send([@row, :header?, "C"]) +    assert_not_send([@row, :header?, "Z"]) +    assert_send([@row, :include?, "A"])  # alias + +    # fields +    assert(@row.field?(4)) +    assert(@row.field?(nil)) +    assert(!@row.field?(10)) +  end + +  def test_each +    # array style +    ary = @row.to_a +    @row.each do |pair| +      assert_equal(ary.first.first, pair.first) +      assert_equal(ary.shift.last, pair.last) +    end + +    # hash style +    ary = @row.to_a +    @row.each do |header, field| +      assert_equal(ary.first.first, header) +      assert_equal(ary.shift.last, field) +    end + +    # verify that we can chain the call +    assert_equal(@row, @row.each { }) +  end + +  def test_enumerable +    assert_equal( [["A", 1], ["A", 4], ["A", nil]], +                  @row.select { |pair| pair.first == "A" } ) + +    assert_equal(10, @row.inject(0) { |sum, (_, n)| sum + (n || 0) }) +  end + +  def test_to_a +    row = CSV::Row.new(%w{A B C}, [1, 2, 3]).to_a +    assert_instance_of(Array, row) +    row.each do |pair| +      assert_instance_of(Array, pair) +      assert_equal(2, pair.size) +    end +    assert_equal([["A", 1], ["B", 2], ["C", 3]], row) +  end + +  def test_to_hash +    hash = @row.to_hash +    assert_equal({"A" => nil, "B" => 2, "C" => 3}, hash) +    hash.keys.each_with_index do |string_key, h| +      assert_predicate(string_key, :frozen?) +      assert_same(string_key, @row.headers[h]) +    end +  end + +  def test_to_csv +    # normal conversion +    assert_equal("1,2,3,4,\n", @row.to_csv) +    assert_equal("1,2,3,4,\n", @row.to_s)  # alias + +    # with options +    assert_equal( "1|2|3|4|\r\n", +                  @row.to_csv(col_sep: "|", row_sep: "\r\n") ) +  end + +  def test_array_delegation +    assert_not_empty(@row, "Row was empty.") + +    assert_equal([@row.headers.size, @row.fields.size].max, @row.size) +  end + +  def test_inspect_shows_header_field_pairs +    str = @row.inspect +    @row.each do |header, field| +      assert_include(str, "#{header.inspect}:#{field.inspect}", +                     "Header field pair not found.") +    end +  end + +  def test_inspect_encoding_is_ascii_compatible +    assert_send([Encoding, :compatible?, +                 Encoding.find("US-ASCII"), +                 @row.inspect.encoding], +                "inspect() was not ASCII compatible.") +  end + +  def test_inspect_shows_symbol_headers_as_bare_attributes +    str = CSV::Row.new(@row.headers.map { |h| h.to_sym }, @row.fields).inspect +    @row.each do |header, field| +      assert_include(str, "#{header}:#{field.inspect}", +                     "Header field pair not found.") +    end +  end + +  def test_can_be_compared_with_other_classes +    assert_not_nil(CSV::Row.new([ ], [ ]), "The row was nil") +  end + +  def test_can_be_compared_when_not_a_row +    r = @row == [] +    assert_equal false, r +  end +end diff --git a/jni/ruby/test/csv/test_table.rb b/jni/ruby/test/csv/test_table.rb new file mode 100755 index 0000000..44e9d4a --- /dev/null +++ b/jni/ruby/test/csv/test_table.rb @@ -0,0 +1,434 @@ +#!/usr/bin/env ruby -w +# encoding: UTF-8 + +# tc_table.rb +# +#  Created by James Edward Gray II on 2005-10-31. +#  Copyright 2005 James Edward Gray II. You can redistribute or modify this code +#  under the terms of Ruby's license. + +require_relative "base" + +class TestCSV::Table < TestCSV +  extend DifferentOFS + +  def setup +    super +    @rows  = [ CSV::Row.new(%w{A B C}, [1, 2, 3]), +               CSV::Row.new(%w{A B C}, [4, 5, 6]), +               CSV::Row.new(%w{A B C}, [7, 8, 9]) ] +    @table = CSV::Table.new(@rows) + +    @header_table = CSV::Table.new( +      [CSV::Row.new(%w{A B C}, %w{A B C}, true)] + @rows +    ) +  end + +  def test_initialze +    assert_not_nil(@table) +    assert_instance_of(CSV::Table, @table) +  end + +  def test_modes +    assert_equal(:col_or_row, @table.mode) + +    # non-destructive changes, intended for one shot calls +    cols = @table.by_col +    assert_equal(:col_or_row, @table.mode) +    assert_equal(:col, cols.mode) +    assert_equal(@table, cols) + +    rows = @table.by_row +    assert_equal(:col_or_row, @table.mode) +    assert_equal(:row, rows.mode) +    assert_equal(@table, rows) + +    # destructive mode changing calls +    assert_equal(@table, @table.by_row!) +    assert_equal(:row, @table.mode) +    assert_equal(@table, @table.by_col_or_row!) +    assert_equal(:col_or_row, @table.mode) +  end + +  def test_headers +    assert_equal(@rows.first.headers, @table.headers) +  end + +  def test_headers_empty +    t = CSV::Table.new([]) +    assert_equal Array.new, t.headers +  end + +  def test_index +    ################## +    ### Mixed Mode ### +    ################## +    # by row +    @rows.each_index { |i| assert_equal(@rows[i], @table[i]) } +    assert_equal(nil, @table[100])  # empty row + +    # by col +    @rows.first.headers.each do |header| +      assert_equal(@rows.map { |row| row[header] }, @table[header]) +    end +    assert_equal([nil] * @rows.size, @table["Z"])  # empty col + +    # by cell, row then col +    assert_equal(2, @table[0][1]) +    assert_equal(6, @table[1]["C"]) + +    # by cell, col then row +    assert_equal(5, @table["B"][1]) +    assert_equal(9, @table["C"][2]) + +    # with headers (by col) +    assert_equal(["B", 2, 5, 8], @header_table["B"]) + +    ################### +    ### Column Mode ### +    ################### +    @table.by_col! + +    assert_equal([2, 5, 8], @table[1]) +    assert_equal([2, 5, 8], @table["B"]) + +    ################ +    ### Row Mode ### +    ################ +    @table.by_row! + +    assert_equal(@rows[1], @table[1]) +    assert_raise(TypeError) { @table["B"] } + +    ############################ +    ### One Shot Mode Change ### +    ############################ +    assert_equal(@rows[1], @table[1]) +    assert_equal([2, 5, 8], @table.by_col[1]) +    assert_equal(@rows[1], @table[1]) +  end + +  def test_set_row_or_column +    ################## +    ### Mixed Mode ### +    ################## +    # set row +    @table[2] = [10, 11, 12] +    assert_equal([%w[A B C], [1, 2, 3], [4, 5, 6], [10, 11, 12]], @table.to_a) + +    @table[3] = CSV::Row.new(%w[A B C], [13, 14, 15]) +    assert_equal( [%w[A B C], [1, 2, 3], [4, 5, 6], [10, 11, 12], [13, 14, 15]], +                  @table.to_a ) + +    # set col +    @table["Type"] = "data" +    assert_equal( [ %w[A B C Type], +                    [1, 2, 3, "data"], +                    [4, 5, 6, "data"], +                    [10, 11, 12, "data"], +                    [13, 14, 15, "data"] ], +                  @table.to_a ) + +    @table["Index"] = [1, 2, 3] +    assert_equal( [ %w[A B C Type Index], +                    [1, 2, 3, "data", 1], +                    [4, 5, 6, "data", 2], +                    [10, 11, 12, "data", 3], +                    [13, 14, 15, "data", nil] ], +                  @table.to_a ) + +    @table["B"] = [100, 200] +    assert_equal( [ %w[A B C Type Index], +                    [1, 100, 3, "data", 1], +                    [4, 200, 6, "data", 2], +                    [10, nil, 12, "data", 3], +                    [13, nil, 15, "data", nil] ], +                  @table.to_a ) + +    # verify resulting table +    assert_equal(<<-END_RESULT.gsub(/^\s+/, ""), @table.to_csv) +    A,B,C,Type,Index +    1,100,3,data,1 +    4,200,6,data,2 +    10,,12,data,3 +    13,,15,data, +    END_RESULT + +    # with headers +    @header_table["Type"] = "data" +    assert_equal(%w[Type data data data], @header_table["Type"]) + +    ################### +    ### Column Mode ### +    ################### +    @table.by_col! + +    @table[1] = [2, 5, 11, 14] +    assert_equal( [ %w[A B C Type Index], +                    [1, 2, 3, "data", 1], +                    [4, 5, 6, "data", 2], +                    [10, 11, 12, "data", 3], +                    [13, 14, 15, "data", nil] ], +                  @table.to_a ) + +    @table["Extra"] = "new stuff" +    assert_equal( [ %w[A B C Type Index Extra], +                    [1, 2, 3, "data", 1, "new stuff"], +                    [4, 5, 6, "data", 2, "new stuff"], +                    [10, 11, 12, "data", 3, "new stuff"], +                    [13, 14, 15, "data", nil, "new stuff"] ], +                  @table.to_a ) + +    ################ +    ### Row Mode ### +    ################ +    @table.by_row! + +    @table[1] = (1..6).to_a +    assert_equal( [ %w[A B C Type Index Extra], +                    [1, 2, 3, "data", 1, "new stuff"], +                    [1, 2, 3, 4, 5, 6], +                    [10, 11, 12, "data", 3, "new stuff"], +                    [13, 14, 15, "data", nil, "new stuff"] ], +                  @table.to_a ) + +    assert_raise(TypeError) { @table["Extra"] = nil } +  end + +  def test_set_by_col_with_header_row +    r  = [ CSV::Row.new(%w{X Y Z}, [97, 98, 99], true) ] +    t = CSV::Table.new(r) +    t.by_col! +    t['A'] = [42] +    assert_equal(['A'], t['A']) +  end + +  def test_each +    ###################### +    ### Mixed/Row Mode ### +    ###################### +    i = 0 +    @table.each do |row| +      assert_equal(@rows[i], row) +      i += 1 +    end + +    # verify that we can chain the call +    assert_equal(@table, @table.each { }) + +    ################### +    ### Column Mode ### +    ################### +    @table.by_col! + +    headers = @table.headers +    @table.each do |header, column| +      assert_equal(headers.shift, header) +      assert_equal(@table[header], column) +    end + +    ############################ +    ### One Shot Mode Change ### +    ############################ +    @table.by_col_or_row! + +    @table.each { |row| assert_instance_of(CSV::Row, row) } +    @table.by_col.each { |tuple| assert_instance_of(Array, tuple) } +    @table.each { |row| assert_instance_of(CSV::Row, row) } +  end + +  def test_enumerable +    assert_equal( @rows.values_at(0, 2), +                  @table.select { |row| (row["B"] % 2).zero? } ) + +    assert_equal(@rows[1], @table.find { |row| row["C"] > 5 }) +  end + +  def test_to_a +    assert_equal([%w[A B C], [1, 2, 3], [4, 5, 6], [7, 8, 9]], @table.to_a) + +    # with headers +    assert_equal( [%w[A B C], [1, 2, 3], [4, 5, 6], [7, 8, 9]], +                  @header_table.to_a ) +  end + +  def test_to_csv +    csv = <<-END_CSV.gsub(/^\s+/, "") +    A,B,C +    1,2,3 +    4,5,6 +    7,8,9 +    END_CSV + +    # normal conversion +    assert_equal(csv, @table.to_csv) +    assert_equal(csv, @table.to_s)  # alias + +    # with options +    assert_equal( csv.gsub(",", "|").gsub("\n", "\r\n"), +                  @table.to_csv(col_sep: "|", row_sep: "\r\n") ) +    assert_equal( csv.lines.to_a[1..-1].join(''), +                  @table.to_csv(:write_headers => false) ) + +    # with headers +    assert_equal(csv, @header_table.to_csv) +  end + +  def test_append +    # verify that we can chain the call +    assert_equal(@table, @table << [10, 11, 12]) + +    # Array append +    assert_equal(CSV::Row.new(%w[A B C], [10, 11, 12]), @table[-1]) + +    # Row append +    assert_equal(@table, @table << CSV::Row.new(%w[A B C], [13, 14, 15])) +    assert_equal(CSV::Row.new(%w[A B C], [13, 14, 15]), @table[-1]) +  end + +  def test_delete_mixed +    ################## +    ### Mixed Mode ### +    ################## +    # delete a row +    assert_equal(@rows[1], @table.delete(1)) + +    # delete a col +    assert_equal(@rows.map { |row| row["A"] }, @table.delete("A")) + +    # verify resulting table +    assert_equal(<<-END_RESULT.gsub(/^\s+/, ""), @table.to_csv) +    B,C +    2,3 +    8,9 +    END_RESULT +  end + +  def test_delete_column +    ################### +    ### Column Mode ### +    ################### +    @table.by_col! + +    assert_equal(@rows.map { |row| row[0] }, @table.delete(0)) +    assert_equal(@rows.map { |row| row["C"] }, @table.delete("C")) + +    # verify resulting table +    assert_equal(<<-END_RESULT.gsub(/^\s+/, ""), @table.to_csv) +    B +    2 +    5 +    8 +    END_RESULT +  end + +  def test_delete_row +    ################ +    ### Row Mode ### +    ################ +    @table.by_row! + +    assert_equal(@rows[1], @table.delete(1)) +    assert_raise(TypeError) { @table.delete("C") } + +    # verify resulting table +    assert_equal(<<-END_RESULT.gsub(/^\s+/, ""), @table.to_csv) +    A,B,C +    1,2,3 +    7,8,9 +    END_RESULT +  end + +  def test_delete_with_blank_rows +    data = "col1,col2\nra1,ra2\n\nrb1,rb2" +    table = CSV.parse(data, :headers => true) +    assert_equal(["ra2", nil, "rb2"], table.delete("col2")) +  end + +  def test_delete_if_row +    ###################### +    ### Mixed/Row Mode ### +    ###################### +    # verify that we can chain the call +    assert_equal(@table, @table.delete_if { |row| (row["B"] % 2).zero? }) + +    # verify resulting table +    assert_equal(<<-END_RESULT.gsub(/^\s+/, ""), @table.to_csv) +    A,B,C +    4,5,6 +    END_RESULT +  end + +  def test_delete_if_column +    ################### +    ### Column Mode ### +    ################### +    @table.by_col! + +    assert_equal(@table, @table.delete_if { |h, v| h > "A" }) +    assert_equal(<<-END_RESULT.gsub(/^\s+/, ""), @table.to_csv) +    A +    1 +    4 +    7 +    END_RESULT +  end + +  def test_values_at +    ################## +    ### Mixed Mode ### +    ################## +    # rows +    assert_equal(@rows.values_at(0, 2), @table.values_at(0, 2)) +    assert_equal(@rows.values_at(1..2), @table.values_at(1..2)) + +    # cols +    assert_equal([[1, 3], [4, 6], [7, 9]], @table.values_at("A", "C")) +    assert_equal([[2, 3], [5, 6], [8, 9]], @table.values_at("B".."C")) + +    ################### +    ### Column Mode ### +    ################### +    @table.by_col! + +    assert_equal([[1, 3], [4, 6], [7, 9]], @table.values_at(0, 2)) +    assert_equal([[1, 3], [4, 6], [7, 9]], @table.values_at("A", "C")) + +    ################ +    ### Row Mode ### +    ################ +    @table.by_row! + +    assert_equal(@rows.values_at(0, 2), @table.values_at(0, 2)) +    assert_raise(TypeError) { @table.values_at("A", "C") } + +    ############################ +    ### One Shot Mode Change ### +    ############################ +    assert_equal(@rows.values_at(0, 2), @table.values_at(0, 2)) +    assert_equal([[1, 3], [4, 6], [7, 9]], @table.by_col.values_at(0, 2)) +    assert_equal(@rows.values_at(0, 2), @table.values_at(0, 2)) +  end + +  def test_array_delegation +    assert_not_empty(@table, "Table was empty.") + +    assert_equal(@rows.size, @table.size) +  end + +  def test_inspect_shows_current_mode +    str = @table.inspect +    assert_include(str, "mode:#{@table.mode}", "Mode not shown.") + +    @table.by_col! +    str = @table.inspect +    assert_include(str, "mode:#{@table.mode}", "Mode not shown.") +  end + +  def test_inspect_encoding_is_ascii_compatible +    assert_send([Encoding, :compatible?, +                 Encoding.find("US-ASCII"), +                 @table.inspect.encoding], +            "inspect() was not ASCII compatible." ) +  end +end diff --git a/jni/ruby/test/csv/ts_all.rb b/jni/ruby/test/csv/ts_all.rb new file mode 100644 index 0000000..3893841 --- /dev/null +++ b/jni/ruby/test/csv/ts_all.rb @@ -0,0 +1,20 @@ +#!/usr/bin/env ruby -w +# encoding: UTF-8 + +# ts_all.rb +# +#  Created by James Edward Gray II on 2005-10-31. +#  Copyright 2005 James Edward Gray II. You can redistribute or modify this code +#  under the terms of Ruby's license. + +require "test/unit" + +require "test_csv_parsing" +require "test_features" +require "test_interface" +require "test_csv_writing" +require "test_data_converters" +require "test_row" +require "test_table" +require "test_headers" +require "test_encodings" | 
