adsp 1.0.2 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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