1#!/usr/bin/env ruby -w 2# encoding: UTF-8 3 4# tc_features.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 10begin 11 require "zlib" 12rescue LoadError 13end 14 15require_relative "base" 16require "tempfile" 17 18class TestCSV::Features < TestCSV 19 extend DifferentOFS 20 21 TEST_CASES = [ [%Q{a,b}, ["a", "b"]], 22 [%Q{a,"""b"""}, ["a", "\"b\""]], 23 [%Q{a,"""b"}, ["a", "\"b"]], 24 [%Q{a,"b"""}, ["a", "b\""]], 25 [%Q{a,"\nb"""}, ["a", "\nb\""]], 26 [%Q{a,"""\nb"}, ["a", "\"\nb"]], 27 [%Q{a,"""\nb\n"""}, ["a", "\"\nb\n\""]], 28 [%Q{a,"""\nb\n""",\nc}, ["a", "\"\nb\n\"", nil]], 29 [%Q{a,,,}, ["a", nil, nil, nil]], 30 [%Q{,}, [nil, nil]], 31 [%Q{"",""}, ["", ""]], 32 [%Q{""""}, ["\""]], 33 [%Q{"""",""}, ["\"",""]], 34 [%Q{,""}, [nil,""]], 35 [%Q{,"\r"}, [nil,"\r"]], 36 [%Q{"\r\n,"}, ["\r\n,"]], 37 [%Q{"\r\n,",}, ["\r\n,", nil]] ] 38 39 def setup 40 super 41 @sample_data = <<-END_DATA.gsub(/^ +/, "") 42 line,1,abc 43 line,2,"def\nghi" 44 45 line,4,jkl 46 END_DATA 47 @csv = CSV.new(@sample_data) 48 end 49 50 def test_col_sep 51 [";", "\t"].each do |sep| 52 TEST_CASES.each do |test_case| 53 assert_equal( test_case.last.map { |t| t.tr(",", sep) unless t.nil? }, 54 CSV.parse_line( test_case.first.tr(",", sep), 55 col_sep: sep ) ) 56 end 57 end 58 assert_equal([",,,", nil], CSV.parse_line(",,,;", col_sep: ";")) 59 end 60 61 def test_row_sep 62 assert_raise(CSV::MalformedCSVError) do 63 CSV.parse_line("1,2,3\n,4,5\r\n", row_sep: "\r\n") 64 end 65 assert_equal( ["1", "2", "3\n", "4", "5"], 66 CSV.parse_line(%Q{1,2,"3\n",4,5\r\n}, row_sep: "\r\n")) 67 end 68 69 def test_quote_char 70 TEST_CASES.each do |test_case| 71 assert_equal( test_case.last.map { |t| t.tr('"', "'") unless t.nil? }, 72 CSV.parse_line( test_case.first.tr('"', "'"), 73 quote_char: "'" ) ) 74 end 75 end 76 77 def test_bug_8405 78 TEST_CASES.each do |test_case| 79 assert_equal( test_case.last.map { |t| t.tr('"', "|") unless t.nil? }, 80 CSV.parse_line( test_case.first.tr('"', "|"), 81 quote_char: "|" ) ) 82 end 83 end 84 85 def test_csv_char_readers 86 %w[col_sep row_sep quote_char].each do |reader| 87 csv = CSV.new("abc,def", reader.to_sym => "|") 88 assert_equal("|", csv.send(reader)) 89 end 90 end 91 92 def test_row_sep_auto_discovery 93 ["\r\n", "\n", "\r"].each do |line_end| 94 data = "1,2,3#{line_end}4,5#{line_end}" 95 discovered = CSV.new(data).row_sep 96 assert_equal(line_end, discovered) 97 end 98 99 assert_equal("\n", CSV.new("\n\r\n\r").row_sep) 100 101 assert_equal($/, CSV.new("").row_sep) 102 103 assert_equal($/, CSV.new(STDERR).row_sep) 104 end 105 106 def test_lineno 107 assert_equal(5, @sample_data.lines.to_a.size) 108 109 4.times do |line_count| 110 assert_equal(line_count, @csv.lineno) 111 assert_not_nil(@csv.shift) 112 assert_equal(line_count + 1, @csv.lineno) 113 end 114 assert_nil(@csv.shift) 115 end 116 117 def test_readline 118 test_lineno 119 120 @csv.rewind 121 122 test_lineno 123 end 124 125 def test_unknown_options 126 assert_raise(ArgumentError) { CSV.new(String.new, unknown: :error) } 127 end 128 129 def test_skip_blanks 130 assert_equal(4, @csv.to_a.size) 131 132 @csv = CSV.new(@sample_data, skip_blanks: true) 133 134 count = 0 135 @csv.each do |row| 136 count += 1 137 assert_equal("line", row.first) 138 end 139 assert_equal(3, count) 140 end 141 142 def test_csv_behavior_readers 143 %w[ unconverted_fields return_headers write_headers 144 skip_blanks force_quotes ].each do |behavior| 145 assert( !CSV.new("abc,def").send("#{behavior}?"), 146 "Behavior defaulted to on." ) 147 csv = CSV.new("abc,def", behavior.to_sym => true) 148 assert(csv.send("#{behavior}?"), "Behavior change now registered.") 149 end 150 end 151 152 def test_converters_reader 153 # no change 154 assert_equal( [:integer], 155 CSV.new("abc,def", converters: [:integer]).converters ) 156 157 # just one 158 assert_equal( [:integer], 159 CSV.new("abc,def", converters: :integer).converters ) 160 161 # expanded 162 assert_equal( [:integer, :float], 163 CSV.new("abc,def", converters: :numeric).converters ) 164 165 # custom 166 csv = CSV.new("abc,def", converters: [:integer, lambda { }]) 167 assert_equal(2, csv.converters.size) 168 assert_equal(:integer, csv.converters.first) 169 assert_instance_of(Proc, csv.converters.last) 170 end 171 172 def test_header_converters_reader 173 # no change 174 hc = :header_converters 175 assert_equal([:downcase], CSV.new("abc,def", hc => [:downcase]).send(hc)) 176 177 # just one 178 assert_equal([:downcase], CSV.new("abc,def", hc => :downcase).send(hc)) 179 180 # custom 181 csv = CSV.new("abc,def", hc => [:symbol, lambda { }]) 182 assert_equal(2, csv.send(hc).size) 183 assert_equal(:symbol, csv.send(hc).first) 184 assert_instance_of(Proc, csv.send(hc).last) 185 end 186 187 # reported by Kev Jackson 188 def test_failing_to_escape_col_sep_bug_fix 189 assert_nothing_raised(Exception) { CSV.new(String.new, col_sep: "|") } 190 end 191 192 # reported by Chris Roos 193 def test_failing_to_reset_headers_in_rewind_bug_fix 194 csv = CSV.new("forename,surname", headers: true, return_headers: true) 195 csv.each { |row| assert row.header_row? } 196 csv.rewind 197 csv.each { |row| assert row.header_row? } 198 end 199 200 # reported by Dave Burt 201 def test_leading_empty_fields_with_multibyte_col_sep_bug_fix 202 data = <<-END_DATA.gsub(/^\s+/, "") 203 <=><=>A<=>B<=>C 204 1<=>2<=>3 205 END_DATA 206 parsed = CSV.parse(data, col_sep: "<=>") 207 assert_equal([[nil, nil, "A", "B", "C"], ["1", "2", "3"]], parsed) 208 end 209 210 def test_gzip_reader_bug_fix 211 zipped = nil 212 assert_nothing_raised(NoMethodError) do 213 zipped = CSV.new( 214 Zlib::GzipReader.open( 215 File.join(File.dirname(__FILE__), "line_endings.gz") 216 ) 217 ) 218 end 219 assert_equal("\r\n", zipped.row_sep) 220 end if defined?(Zlib::GzipReader) 221 222 def test_gzip_writer_bug_fix 223 tempfile = Tempfile.new(%w"temp .gz") 224 tempfile.close 225 file = tempfile.path 226 zipped = nil 227 assert_nothing_raised(NoMethodError) do 228 zipped = CSV.new(Zlib::GzipWriter.open(file)) 229 end 230 zipped << %w[one two three] 231 zipped << [1, 2, 3] 232 zipped.close 233 234 assert( Zlib::GzipReader.open(file) { |f| f.read }. 235 include?($INPUT_RECORD_SEPARATOR), 236 "@row_sep did not default" ) 237 tempfile.close(true) 238 end if defined?(Zlib::GzipWriter) 239 240 def test_inspect_is_smart_about_io_types 241 str = CSV.new("string,data").inspect 242 assert(str.include?("io_type:StringIO"), "IO type not detected.") 243 244 str = CSV.new($stderr).inspect 245 assert(str.include?("io_type:$stderr"), "IO type not detected.") 246 247 tempfile = Tempfile.new(%w"temp .csv") 248 tempfile.close 249 path = tempfile.path 250 File.open(path, "w") { |csv| csv << "one,two,three\n1,2,3\n" } 251 str = CSV.open(path) { |csv| csv.inspect } 252 assert(str.include?("io_type:File"), "IO type not detected.") 253 tempfile.close(true) 254 end 255 256 def test_inspect_shows_key_attributes 257 str = @csv.inspect 258 %w[lineno col_sep row_sep quote_char].each do |attr_name| 259 assert_match(/\b#{attr_name}:[^\s>]+/, str) 260 end 261 end 262 263 def test_inspect_shows_headers_when_available 264 CSV.new("one,two,three\n1,2,3\n", headers: true) do |csv| 265 assert(csv.inspect.include?("headers:true"), "Header hint not shown.") 266 csv.shift # load headers 267 assert_match(/headers:\[[^\]]+\]/, csv.inspect) 268 end 269 end 270 271 def test_inspect_encoding_is_ascii_compatible 272 CSV.new("one,two,three\n1,2,3\n".encode("UTF-16BE")) do |csv| 273 assert( Encoding.compatible?( Encoding.find("US-ASCII"), 274 csv.inspect.encoding ), 275 "inspect() was not ASCII compatible." ) 276 end 277 end 278 279 def test_version 280 assert_not_nil(CSV::VERSION) 281 assert_instance_of(String, CSV::VERSION) 282 assert(CSV::VERSION.frozen?) 283 assert_match(/\A\d\.\d\.\d\Z/, CSV::VERSION) 284 end 285 286 def test_accepts_comment_skip_lines_option 287 assert_nothing_raised(ArgumentError) do 288 CSV.new nil, :skip_lines => /\A\s*#/ 289 end 290 end 291 292 def test_accepts_comment_defaults_to_nil 293 c = CSV.new nil 294 assert_equal c.skip_lines, nil 295 end 296 297 class RegexStub 298 end 299 300 def test_requires_skip_lines_to_call_match 301 regex_stub = RegexStub.new 302 assert_raise(ArgumentError) do 303 CSV.new nil, :skip_lines => regex_stub 304 end 305 end 306 307 def test_comment_rows_are_ignored 308 sample_data = "line,1,a\n#not,a,line\nline,2,b\n #also,no,line" 309 c = CSV.new sample_data, :skip_lines => /\A\s*#/ 310 assert_equal c.each.to_a, [["line", "1", "a"], ["line", "2", "b"]] 311 end 312 313 def test_quoted_skip_line_markers_are_ignored 314 sample_data = "line,1,a\n\"#not\",a,line\nline,2,b" 315 c = CSV.new sample_data, :skip_lines => /\A\s*#/ 316 assert_equal c.each.to_a, [["line", "1", "a"], ["#not", "a", "line"], ["line", "2", "b"]] 317 end 318end 319