adsp 1.0.2 → 1.0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/{test → lib/adsp/test}/common.rb +4 -1
  3. data/{test → lib/adsp/test}/coverage_helper.rb +0 -0
  4. data/lib/adsp/test/file.rb +120 -0
  5. data/{test → lib/adsp/test}/minitest.rb +1 -0
  6. data/{test → lib/adsp/test}/mock/common.rb +1 -0
  7. data/{test → lib/adsp/test}/mock/file.rb +1 -0
  8. data/{test → lib/adsp/test}/mock/stream/raw/compressor.rb +1 -0
  9. data/{test → lib/adsp/test}/mock/stream/raw/decompressor.rb +1 -0
  10. data/{test → lib/adsp/test}/mock/stream/raw/native_compressor.rb +1 -0
  11. data/{test → lib/adsp/test}/mock/stream/raw/native_decompressor.rb +1 -0
  12. data/{test → lib/adsp/test}/mock/stream/reader.rb +1 -0
  13. data/{test → lib/adsp/test}/mock/stream/writer.rb +1 -0
  14. data/{test → lib/adsp/test}/mock/string.rb +1 -0
  15. data/{test → lib/adsp/test}/option.rb +1 -0
  16. data/{test → lib/adsp/test}/stream/abstract.rb +1 -0
  17. data/lib/adsp/test/stream/minitar.rb +50 -0
  18. data/{test → lib/adsp/test}/stream/raw/abstract.rb +1 -0
  19. data/lib/adsp/test/stream/raw/compressor.rb +165 -0
  20. data/lib/adsp/test/stream/raw/decompressor.rb +165 -0
  21. data/lib/adsp/test/stream/reader.rb +642 -0
  22. data/lib/adsp/test/stream/reader_helpers.rb +421 -0
  23. data/lib/adsp/test/stream/writer.rb +609 -0
  24. data/lib/adsp/test/stream/writer_helpers.rb +267 -0
  25. data/lib/adsp/test/string.rb +95 -0
  26. data/{test → lib/adsp/test}/validation.rb +7 -0
  27. data/lib/adsp/test/version.rb +18 -0
  28. data/lib/adsp/version.rb +1 -1
  29. data/test/file.test.rb +3 -116
  30. data/test/stream/minitar.test.rb +3 -46
  31. data/test/stream/raw/compressor.test.rb +3 -162
  32. data/test/stream/raw/decompressor.test.rb +3 -162
  33. data/test/stream/reader.test.rb +3 -639
  34. data/test/stream/reader_helpers.test.rb +3 -417
  35. data/test/stream/writer.test.rb +3 -606
  36. data/test/stream/writer_helpers.test.rb +3 -263
  37. data/test/string.test.rb +3 -91
  38. data/test/version.test.rb +3 -14
  39. metadata +28 -19
@@ -1,610 +1,7 @@
1
1
  # Abstract data stream processor.
2
2
  # Copyright (c) 2021 AUTHORS, MIT License.
3
3
 
4
- require "set"
5
- require "socket"
6
- require "stringio"
4
+ require "adsp/test/minitest"
5
+ require "adsp/test/stream/writer"
7
6
 
