require 'em_test_helper' # TODO!!! Need tests for overlength headers and text bodies. class TestLineText2 < Test::Unit::TestCase # Run each of these tests two ways: passing in the whole test-dataset in one chunk, # and passing it in one character at a time. class Basic include EM::Protocols::LineText2 attr_reader :lines def receive_line line (@lines ||= []) << line end end def test_basic testdata = "Line 1\nLine 2\r\nLine 3\n" a = Basic.new a.receive_data testdata assert_equal( ["Line 1", "Line 2", "Line 3"], a.lines ) a = Basic.new testdata.length.times {|i| a.receive_data( testdata[i...i+1] ) } assert_equal( ["Line 1", "Line 2", "Line 3"], a.lines ) end # The basic test above shows that extra newlines are chomped # This test shows that newlines are preserved if the delimiter isn't \n class PreserveNewlines include EM::Protocols::LineText2 attr_reader :lines def initialize *args super @delim = "|" set_delimiter @delim end def receive_line line (@lines ||= []) << line end end def test_preserve_newlines a = PreserveNewlines.new a.receive_data "aaa|bbb|ccc|\n|\r\n| \t ||" assert_equal( ["aaa", "bbb", "ccc", "\n", "\r\n", " \t ", ""], a.lines ) end class ChangeDelimiter include EM::Protocols::LineText2 attr_reader :lines def initialize *args super @delim = "A" set_delimiter @delim end def receive_line line (@lines ||= []) << line set_delimiter( @delim.succ! ) end end def test_change_delimiter testdata = %Q(LineaALinebBLinecCLinedD) a = ChangeDelimiter.new a.receive_data testdata assert_equal( ["Linea", "Lineb", "Linec", "Lined"], a.lines ) a = ChangeDelimiter.new testdata.length.times {|i| a.receive_data( testdata[i...i+1] ) } assert_equal( ["Linea", "Lineb", "Linec", "Lined"], a.lines ) end class RegexDelimiter include EM::Protocols::LineText2 attr_reader :lines def initialize *args super @delim = /[A-D]/ set_delimiter @delim end def receive_line line (@lines ||= []) << line end end def test_regex_delimiter testdata = %Q(LineaALinebBLinecCLinedD) a = RegexDelimiter.new a.receive_data testdata assert_equal( ["Linea", "Lineb", "Linec", "Lined"], a.lines ) a = RegexDelimiter.new testdata.length.times {|i| a.receive_data( testdata[i...i+1] ) } assert_equal( ["Linea", "Lineb", "Linec", "Lined"], a.lines ) end #-- # Test two lines followed by an empty line, ten bytes of binary data, then # two more lines. class Binary include EM::Protocols::LineText2 attr_reader :lines, :body def initialize *args super @lines = [] @body = nil end def receive_line ln if ln == "" set_text_mode 10 else @lines << ln end end def receive_binary_data data @body = data end end def test_binary testdata = %Q(Line 1 Line 2 0000000000Line 3 Line 4 ) a = Binary.new a.receive_data testdata assert_equal( ["Line 1", "Line 2", "Line 3", "Line 4"], a.lines) assert_equal( "0000000000", a.body ) a = Binary.new testdata.length.times {|i| a.receive_data( testdata[i...i+1] ) } assert_equal( ["Line 1", "Line 2", "Line 3", "Line 4"], a.lines) assert_equal( "0000000000", a.body ) end # Test unsized binary data. The expectation is that each chunk of it # will be passed to us as it it received. class UnsizedBinary include EM::Protocols::LineText2 attr_reader :n_calls, :body def initialize *args super set_text_mode end def receive_binary_data data @n_calls ||= 0 @n_calls += 1 (@body ||= "") << data end end def test_unsized_binary testdata = "X\0" * 1000 a = UnsizedBinary.new a.receive_data testdata assert_equal( 1, a.n_calls ) assert_equal( testdata, a.body ) a = UnsizedBinary.new testdata.length.times {|i| a.receive_data( testdata[i...i+1] ) } assert_equal( 2000, a.n_calls ) assert_equal( testdata, a.body ) end # Test binary data with a "throw back" into line-mode. class ThrowBack include EM::Protocols::LineText2 attr_reader :headers def initialize *args super @headers = [] @n_bytes = 0 set_text_mode end def receive_binary_data data wanted = 25 - @n_bytes will_take = if data.length > wanted data.length - wanted else data.length end @n_bytes += will_take if @n_bytes == 25 set_line_mode( data[will_take..-1] ) end end def receive_line ln @headers << ln end end def test_throw_back testdata = "Line\n" * 10 a = ThrowBack.new a.receive_data testdata assert_equal( ["Line"] * 5, a.headers ) a = ThrowBack.new testdata.length.times {|i| a.receive_data( testdata[i...i+1] ) } assert_equal( ["Line"] * 5, a.headers ) end # Test multi-character line delimiters. # Also note that the test data has a "tail" with no delimiter, that will be # discarded, but cf. the BinaryTail test. # TODO!!! This test doesn't work in the byte-by-byte case. class Multichar include EM::Protocols::LineText2 attr_reader :lines def initialize *args super @lines = [] set_delimiter "012" end def receive_line ln @lines << ln end end def test_multichar testdata = "Line012Line012Line012Line" a = Multichar.new a.receive_data testdata assert_equal( ["Line"]*3, a.lines ) a = Multichar.new testdata.length.times {|i| a.receive_data( testdata[i...i+1] ) } # DOESN'T WORK in this case. Multi-character delimiters are broken. #assert_equal( ["Line"]*3, a.lines ) end # Test a binary "tail," when a sized binary transfer doesn't complete because # of an unbind. We get a partial result. class BinaryTail include EM::Protocols::LineText2 attr_reader :data def initialize *args super @data = "" set_text_mode 1000 end def receive_binary_data data # we expect to get all the data in one chunk, even in the byte-by-byte case, # because sized transfers by definition give us exactly one call to # #receive_binary_data. @data = data end end def test_binary_tail testdata = "0" * 500 a = BinaryTail.new a.receive_data testdata a.unbind assert_equal( "0" * 500, a.data ) a = BinaryTail.new testdata.length.times {|i| a.receive_data( testdata[i...i+1] ) } a.unbind assert_equal( "0" * 500, a.data ) end # Test an end-of-binary call. Arrange to receive binary data but don't bother counting it # as it comes. Rely on getting receive_end_of_binary_data to signal the transition back to # line mode. # At the present time, this isn't strictly necessary with sized binary chunks because by # definition we accumulate them and make exactly one call to receive_binary_data, but # we may want to support a mode in the future that would break up large chunks into multiple # calls. class LazyBinary include EM::Protocols::LineText2 attr_reader :data, :end def initialize *args super @data = "" set_text_mode 1000 end def receive_binary_data data # we expect to get all the data in one chunk, even in the byte-by-byte case, # because sized transfers by definition give us exactly one call to # #receive_binary_data. @data = data end def receive_end_of_binary_data @end = true end end def test_receive_end_of_binary_data testdata = "_" * 1000 a = LazyBinary.new testdata.length.times {|i| a.receive_data( testdata[i...i+1] ) } assert_equal( "_" * 1000, a.data ) assert( a.end ) end # This tests a bug fix in which calling set_text_mode failed when called # inside receive_binary_data. # class BinaryPair include EM::Protocols::LineText2 attr_reader :sizes def initialize *args super set_text_mode 1 @sizes = [] end def receive_binary_data dt @sizes << dt.length set_text_mode( (dt.length == 1) ? 2 : 1 ) end end def test_binary_pairs test_data = "123" * 5 a = BinaryPair.new a.receive_data test_data assert_equal( [1,2,1,2,1,2,1,2,1,2], a.sizes ) end end