summaryrefslogtreecommitdiff
path: root/spec/ruby/core/string/shared/each_line.rb
blob: 066b0c1040405a392f30c7b1d316c3e0028be09c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
describe :string_each_line, shared: true do
  it "splits using default newline separator when none is specified" do
    a = []
    "one\ntwo\r\nthree".send(@method) { |s| a << s }
    a.should == ["one\n", "two\r\n", "three"]

    b = []
    "hello\n\n\nworld".send(@method) { |s| b << s }
    b.should == ["hello\n", "\n", "\n", "world"]

    c = []
    "\n\n\n\n\n".send(@method) {|s| c << s}
    c.should == ["\n", "\n", "\n", "\n", "\n"]
  end

  it "splits self using the supplied record separator and passes each substring to the block" do
    a = []
    "one\ntwo\r\nthree".send(@method, "\n") { |s| a << s }
    a.should == ["one\n", "two\r\n", "three"]

    b = []
    "hello\nworld".send(@method, 'l') { |s| b << s }
    b.should == [ "hel", "l", "o\nworl", "d" ]

    c = []
    "hello\n\n\nworld".send(@method, "\n") { |s| c << s }
    c.should == ["hello\n", "\n", "\n", "world"]
  end

  it "splits strings containing multibyte characters" do
    s = <<~EOS
      foo
      🤡🤡🤡🤡🤡🤡🤡
      bar
      baz
    EOS

    b = []
    s.send(@method) { |part| b << part }
    b.should == ["foo\n", "🤡🤡🤡🤡🤡🤡🤡\n", "bar\n", "baz\n"]
  end

  it "taints substrings that are passed to the block if self is tainted" do
    "one\ntwo\r\nthree".taint.send(@method) { |s| s.tainted?.should == true }

    "x.y.".send(@method, ".".taint) { |s| s.tainted?.should == false }
  end

  it "passes self as a whole to the block if the separator is nil" do
    a = []
    "one\ntwo\r\nthree".send(@method, nil) { |s| a << s }
    a.should == ["one\ntwo\r\nthree"]
  end

  ruby_version_is ''...'2.5' do
    it "yields paragraphs (broken by 2 or more successive newlines) when passed ''" do
      a = []
      "hello\nworld\n\n\nand\nuniverse\n\n\n\n\n".send(@method, '') { |s| a << s }
      a.should == ["hello\nworld\n\n\n", "and\nuniverse\n\n\n\n\n"]

      a = []
      "hello\nworld\n\n\nand\nuniverse\n\n\n\n\ndog".send(@method, '') { |s| a << s }
      a.should == ["hello\nworld\n\n\n", "and\nuniverse\n\n\n\n\n", "dog"]
    end
  end

  ruby_version_is '2.5' do
    it "yields paragraphs (broken by 2 or more successive newlines) when passed '' and replaces multiple newlines with only two ones" do
      a = []
      "hello\nworld\n\n\nand\nuniverse\n\n\n\n\n".send(@method, '') { |s| a << s }
      a.should == ["hello\nworld\n\n", "and\nuniverse\n\n"]

      a = []
      "hello\nworld\n\n\nand\nuniverse\n\n\n\n\ndog".send(@method, '') { |s| a << s }
      a.should == ["hello\nworld\n\n", "and\nuniverse\n\n", "dog"]
    end
  end

  describe "uses $/" do
    before :each do
      @before_separator = $/
    end

    after :each do
      $/ = @before_separator
    end

    it "as the separator when none is given" do
      [
        "", "x", "x\ny", "x\ry", "x\r\ny", "x\n\r\r\ny",
        "hello hullo bello"
      ].each do |str|
        ["", "llo", "\n", "\r", nil].each do |sep|
          expected = []
          str.send(@method, sep) { |x| expected << x }

          $/ = sep

          actual = []
          str.send(@method) { |x| actual << x }

          actual.should == expected
        end
      end
    end
  end

  it "yields subclass instances for subclasses" do
    a = []
    StringSpecs::MyString.new("hello\nworld").send(@method) { |s| a << s.class }
    a.should == [StringSpecs::MyString, StringSpecs::MyString]
  end

  it "returns self" do
    s = "hello\nworld"
    (s.send(@method) {}).should equal(s)
  end

  it "tries to convert the separator to a string using to_str" do
    separator = mock('l')
    separator.should_receive(:to_str).and_return("l")

    a = []
    "hello\nworld".send(@method, separator) { |s| a << s }
    a.should == [ "hel", "l", "o\nworl", "d" ]
  end

  it "does not care if the string is modified while substituting" do
    str = "hello\nworld."
    out = []
    str.send(@method){|x| out << x; str[-1] = '!' }.should == "hello\nworld!"
    out.should == ["hello\n", "world."]
  end

  it "raises a TypeError when the separator can't be converted to a string" do
    lambda { "hello world".send(@method, false) {}     }.should raise_error(TypeError)
    lambda { "hello world".send(@method, mock('x')) {} }.should raise_error(TypeError)
  end

  it "accepts a string separator" do
    "hello world".send(@method, ?o).to_a.should == ["hello", " wo", "rld"]
  end

  it "raises a TypeError when the separator is a symbol" do
    lambda { "hello world".send(@method, :o).to_a }.should raise_error(TypeError)
  end

  ruby_version_is '2.4' do
    context "when `chomp` keyword argument is passed" do
      it "removes new line characters when separator is not specified" do
        a = []
        "hello \nworld\n".send(@method, chomp: true) { |s| a << s }
        a.should == ["hello ", "world"]

        a = []
        "hello \r\nworld\r\n".send(@method, chomp: true) { |s| a << s }
        a.should == ["hello ", "world"]
      end

      it "removes only specified separator" do
        a = []
        "hello world".send(@method, ' ', chomp: true) { |s| a << s }
        a.should == ["hello", "world"]
      end

      # https://bugs.ruby-lang.org/issues/14257
      it "ignores new line characters when separator is specified" do
        a = []
        "hello\n world\n".send(@method, ' ', chomp: true) { |s| a << s }
        a.should == ["hello\n", "world\n"]

        a = []
        "hello\r\n world\r\n".send(@method, ' ', chomp: true) { |s| a << s }
        a.should == ["hello\r\n", "world\r\n"]
      end
    end
  end
end