8
- require_relative "abstract"
9
- require_relative "../common"
10
- require_relative "../minitest"
11
- require_relative "../option"
12
- require_relative "../mock/stream/writer"
13
- require_relative "../mock/string"
14
-
15
- module ADSP
16
- module Test
17
- module Stream
18
- class Writer < Abstract
19
- Target = Mock::Stream::Writer
20
- String = Mock::String
21
-
22
- ARCHIVE_PATH = Common::ARCHIVE_PATH
23
- ENCODINGS = Common::ENCODINGS
24
- TRANSCODE_OPTIONS = Common::TRANSCODE_OPTIONS
25
- TEXTS = Common::TEXTS
26
- LARGE_TEXTS = Common::LARGE_TEXTS
27
- PORTION_LENGTHS = Common::PORTION_LENGTHS
28
- LARGE_PORTION_LENGTHS = Common::LARGE_PORTION_LENGTHS
29
-
30
- BUFFER_LENGTH_NAMES = %i[destination_buffer_length].freeze
31
- BUFFER_LENGTH_MAPPING = { :destination_buffer_length => :destination_buffer_length }.freeze
32
- FINISH_MODES = OCG.new(
33
- :flush_nonblock => Option::BOOLS,
34
- :close_nonblock => Option::BOOLS
35
- )
36
- .freeze
37
-
38
- NONBLOCK_SERVER_MODES = {
39
- :request => 0,
40
- :response => 1
41
- }
42
- .freeze
43
-
44
- NONBLOCK_SERVER_TIMEOUT = 0.1
45
-
46
- def initialize(*args)
47
- super(*args)
48
-
49
- @nonblock_client_lock = ::Mutex.new
50
- @nonblock_client_id = 0
51
- end
52
-
53
- def test_invalid_initialize
54
- get_invalid_compressor_options do |invalid_options|
55
- assert_raises ValidateError do
56
- target.new ::StringIO.new, invalid_options
57
- end
58
- end
59
-
60
- super
61
- end
62
-
63
- # -- synchronous --
64
-
65
- def test_invalid_write
66
- instance = target.new Validation::StringIOWithoutWrite.new
67
-
68
- assert_raises ValidateError do
69
- instance.write ""
70
- end
71
-
72
- assert_raises ValidateError do
73
- instance.flush
74
- end
75
-
76
- assert_raises ValidateError do
77
- instance.rewind
78
- end
79
-
80
- assert_raises ValidateError do
81
- instance.close
82
- end
83
- end
84
-
85
- def test_write
86
- parallel_compressor_options do |compressor_options|
87
- TEXTS.each do |text|
88
- PORTION_LENGTHS.each do |portion_length|
89
- sources = get_sources text, portion_length
90
- io = ::StringIO.new
91
- instance = target.new io, compressor_options
92
-
93
- begin
94
- sources.each_slice 2 do |current_sources|
95
- instance.write(*current_sources)
96
- instance.flush
97
- end
98
-
99
- assert_equal instance.pos, text.bytesize
100
- assert_equal instance.pos, instance.tell
101
- ensure
102
- refute_predicate instance, :closed?
103
- instance.close
104
- assert_predicate instance, :closed?
105
- end
106
-
107
- compressed_text = io.string
108
-
109
- get_compatible_decompressor_options compressor_options do |decompressor_options|
110
- check_text text, compressed_text, decompressor_options
111
- end
112
- end
113
- end
114
- end
115
- end
116
-
117
- def test_write_with_large_texts
118
- options_generator = OCG.new(
119
- :text => LARGE_TEXTS,
120
- :portion_length => LARGE_PORTION_LENGTHS
121
- )
122
-
123
- Common.parallel_options options_generator do |options|
124
- text = options[:text]
125
- portion_length = options[:portion_length]
126
-
127
- sources = get_sources text, portion_length
128
- io = ::StringIO.new
129
- instance = target.new io
130
-
131
- begin
132
- sources.each_slice 2 do |current_sources|
133
- instance.write(*current_sources)
134
- instance.flush
135
- end
136
- ensure
137
- instance.close
138
- end
139
-
140
- compressed_text = io.string
141
- check_text text, compressed_text
142
- end
143
- end
144
-
145
- def test_encoding
146
- parallel_compressor_options do |compressor_options|
147
- TEXTS.each do |text|
148
- # We don't need to transcode between same encodings.
149
- (ENCODINGS - [text.encoding]).each do |external_encoding|
150
- target_text = text.encode external_encoding, **TRANSCODE_OPTIONS
151
- io = ::StringIO.new
152
-
153
- instance = target.new(
154
- io,
155
- compressor_options,
156
- :external_encoding => external_encoding,
157
- :transcode_options => TRANSCODE_OPTIONS
158
- )
159
-
160
- assert_equal external_encoding, instance.external_encoding
161
- assert_equal TRANSCODE_OPTIONS, instance.transcode_options
162
-
163
- begin
164
- instance.set_encoding external_encoding, nil, TRANSCODE_OPTIONS
165
- assert_equal external_encoding, instance.external_encoding
166
- assert_equal TRANSCODE_OPTIONS, instance.transcode_options
167
-
168
- instance.write text
169
- ensure
170
- instance.close
171
- end
172
-
173
- compressed_text = io.string
174
-
175
- get_compatible_decompressor_options compressor_options do |decompressor_options|
176
- check_text target_text, compressed_text, decompressor_options
177
- assert_predicate target_text, :valid_encoding?
178
- end
179
- end
180
- end
181
- end
182
- end
183
-
184
- def test_rewind
185
- parallel_compressor_options do |compressor_options, worker_index|
186
- archive_path = Common.get_path ARCHIVE_PATH, worker_index
187
-
188
- compressed_texts = []
189
-
190
- ::File.open archive_path, "wb" do |file|
191
- instance = target.new file, compressor_options
192
-
193
- begin
194
- TEXTS.each do |text|
195
- instance.write text
196
- instance.flush
197
-
198
- assert_equal instance.pos, text.bytesize
199
- assert_equal instance.pos, instance.tell
200
-
201
- assert_equal 0, instance.rewind
202
-
203
- compressed_texts << ::File.read(archive_path, :mode => "rb")
204
-
205
- assert_equal 0, instance.pos
206
- assert_equal instance.pos, instance.tell
207
-
208
- file.truncate 0
209
- end
210
- ensure
211
- instance.close
212
- end
213
- end
214
-
215
- TEXTS.each.with_index do |text, index|
216
- compressed_text = compressed_texts[index]
217
-
218
- get_compatible_decompressor_options compressor_options do |decompressor_options|
219
- check_text text, compressed_text, decompressor_options
220
- end
221
- end
222
- end
223
- end
224
-
225
- # -- asynchronous --
226
-
227
- def test_invalid_write_nonblock
228
- instance = target.new Validation::StringIOWithoutWriteNonblock.new
229
-
230
- assert_raises ValidateError do
231
- instance.write_nonblock ""
232
- end
233
-
234
- assert_raises ValidateError do
235
- instance.flush_nonblock
236
- end
237
-
238
- assert_raises ValidateError do
239
- instance.close_nonblock
240
- end
241
- end
242
-
243
- def test_write_nonblock
244
- nonblock_server do |server|
245
- parallel_compressor_options do |compressor_options|
246
- TEXTS.each do |text|
247
- PORTION_LENGTHS.each do |portion_length|
248
- sources = get_sources text, portion_length
249
-
250
- FINISH_MODES.each do |finish_mode|
251
- nonblock_test server, text, portion_length, compressor_options do |instance, socket|
252
- # write
253
-
254
- sources.each.with_index do |source, index|
255
- if index.even?
256
- loop do
257
- begin
258
- bytes_written = instance.write_nonblock source
259
- rescue ::IO::WaitWritable
260
- socket.wait_writable
261
- retry
262
- end
263
-
264
- source = source.byteslice bytes_written, source.bytesize - bytes_written
265
- break if source.bytesize.zero?
266
- end
267
- else
268
- instance.write source
269
- end
270
- end
271
-
272
- # flush
273
-
274
- if finish_mode[:flush_nonblock]
275
- loop do
276
- begin
277
- is_flushed = instance.flush_nonblock
278
- rescue ::IO::WaitWritable
279
- socket.wait_writable
280
- retry
281
- end
282
-
283
- break if is_flushed
284
- end
285
- else
286
- instance.flush
287
- end
288
-
289
- assert_equal instance.pos, text.bytesize
290
- assert_equal instance.pos, instance.tell
291
-
292
- ensure
293
- # close
294
-
295
- refute_predicate instance, :closed?
296
-
297
- if finish_mode[:close_nonblock]
298
- loop do
299
- begin
300
- is_closed = instance.close_nonblock
301
- rescue ::IO::WaitWritable
302
- socket.wait_writable
303
- retry
304
- end
305
-
306
- break if is_closed
307
- end
308
- else
309
- instance.close
310
- end
311
-
312
- assert_predicate instance, :closed?
313
- end
314
- end
315
- end
316
- end
317
- end
318
- end
319
- end
320
-
321
- def test_write_nonblock_with_large_texts
322
- nonblock_server do |server|
323
- Common.parallel LARGE_TEXTS do |text|
324
- LARGE_PORTION_LENGTHS.each do |portion_length|
325
- sources = get_sources text, portion_length
326
-
327
- FINISH_MODES.each do |finish_mode|
328
- nonblock_test server, text, portion_length do |instance, socket|
329
- # write
330
-
331
- sources.each.with_index do |source, index|
332
- if index.even?
333
- loop do
334
- begin
335
- bytes_written = instance.write_nonblock source
336
- rescue ::IO::WaitWritable
337
- socket.wait_writable
338
- retry
339
- end
340
-
341
- source = source.byteslice bytes_written, source.bytesize - bytes_written
342
- break if source.bytesize.zero?
343
- end
344
- else
345
- instance.write source
346
- end
347
- end
348
-
349
- # flush
350
-
351
- if finish_mode[:flush_nonblock]
352
- loop do
353
- begin
354
- is_flushed = instance.flush_nonblock
355
- rescue ::IO::WaitWritable
356
- socket.wait_writable
357
- retry
358
- end
359
-
360
- break if is_flushed
361
- end
362
- else
363
- instance.flush
364
- end
365
-
366
- ensure
367
- # close
368
-
369
- if finish_mode[:close_nonblock]
370
- loop do
371
- begin
372
- is_closed = instance.close_nonblock
373
- rescue ::IO::WaitWritable
374
- socket.wait_writable
375
- retry
376
- end
377
-
378
- break if is_closed
379
- end
380
- else
381
- instance.close
382
- end
383
- end
384
- end
385
- end
386
- end
387
- end
388
- end
389
-
390
- def test_invalid_rewind_nonblock
391
- instance = target.new Validation::StringIOWithoutWriteNonblock.new
392
-
393
- assert_raises ValidateError do
394
- instance.rewind_nonblock
395
- end
396
- end
397
-
398
- def test_rewind_nonblock
399
- return unless Common.file_can_be_used_nonblock?
400
-
401
- parallel_compressor_options do |compressor_options, worker_index|
402
- archive_path = Common.get_path ARCHIVE_PATH, worker_index
403
-
404
- compressed_texts = []
405
-
406
- ::File.open archive_path, "wb" do |file|
407
- instance = target.new file, compressor_options
408
-
409
- begin
410
- TEXTS.each do |text|
411
- instance.write text
412
- instance.flush
413
-
414
- assert_equal instance.pos, text.bytesize
415
- assert_equal instance.pos, instance.tell
416
-
417
- loop do
418
- begin
419
- is_rewinded = instance.rewind_nonblock
420
- rescue ::IO::WaitWritable
421
- file.wait_writable
422
- retry
423
- end
424
-
425
- break if is_rewinded
426
- end
427
-
428
- compressed_texts << ::File.read(archive_path, :mode => "rb")
429
-
430
- assert_equal 0, instance.pos
431
- assert_equal instance.pos, instance.tell
432
-
433
- file.truncate 0
434
- end
435
- ensure
436
- instance.close
437
- end
438
- end
439
-
440
- TEXTS.each.with_index do |text, index|
441
- compressed_text = compressed_texts[index]
442
-
443
- get_compatible_decompressor_options compressor_options do |decompressor_options|
444
- check_text text, compressed_text, decompressor_options
445
- end
446
- end
447
- end
448
- end
449
-
450
- # -- nonblock test --
451
-
452
- protected def nonblock_server
453
- # We need to test close nonblock.
454
- # This method writes remaining data and closes socket.
455
- # Server is not able to send response immediately.
456
- # Client has to reconnect to server once again.
457
-
458
- ::TCPServer.open 0 do |server|
459
- # Server loop will be processed in separate (parent) thread.
460
- # Child threads will be collected for later usage.
461
- child_lock = ::Mutex.new
462
- child_threads = ::Set.new
463
-
464
- # Server need to maintain mapping between client id and result.
465
- results_lock = ::Mutex.new
466
- results = {}
467
-
468
- parent_thread = ::Thread.new do
469
- loop do
470
- child_thread = ::Thread.start server.accept do |socket|
471
- # Reading head.
472
- client_id, portion_length, mode = socket.read(13).unpack "NQC"
473
-
474
- if mode == NONBLOCK_SERVER_MODES[:request]
475
- # Reading result from client.
476
- result = "".b
477
-
478
- loop do
479
- result << socket.read_nonblock(portion_length)
480
- rescue ::IO::WaitReadable
481
- socket.wait_readable
482
- rescue ::EOFError
483
- break
484
- end
485
-
486
- # Saving result for client.
487
- results_lock.synchronize { results[client_id] = result }
488
-
489
- next
490
- end
491
-
492
- loop do
493
- # Waiting when result will be ready.
494
- result = results_lock.synchronize { results[client_id] }
495
-
496
- unless result.nil?
497
- # Sending result to client.
498
- socket.write result
499
-
500
- break
501
- end
502
-
503
- sleep NONBLOCK_SERVER_TIMEOUT
504
- end
505
-
506
- # Removing result for client.
507
- results_lock.synchronize { results.delete client_id }
508
-
509
- ensure
510
- socket.close
511
-
512
- # Removing current child thread.
513
- child_lock.synchronize { child_threads.delete ::Thread.current }
514
- end
515
-
516
- # Adding new child thread.
517
- child_lock.synchronize { child_threads.add child_thread }
518
- end
519
- end
520
-
521
- # Processing client.
522
- begin
523
- yield server
524
- ensure
525
- # We need to kill parent thread when client has finished.
526
- # So server won't be able to create new child threads.
527
- # Than we can join all remaining child threads.
528
- parent_thread.kill.join
529
- child_threads.each(&:join)
530
- end
531
- end
532
- end
533
-
534
- protected def nonblock_test(server, text, portion_length, compressor_options = {}, &_block)
535
- port = server.addr[1]
536
- client_id = @nonblock_client_lock.synchronize { @nonblock_client_id += 1 }
537
-
538
- # Writing request.
539
- ::TCPSocket.open "localhost", port do |socket|
540
- # Writing head.
541
- head = [client_id, portion_length, NONBLOCK_SERVER_MODES[:request]].pack "NQC"
542
- socket.write head
543
-
544
- # Instance is going to write compressed text.
545
- instance = target.new socket, compressor_options
546
-
547
- begin
548
- yield instance, socket
549
- ensure
550
- instance.close
551
- end
552
- end
553
-
554
- # Reading response.
555
- compressed_text = ::TCPSocket.open "localhost", port do |socket|
556
- # Writing head.
557
- head = [client_id, portion_length, NONBLOCK_SERVER_MODES[:response]].pack "NQC"
558
- socket.write head
559
-
560
- # Reading compressed text.
561
- socket.read
562
- end
563
-
564
- # Testing compressed text.
565
- if compressor_options.empty?
566
- check_text text, compressed_text
567
- else
568
- get_compatible_decompressor_options compressor_options do |decompressor_options|
569
- check_text text, compressed_text, decompressor_options
570
- end
571
- end
572
- end
573
-
574
- # -----
575
-
576
- protected def get_sources(text, portion_length)
577
- sources = text
578
- .chars
579
- .each_slice(portion_length)
580
- .map(&:join)
581
-
582
- return ["".b] if sources.empty?
583
-
584
- sources
585
- end
586
-
587
- protected def check_text(text, compressed_text, decompressor_options = {})
588
- decompressed_text = String.decompress compressed_text, decompressor_options
589
- decompressed_text.force_encoding text.encoding
590
-
591
- assert_equal text, decompressed_text
592
- end
593
-
594
- def get_invalid_compressor_options(&block)
595
- Option.get_invalid_compressor_options BUFFER_LENGTH_NAMES, &block
596
- end
597
-
598
- def parallel_compressor_options(&block)
599
- Common.parallel_options Option.get_compressor_options_generator(BUFFER_LENGTH_NAMES), &block
600
- end
601
-
602
- def get_compatible_decompressor_options(compressor_options, &block)
603
- Option.get_compatible_decompressor_options compressor_options, BUFFER_LENGTH_MAPPING, &block
604
- end
605
- end
606
-
607
- Minitest << Writer
608
- end
609
- end
610
- end
7
+ Minitest << ADSP::Test::Stream::Writer