1Code.require_file("test_helper.exs", __DIR__) 2 3defmodule StringIOTest do 4 use ExUnit.Case, async: true 5 6 doctest StringIO 7 8 test "open and close" do 9 {:ok, pid} = StringIO.open("") 10 assert StringIO.close(pid) == {:ok, {"", ""}} 11 end 12 13 test "contents" do 14 {:ok, pid} = StringIO.open("abc") 15 IO.write(pid, "edf") 16 assert StringIO.contents(pid) == {"abc", "edf"} 17 end 18 19 test "flush" do 20 {:ok, pid} = StringIO.open("") 21 IO.write(pid, "edf") 22 assert StringIO.flush(pid) == "edf" 23 assert StringIO.contents(pid) == {"", ""} 24 end 25 26 ## IO module 27 28 test "IO.read :line with \\n" do 29 {:ok, pid} = StringIO.open("abc\n") 30 assert IO.read(pid, :line) == "abc\n" 31 assert IO.read(pid, :line) == :eof 32 assert StringIO.contents(pid) == {"", ""} 33 end 34 35 test "IO.read :line with \\rn" do 36 {:ok, pid} = StringIO.open("abc\r\n") 37 assert IO.read(pid, :line) == "abc\n" 38 assert IO.read(pid, :line) == :eof 39 assert StringIO.contents(pid) == {"", ""} 40 end 41 42 test "IO.read :line without line break" do 43 {:ok, pid} = StringIO.open("abc") 44 assert IO.read(pid, :line) == "abc" 45 assert IO.read(pid, :line) == :eof 46 assert StringIO.contents(pid) == {"", ""} 47 end 48 49 test "IO.read :line with UTF-8" do 50 {:ok, pid} = StringIO.open("⼊\n") 51 assert IO.read(pid, :line) == "⼊\n" 52 assert IO.read(pid, :line) == :eof 53 assert StringIO.contents(pid) == {"", ""} 54 end 55 56 test "IO.read :line with invalid UTF-8" do 57 {:ok, pid} = StringIO.open(<<130, 227, 129, 132, 227, 129, 134>>) 58 assert IO.read(pid, :line) == {:error, :collect_line} 59 assert StringIO.contents(pid) == {<<130, 227, 129, 132, 227, 129, 134>>, ""} 60 end 61 62 test "IO.read count" do 63 {:ok, pid} = StringIO.open("abc") 64 assert IO.read(pid, 2) == "ab" 65 assert IO.read(pid, 8) == "c" 66 assert IO.read(pid, 1) == :eof 67 assert StringIO.contents(pid) == {"", ""} 68 end 69 70 test "IO.read count with UTF-8" do 71 {:ok, pid} = StringIO.open("あいう") 72 assert IO.read(pid, 2) == "あい" 73 assert IO.read(pid, 8) == "う" 74 assert IO.read(pid, 1) == :eof 75 assert StringIO.contents(pid) == {"", ""} 76 end 77 78 test "IO.read count with invalid UTF-8" do 79 {:ok, pid} = StringIO.open(<<130, 227, 129, 132, 227, 129, 134>>) 80 assert IO.read(pid, 2) == {:error, :invalid_unicode} 81 assert StringIO.contents(pid) == {<<130, 227, 129, 132, 227, 129, 134>>, ""} 82 end 83 84 test "IO.binread :line with \\n" do 85 {:ok, pid} = StringIO.open("abc\n") 86 assert IO.binread(pid, :line) == "abc\n" 87 assert IO.binread(pid, :line) == :eof 88 assert StringIO.contents(pid) == {"", ""} 89 end 90 91 test "IO.binread :line with \\r\\n" do 92 {:ok, pid} = StringIO.open("abc\r\n") 93 assert IO.binread(pid, :line) == "abc\n" 94 assert IO.binread(pid, :line) == :eof 95 assert StringIO.contents(pid) == {"", ""} 96 end 97 98 test "IO.binread :line without line break" do 99 {:ok, pid} = StringIO.open("abc") 100 assert IO.binread(pid, :line) == "abc" 101 assert IO.binread(pid, :line) == :eof 102 assert StringIO.contents(pid) == {"", ""} 103 end 104 105 test "IO.binread :line with raw bytes" do 106 {:ok, pid} = StringIO.open(<<181, 255, 194, ?\n>>) 107 assert IO.binread(pid, :line) == <<181, 255, 194, ?\n>> 108 assert IO.binread(pid, :line) == :eof 109 assert StringIO.contents(pid) == {"", ""} 110 end 111 112 test "IO.binread count" do 113 {:ok, pid} = StringIO.open("abc") 114 assert IO.binread(pid, 2) == "ab" 115 assert IO.binread(pid, 8) == "c" 116 assert IO.binread(pid, 1) == :eof 117 assert StringIO.contents(pid) == {"", ""} 118 end 119 120 test "IO.binread count with UTF-8" do 121 {:ok, pid} = StringIO.open("あいう") 122 assert IO.binread(pid, 2) == <<227, 129>> 123 assert IO.binread(pid, 8) == <<130, 227, 129, 132, 227, 129, 134>> 124 assert IO.binread(pid, 1) == :eof 125 assert StringIO.contents(pid) == {"", ""} 126 end 127 128 test "IO.write" do 129 {:ok, pid} = StringIO.open("") 130 assert IO.write(pid, "foo") == :ok 131 assert StringIO.contents(pid) == {"", "foo"} 132 end 133 134 test "IO.write with UTF-8" do 135 {:ok, pid} = StringIO.open("") 136 assert IO.write(pid, "あいう") == :ok 137 assert StringIO.contents(pid) == {"", "あいう"} 138 end 139 140 test "IO.write with non-printable arguments" do 141 {:ok, pid} = StringIO.open("") 142 143 assert_raise ArgumentError, fn -> 144 IO.write(pid, [<<1::1>>]) 145 end 146 end 147 148 test "IO.binwrite" do 149 {:ok, pid} = StringIO.open("") 150 assert IO.binwrite(pid, "foo") == :ok 151 assert StringIO.contents(pid) == {"", "foo"} 152 end 153 154 test "IO.binwrite with UTF-8" do 155 {:ok, pid} = StringIO.open("") 156 assert IO.binwrite(pid, "あいう") == :ok 157 158 binary = 159 <<195, 163, 194, 129, 194, 130, 195, 163>> <> 160 <<194, 129, 194, 132, 195, 163, 194, 129, 194, 134>> 161 162 assert StringIO.contents(pid) == {"", binary} 163 end 164 165 test "IO.binwrite with bytes" do 166 {:ok, pid} = StringIO.open("") 167 assert IO.binwrite(pid, <<127, 128>>) == :ok 168 assert StringIO.contents(pid) == {"", <<127, 194, 128>>} 169 end 170 171 test "IO.binwrite with bytes and latin1 encoding" do 172 {:ok, pid} = StringIO.open("", encoding: :latin1) 173 assert IO.binwrite(pid, <<127, 128>>) == :ok 174 assert StringIO.contents(pid) == {"", <<127, 128>>} 175 end 176 177 test "IO.puts" do 178 {:ok, pid} = StringIO.open("") 179 assert IO.puts(pid, "abc") == :ok 180 assert StringIO.contents(pid) == {"", "abc\n"} 181 end 182 183 test "IO.puts with non-printable arguments" do 184 {:ok, pid} = StringIO.open("") 185 186 assert_raise ArgumentError, fn -> 187 IO.puts(pid, [<<1::1>>]) 188 end 189 end 190 191 test "IO.inspect" do 192 {:ok, pid} = StringIO.open("") 193 assert IO.inspect(pid, {}, []) == {} 194 assert StringIO.contents(pid) == {"", "{}\n"} 195 end 196 197 test "IO.getn" do 198 {:ok, pid} = StringIO.open("abc") 199 assert IO.getn(pid, ">", 2) == "ab" 200 assert StringIO.contents(pid) == {"c", ""} 201 end 202 203 test "IO.getn with UTF-8" do 204 {:ok, pid} = StringIO.open("あいう") 205 assert IO.getn(pid, ">", 2) == "あい" 206 assert StringIO.contents(pid) == {"う", ""} 207 end 208 209 test "IO.getn with invalid UTF-8" do 210 {:ok, pid} = StringIO.open(<<130, 227, 129, 132, 227, 129, 134>>) 211 assert IO.getn(pid, ">", 2) == {:error, :invalid_unicode} 212 assert StringIO.contents(pid) == {<<130, 227, 129, 132, 227, 129, 134>>, ""} 213 end 214 215 test "IO.getn with capture_prompt" do 216 {:ok, pid} = StringIO.open("abc", capture_prompt: true) 217 assert IO.getn(pid, ">", 2) == "ab" 218 assert StringIO.contents(pid) == {"c", ">"} 219 end 220 221 test "IO.gets with \\n" do 222 {:ok, pid} = StringIO.open("abc\nd") 223 assert IO.gets(pid, ">") == "abc\n" 224 assert StringIO.contents(pid) == {"d", ""} 225 end 226 227 test "IO.gets with \\r\\n" do 228 {:ok, pid} = StringIO.open("abc\r\nd") 229 assert IO.gets(pid, ">") == "abc\n" 230 assert StringIO.contents(pid) == {"d", ""} 231 end 232 233 test "IO.gets without line breaks" do 234 {:ok, pid} = StringIO.open("abc") 235 assert IO.gets(pid, ">") == "abc" 236 assert StringIO.contents(pid) == {"", ""} 237 end 238 239 test "IO.gets with invalid UTF-8" do 240 {:ok, pid} = StringIO.open(<<130, 227, 129, 132, 227, 129, 134>>) 241 assert IO.gets(pid, ">") == {:error, :collect_line} 242 assert StringIO.contents(pid) == {<<130, 227, 129, 132, 227, 129, 134>>, ""} 243 end 244 245 test "IO.gets with capture_prompt" do 246 {:ok, pid} = StringIO.open("abc\n", capture_prompt: true) 247 assert IO.gets(pid, ">") == "abc\n" 248 assert StringIO.contents(pid) == {"", ">"} 249 end 250 251 test ":io.get_password" do 252 {:ok, pid} = StringIO.open("abc\n") 253 assert :io.get_password(pid) == "abc\n" 254 assert StringIO.contents(pid) == {"", ""} 255 end 256 257 test "IO.stream" do 258 {:ok, pid} = StringIO.open("abc") 259 assert IO.stream(pid, 2) |> Enum.to_list() == ["ab", "c"] 260 assert StringIO.contents(pid) == {"", ""} 261 end 262 263 test "IO.stream with invalid UTF-8" do 264 {:ok, pid} = StringIO.open(<<130, 227, 129, 132, 227, 129, 134>>) 265 266 assert_raise IO.StreamError, fn -> 267 IO.stream(pid, 2) |> Enum.to_list() 268 end 269 270 assert StringIO.contents(pid) == {<<130, 227, 129, 132, 227, 129, 134>>, ""} 271 end 272 273 test "IO.binstream" do 274 {:ok, pid} = StringIO.open("abc") 275 assert IO.stream(pid, 2) |> Enum.to_list() == ["ab", "c"] 276 assert StringIO.contents(pid) == {"", ""} 277 end 278 279 defp get_until(pid, encoding, prompt, module, function) do 280 :io.request(pid, {:get_until, encoding, prompt, module, function, []}) 281 end 282 283 defmodule GetUntilCallbacks do 284 def until_eof(continuation, :eof) do 285 {:done, continuation, :eof} 286 end 287 288 def until_eof(continuation, content) do 289 {:more, continuation ++ content} 290 end 291 292 def until_eof_then_try_more('magic-stop-prefix' ++ continuation, :eof) do 293 {:done, continuation, :eof} 294 end 295 296 def until_eof_then_try_more(continuation, :eof) do 297 {:more, 'magic-stop-prefix' ++ continuation} 298 end 299 300 def until_eof_then_try_more(continuation, content) do 301 {:more, continuation ++ content} 302 end 303 304 def up_to_3_bytes(continuation, :eof) do 305 {:done, continuation, :eof} 306 end 307 308 def up_to_3_bytes(continuation, content) do 309 case continuation ++ content do 310 [a, b, c | tail] -> {:done, [a, b, c], tail} 311 str -> {:more, str} 312 end 313 end 314 315 def up_to_3_bytes_discard_rest(continuation, :eof) do 316 {:done, continuation, :eof} 317 end 318 319 def up_to_3_bytes_discard_rest(continuation, content) do 320 case continuation ++ content do 321 [a, b, c | _tail] -> {:done, [a, b, c], :eof} 322 str -> {:more, str} 323 end 324 end 325 end 326 327 test "get_until with up_to_3_bytes" do 328 {:ok, pid} = StringIO.open("abcdefg") 329 result = get_until(pid, :unicode, "", GetUntilCallbacks, :up_to_3_bytes) 330 assert result == "abc" 331 assert IO.read(pid, :all) == "defg" 332 end 333 334 test "get_until with up_to_3_bytes_discard_rest" do 335 {:ok, pid} = StringIO.open("abcdefg") 336 result = get_until(pid, :unicode, "", GetUntilCallbacks, :up_to_3_bytes_discard_rest) 337 assert result == "abc" 338 assert IO.read(pid, :all) == "" 339 end 340 341 test "get_until with until_eof" do 342 {:ok, pid} = StringIO.open("abc\nd") 343 result = get_until(pid, :unicode, "", GetUntilCallbacks, :until_eof) 344 assert result == "abc\nd" 345 end 346 347 test "get_until with until_eof and \\r\\n" do 348 {:ok, pid} = StringIO.open("abc\r\nd") 349 result = get_until(pid, :unicode, "", GetUntilCallbacks, :until_eof) 350 assert result == "abc\r\nd" 351 end 352 353 test "get_until with until_eof capturing prompt" do 354 {:ok, pid} = StringIO.open("abc\nd", capture_prompt: true) 355 result = get_until(pid, :unicode, ">", GetUntilCallbacks, :until_eof) 356 assert result == "abc\nd" 357 assert StringIO.contents(pid) == {"", ">>>"} 358 end 359 360 test "get_until with until_eof_then_try_more" do 361 {:ok, pid} = StringIO.open("abc\nd") 362 result = get_until(pid, :unicode, "", GetUntilCallbacks, :until_eof_then_try_more) 363 assert result == "abc\nd" 364 end 365 366 test "get_until with invalid UTF-8" do 367 {:ok, pid} = StringIO.open(<<130, 227, 129, 132, 227, 129, 134>>) 368 result = get_until(pid, :unicode, "", GetUntilCallbacks, :until_eof) 369 assert result == :error 370 end 371 372 test "get_until with raw bytes (latin1)" do 373 {:ok, pid} = StringIO.open(<<181, 255, 194, ?\n>>) 374 result = get_until(pid, :latin1, "", GetUntilCallbacks, :until_eof) 375 assert result == <<181, 255, 194, ?\n>> 376 end 377 378 test ":io.erl_scan_form/2" do 379 {:ok, pid} = StringIO.open("1.") 380 result = :io.scan_erl_form(pid, 'p>') 381 assert result == {:ok, [{:integer, 1, 1}, {:dot, 1}], 1} 382 assert StringIO.contents(pid) == {"", ""} 383 end 384 385 test ":io.erl_scan_form/2 with capture_prompt" do 386 {:ok, pid} = StringIO.open("1.", capture_prompt: true) 387 result = :io.scan_erl_form(pid, 'p>') 388 assert result == {:ok, [{:integer, 1, 1}, {:dot, 1}], 1} 389 assert StringIO.contents(pid) == {"", "p>p>"} 390 end 391end 392