benry-unixcmd 0.9.0

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.
data/test/run_all.rb ADDED
@@ -0,0 +1,14 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ ###
4
+ ### $Release: 0.9.0 $
5
+ ### $Copyright: copyright(c) 2021 kuwata-lab.com all rights reserved $
6
+ ### $License: MIT License $
7
+ ###
8
+
9
+ here = File.dirname(File.expand_path(__FILE__))
10
+ lib = File.join(File.dirname(here), 'lib')
11
+ $LOAD_PATH << lib unless $LOAD_PATH.include?(lib)
12
+ Dir.glob(here + '/**/*_test.rb').each do |fpath|
13
+ require fpath
14
+ end
@@ -0,0 +1,2305 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ libpath = File.class_eval { join(dirname(dirname(__FILE__)), 'lib') }
4
+ $LOAD_PATH << libpath unless $LOAD_PATH.include?(libpath)
5
+
6
+ require 'oktest'
7
+ require 'stringio'
8
+ require 'etc'
9
+
10
+ require 'benry/unixcmd'
11
+ require 'fileutils'
12
+
13
+
14
+ Oktest.scope do
15
+
16
+
17
+ topic Benry::UnixCommand do
18
+ include Benry::UnixCommand
19
+
20
+ before_all do
21
+ FileUtils.rm_rf "tmpdir"
22
+ FileUtils.mkdir "tmpdir"
23
+ end
24
+
25
+ after_all do
26
+ FileUtils.rm_rf "tmpdir"
27
+ end
28
+
29
+ before do
30
+ @_backto = Dir.pwd
31
+ Dir.chdir "tmpdir"
32
+ File.write("foo1.txt", "FOO1")
33
+ File.write("foo2.txt", "FOO2")
34
+ FileUtils.mkdir "d1"
35
+ File.write("d1/bar.txt", "BAR")
36
+ FileUtils.mkdir "d1/d2"
37
+ File.write("d1/d2/baz.txt", "BAZ")
38
+ end
39
+
40
+ after do
41
+ Dir.chdir @_backto
42
+ FileUtils.rm_rf Dir.glob("tmpdir/*")
43
+ end
44
+
45
+ def same_content?(file1, file2)
46
+ ok {File.read(file1)} == File.read(file2)
47
+ end
48
+
49
+ def capture
50
+ bkup = [$stdout, $stderr]
51
+ $stdout = sout = StringIO.new
52
+ $stderr = serr = StringIO.new
53
+ yield sout, serr
54
+ return sout.string, serr.string
55
+ ensure
56
+ $stdout, $stderr = bkup
57
+ end
58
+
59
+
60
+ topic 'prompt()' do
61
+ spec "[!uilyk] returns prompt string." do
62
+ ok {prompt()} == "$"
63
+ end
64
+ end
65
+
66
+ topic 'prompt!()' do
67
+ spec "[!q992e] adds indentation after prompt." do
68
+ ok {prompt!(0)} == "$ "
69
+ ok {prompt!(1)} == "$ "
70
+ ok {prompt!(2)} == "$ "
71
+ ok {prompt!(3)} == "$ "
72
+ end
73
+ end
74
+
75
+
76
+ topic 'echoback()' do
77
+ spec "[!x7atu] prints argument string into $stdout with prompt." do
78
+ sout, serr = capture_sio() do
79
+ echoback "foo bar baz"
80
+ end
81
+ ok {sout} == "$ foo bar baz\n"
82
+ ok {serr} == ""
83
+ end
84
+ end
85
+
86
+
87
+ topic 'echo()' do
88
+ spec "[!mzbdj] echoback command arguments." do
89
+ sout, serr = capture_sio do
90
+ echo "foo", "bar"
91
+ end
92
+ ok {sout} == ("$ echo foo bar\n"\
93
+ "foo bar\n")
94
+ end
95
+ spec "[!cjggd] prints arguments." do
96
+ sout, serr = capture_sio do
97
+ echo "abc", 123, true, nil
98
+ end
99
+ ok {sout} == ("$ echo abc 123 true \n"\
100
+ "abc 123 true \n")
101
+ end
102
+ spec "[!vhpw3] not print newline at end if '-n' option specified." do
103
+ sout, serr = capture_sio do
104
+ echo :n, "abc"
105
+ end
106
+ ok {sout} == ("$ echo -n abc\n"\
107
+ "abc")
108
+ end
109
+ end
110
+
111
+
112
+ topic 'sys()' do
113
+ spec "[!rqe7a] echoback command and arguments." do
114
+ sout, serr = capture_sio do
115
+ sys "echo foo bar >/dev/null"
116
+ end
117
+ ok {sout} == "$ echo foo bar >/dev/null\n"
118
+ end
119
+ spec "[!agntr] returns process status if command succeeded." do
120
+ sout, serr = capture_sio do
121
+ ret = sys "echo foo bar >/dev/null"
122
+ ok {ret}.is_a?(Process::Status)
123
+ ok {ret.exitstatus} == 0
124
+ end
125
+ end
126
+ spec "[!clfig] yields block if command failed." do
127
+ sout, serr = capture_sio do
128
+ called = false
129
+ stat = sys "false" do |stat|
130
+ called = true
131
+ end
132
+ ok {called} == true
133
+ ok {stat.exitstatus} == 1
134
+ end
135
+ end
136
+ spec "[!deu3e] not yield block if command succeeded." do
137
+ sout, serr = capture_sio do
138
+ called = false
139
+ ret = sys "true" do |stat|
140
+ called = true
141
+ end
142
+ ok {called} == false
143
+ ok {ret.exitstatus} == 0
144
+ end
145
+ end
146
+ spec "[!chko8] block argument is process status." do
147
+ sout, serr = capture_sio do
148
+ arg = nil
149
+ ret = sys "false" do |stat|
150
+ arg = stat
151
+ true
152
+ end
153
+ ok {arg}.is_a?(Process::Status)
154
+ ok {arg}.same?(ret)
155
+ end
156
+ end
157
+ spec "[!0yy6r] (sys) not raise error if block result is truthy" do
158
+ sout, serr = capture_sio do
159
+ pr = proc { sys "false" do true end }
160
+ ok {pr}.NOT.raise?(ArgumentError)
161
+ pr = proc { sys "false" do false end }
162
+ ok {pr}.raise?(RuntimeError, "Command failed with status (1): false")
163
+ end
164
+ end
165
+ spec "[!xsspi] (sys) raises error if command failed." do
166
+ sout, serr = capture_sio do
167
+ pr = proc { sys "grep -q ^FOOBAR foo1.txt" }
168
+ ok {pr}.raise?(RuntimeError, "Command failed with status (1): grep -q ^FOOBAR foo1.txt")
169
+ end
170
+ end
171
+ end
172
+
173
+ topic 'sys!()' do
174
+ spec "[!tbfii] (sys!) returns process status if command failed." do
175
+ sout, serr = capture_sio do
176
+ ret = sys! "grep -q ^FOOBAR foo1.txt"
177
+ ok {ret}.is_a?(Process::Status)
178
+ ok {ret.exitstatus} == 1
179
+ end
180
+ end
181
+ end
182
+
183
+
184
+ topic 'ruby()' do
185
+ spec "[!98qro] echoback command and args." do
186
+ sout, serr = capture_sio do
187
+ ruby "-e", "x=0"
188
+ end
189
+ ok {sout} == "$ #{RbConfig.ruby} -e x=0\n"
190
+ end
191
+ spec "[!u5f5l] run ruby command." do
192
+ sout, serr = capture_sio do
193
+ ruby "-e 'File.write(\"out1\", \"ABC\")'"
194
+ ruby "-e", "File.write(\"out2\", \"XYZ\")"
195
+ end
196
+ ok {sout} == ("$ #{RbConfig.ruby} -e 'File.write(\"out1\", \"ABC\")'\n"\
197
+ "$ #{RbConfig.ruby} -e File.write(\"out2\", \"XYZ\")\n")
198
+ ok {File.read("out1")} == "ABC"
199
+ ok {File.read("out2")} == "XYZ"
200
+ end
201
+ spec "[!2jano] returns process status object if ruby command succeeded." do
202
+ sout, serr = capture_sio do
203
+ ret = ruby "-e", "x = 1"
204
+ ok {ret}.is_a?(Process::Status)
205
+ ok {ret.exitstatus} == 0
206
+ end
207
+ end
208
+ spec "[!69clt] (ruby) error when ruby command failed." do
209
+ sout, serr = capture_sio do
210
+ pr = proc { ruby "-e '1/0' 2> err1" }
211
+ ok {pr}.raise?(RuntimeError, "Command failed with status (1): #{RbConfig.ruby} -e '1/0' 2> err1")
212
+ ok {File.read("err1")} =~ /ZeroDivisionError/
213
+ end
214
+ end
215
+ end
216
+
217
+ topic 'ruby!()' do
218
+ spec "[!z1f03] (ruby!) ignores error even when ruby command failed." do
219
+ sout, serr = capture_sio do
220
+ ret = nil
221
+ pr = proc { ret = ruby! "-e '1/0' 2> err1" }
222
+ ok {pr}.NOT.raise?(RuntimeError)
223
+ ok {File.read("err1")} =~ /ZeroDivisionError/
224
+ ok {ret}.is_a?(Process::Status)
225
+ ok {ret.exitstatus} == 1
226
+ end
227
+ end
228
+ end
229
+
230
+
231
+ topic 'popen2()', tag: 'open3' do
232
+ spec "[!8que2] calls 'Open3.popen2()'." do
233
+ expected = (" 1 AA\n"\
234
+ " 2 BB\n"\
235
+ " 3 CC\n")
236
+ #
237
+ sout, serr = capture_sio do
238
+ arr = popen2("cat -n")
239
+ ok {arr}.length(3)
240
+ stdin, stdout, wait_thread = arr
241
+ stdin.write("AA\nBB\nCC\n")
242
+ stdin.close()
243
+ output = stdout.read()
244
+ ok {output} == expected
245
+ end
246
+ ok {sout} == "$ cat -n\n"
247
+ ok {serr} == ""
248
+ #
249
+ sout, serr = capture_sio do
250
+ output2 = popen2("cat -n") do |*args|
251
+ ok {args}.length(3)
252
+ stdin, stdout, wait_thread = args
253
+ stdin.write("AA\nBB\nCC\n")
254
+ stdin.close()
255
+ stdout.read()
256
+ end
257
+ ok {output2} == expected
258
+ end
259
+ ok {sout} == "$ cat -n\n"
260
+ ok {serr} == ""
261
+ end
262
+ end
263
+
264
+ topic 'popen2e()', tag: 'open3' do
265
+ spec "[!s6g1r] calls 'Open3.popen2e()'." do
266
+ expected = (" 1 AA\n"\
267
+ " 2 BB\n"\
268
+ " 3 CC\n"\
269
+ " 0.00 real 0.00 user 0.00 sys\n")
270
+ #
271
+ sout, serr = capture_sio do
272
+ arr = popen2e("time cat -n")
273
+ ok {arr}.length(3)
274
+ stdin, stdout, wait_thread = arr
275
+ stdin.write("AA\nBB\nCC\n")
276
+ stdin.close()
277
+ output = stdout.read()
278
+ ok {output} == expected
279
+ end
280
+ ok {sout} == "$ time cat -n\n"
281
+ ok {serr} == ""
282
+ #
283
+ sout, serr = capture_sio do
284
+ output2 = popen2e("time cat -n") do |*args|
285
+ ok {args}.length(3)
286
+ stdin, stdout, wait_thread = args
287
+ stdin.write("AA\nBB\nCC\n")
288
+ stdin.close()
289
+ stdout.read()
290
+ end
291
+ ok {output2} == expected
292
+ end
293
+ ok {sout} == "$ time cat -n\n"
294
+ ok {serr} == ""
295
+ end
296
+ end
297
+
298
+ topic 'popen3()', tag: 'open3' do
299
+ spec "[!evlx7] calls 'Open3.popen3()'." do
300
+ expected1 = (" 1 AA\n"\
301
+ " 2 BB\n"\
302
+ " 3 CC\n")
303
+ expected2 = " 0.00 real 0.00 user 0.00 sys\n"
304
+ #
305
+ sout, serr = capture_sio do
306
+ arr = popen3("time cat -n")
307
+ ok {arr}.length(4)
308
+ stdin, stdout, stderr, wait_thread = arr
309
+ stdin.write("AA\nBB\nCC\n")
310
+ stdin.close()
311
+ ok {stdout.read()} == expected1
312
+ ok {stderr.read()} == expected2
313
+ end
314
+ ok {sout} == "$ time cat -n\n"
315
+ ok {serr} == ""
316
+ #
317
+ sout, serr = capture_sio do
318
+ output, error = popen3("time cat -n") do |*args|
319
+ ok {args}.length(4)
320
+ stdin, stdout, stderr, wait_thread = args
321
+ stdin.write("AA\nBB\nCC\n")
322
+ stdin.close()
323
+ [stdout.read(), stderr.read()]
324
+ end
325
+ ok {output} == expected1
326
+ ok {error} == expected2
327
+ end
328
+ ok {sout} == "$ time cat -n\n"
329
+ ok {serr} == ""
330
+ end
331
+ end
332
+
333
+ topic 'capture2()', tag: 'open3' do
334
+ spec "[!5p4dw] calls 'Open3.capture2()'." do
335
+ expected = (" 1 AA\n"\
336
+ " 2 BB\n"\
337
+ " 3 CC\n")
338
+ #
339
+ sout, serr = capture_sio do
340
+ output = capture2("cat -n", stdin_data: "AA\nBB\nCC\n")
341
+ ok {output} == expected
342
+ end
343
+ ok {sout} == "$ cat -n\n"
344
+ ok {serr} == ""
345
+ end
346
+ spec "[!2s1by] error when command failed." do
347
+ sout, serr = capture_sio do
348
+ pr = proc { capture2("grep -q FOOBAR foo1.txt") }
349
+ ok {pr}.raise?(RuntimeError, "Command failed with status (1): grep -q FOOBAR foo1.txt")
350
+ end
351
+ ok {sout} == "$ grep -q FOOBAR foo1.txt\n"
352
+ ok {serr} == ""
353
+ end
354
+ end
355
+
356
+ topic 'capture2e()', tag: 'open3' do
357
+ spec "[!jgn71] calls 'Open3.capture2e()'." do
358
+ expected = (" 1 AA\n"\
359
+ " 2 BB\n"\
360
+ " 3 CC\n"\
361
+ " 0.00 real 0.00 user 0.00 sys\n")
362
+ #
363
+ sout, serr = capture_sio do
364
+ output = capture2e("time cat -n", stdin_data: "AA\nBB\nCC\n")
365
+ ok {output} == expected
366
+ end
367
+ ok {sout} == "$ time cat -n\n"
368
+ ok {serr} == ""
369
+ end
370
+ spec "[!qr3ka] error when command failed." do
371
+ sout, serr = capture_sio do
372
+ pr = proc { capture2e("grep -q FOOBAR foo1.txt") }
373
+ ok {pr}.raise?(RuntimeError, "Command failed with status (1): grep -q FOOBAR foo1.txt")
374
+ end
375
+ ok {sout} == "$ grep -q FOOBAR foo1.txt\n"
376
+ ok {serr} == ""
377
+ end
378
+ end
379
+
380
+ topic 'capture3()', tag: 'open3' do
381
+ spec "[!n91rh] calls 'Open3.capture3()'." do
382
+ expected1 = (" 1 AA\n"\
383
+ " 2 BB\n"\
384
+ " 3 CC\n")
385
+ expected2 = " 0.00 real 0.00 user 0.00 sys\n"
386
+ #
387
+ sout, serr = capture_sio do
388
+ output, error = capture3("time cat -n", stdin_data: "AA\nBB\nCC\n")
389
+ ok {output} == expected1
390
+ ok {error} == expected2
391
+ end
392
+ ok {sout} == "$ time cat -n\n"
393
+ ok {serr} == ""
394
+ end
395
+ spec "[!thnyv] error when command failed." do
396
+ sout, serr = capture_sio do
397
+ pr = proc { capture3("grep -q FOOBAR foo1.txt") }
398
+ ok {pr}.raise?(RuntimeError, "Command failed with status (1): grep -q FOOBAR foo1.txt")
399
+ end
400
+ ok {sout} == "$ grep -q FOOBAR foo1.txt\n"
401
+ ok {serr} == ""
402
+ end
403
+ end
404
+
405
+ topic 'capture2!()', tag: 'open3' do
406
+ spec "[!357e1] ignore errors even if command failed." do
407
+ sout, serr = capture_sio do
408
+ output, process_status = capture2!("grep -q FOOBAR foo1.txt")
409
+ ok {process_status.exitstatus} == 1
410
+ ok {output} == ""
411
+ end
412
+ ok {sout} == "$ grep -q FOOBAR foo1.txt\n"
413
+ ok {serr} == ""
414
+ end
415
+ end
416
+
417
+ topic 'capture2e!()', tag: 'open3' do
418
+ spec "[!o0b7c] ignore errors even if command failed." do
419
+ sout, serr = capture_sio do
420
+ output, process_status = capture2e!("grep -q FOOBAR blabla.txt")
421
+ ok {process_status.exitstatus} == 2
422
+ ok {output} == "grep: blabla.txt: No such file or directory\n"
423
+ end
424
+ ok {sout} == "$ grep -q FOOBAR blabla.txt\n"
425
+ ok {serr} == ""
426
+ end
427
+ end
428
+
429
+ topic 'capture3()', tag: 'open3' do
430
+ spec "[!rwfiu] ignore errors even if command failed." do
431
+ sout, serr = capture_sio do
432
+ output, error, process_status = capture3!("grep -q FOOBAR blabla.txt")
433
+ ok {process_status.exitstatus} == 2
434
+ ok {output} == ""
435
+ ok {error} == "grep: blabla.txt: No such file or directory\n"
436
+ end
437
+ end
438
+ end
439
+
440
+
441
+ topic 'cd()' do
442
+ spec "[!gnmdg] expands file pattern." do
443
+ sout, serr = capture_sio do
444
+ here = Dir.pwd
445
+ cd "d?"
446
+ ok {Dir.pwd} == File.join(here, "d1")
447
+ end
448
+ end
449
+ spec "[!v7bn7] error when pattern not matched to any file." do
450
+ sout, serr = capture_sio do
451
+ pr = proc { cd "blabla*" }
452
+ ok {pr}.raise?(ArgumentError, "cd: blabla*: directory not found.")
453
+ end
454
+ end
455
+ spec "[!08wuv] error when pattern matched to multiple files." do
456
+ sout, serr = capture_sio do
457
+ pr = proc { cd "foo*" }
458
+ ok {pr}.raise?(ArgumentError, "cd: foo*: unexpectedly matched to multiple filenames (foo1.txt, foo2.txt).")
459
+ end
460
+ end
461
+ spec "[!hs7u8] error when argument is not a directory name." do
462
+ sout, serr = capture_sio do
463
+ pr = proc { cd "foo1.txt" }
464
+ ok {pr}.raise?(ArgumentError, "cd: foo1.txt: Not a directory.")
465
+ end
466
+ end
467
+ spec "[!cg5ns] changes current directory." do
468
+ here = Dir.pwd
469
+ begin
470
+ sout, serr = capture_sio() do
471
+ cd "d1/d2"
472
+ ok {Dir.pwd} == here + "/d1/d2"
473
+ end
474
+ ok {Dir.pwd} == here + "/d1/d2"
475
+ ok {sout} == <<-END.gsub(/^ */, '')
476
+ $ cd d1/d2
477
+ END
478
+ ok {serr} == ""
479
+ ensure
480
+ Dir.chdir(here)
481
+ end
482
+ end
483
+ spec "[!uit6q] if block given, then back to current dir." do
484
+ here = Dir.pwd
485
+ sout, serr = capture_sio() do
486
+ cd "d1" do
487
+ ok {Dir.pwd} == here + "/d1"
488
+ cd "d2" do
489
+ ok {Dir.pwd} == here + "/d1/d2"
490
+ end
491
+ ok {Dir.pwd} == here + "/d1"
492
+ end
493
+ ok {Dir.pwd} == here
494
+ end
495
+ ok {sout} == <<-END.gsub(/^ */, '')
496
+ $ cd d1
497
+ $ cd d2
498
+ $ cd -
499
+ $ cd -
500
+ END
501
+ ok {serr} == ""
502
+ end
503
+ spec "[!cg298] returns path before changing directory." do
504
+ here = Dir.pwd
505
+ path = nil
506
+ ret = nil
507
+ capture_sio() do
508
+ ret = cd "d1/d2" do
509
+ path = Dir.pwd
510
+ end
511
+ end
512
+ ok {ret} == here
513
+ ok {ret} != path
514
+ end
515
+ end
516
+
517
+
518
+ topic 'pushd()' do
519
+ spec "[!nvkha] expands file pattern." do
520
+ sout, serr = capture_sio do
521
+ here = Dir.pwd
522
+ pushd "d?" do
523
+ ok {Dir.pwd} == File.join(here, "d1")
524
+ end
525
+ end
526
+ end
527
+ spec "[!q3itn] error when pattern not matched to any file." do
528
+ sout, serr = capture_sio do
529
+ pr = proc { pushd "blabla*" do end }
530
+ ok {pr}.raise?(ArgumentError, "pushd: blabla*: directory not found.")
531
+ end
532
+ end
533
+ spec "[!hveaj] error when pattern matched to multiple files." do
534
+ sout, serr = capture_sio do
535
+ pr = proc { pushd "foo*" do end }
536
+ ok {pr}.raise?(ArgumentError, "pushd: foo*: unexpectedly matched to multiple filenames (foo1.txt, foo2.txt).")
537
+ end
538
+ end
539
+ spec "[!y6cq9] error when argument is not a directory name." do
540
+ sout, serr = capture_sio do
541
+ pr = proc { pushd "foo1.txt" do end }
542
+ ok {pr}.raise?(ArgumentError, "pushd: foo1.txt: Not a directory.")
543
+ end
544
+ end
545
+ #
546
+ spec "[!7ksfd] replaces home path with '~'." do
547
+ home = home2 = nil
548
+ sout, serr = capture_sio do
549
+ home = File.expand_path("~")
550
+ ok {home} != "~"
551
+ pushd home do
552
+ puts Dir.pwd
553
+ home2 = Dir.pwd
554
+ pushd "/" do
555
+ puts Dir.pwd
556
+ end
557
+ end
558
+ end
559
+ skip_when home != home2, "home directory may be a symbolic link"
560
+ ok {sout} =~ /^\$ popd \# back to ~$/
561
+ end
562
+ spec "[!xl6lg] raises error when block not given." do
563
+ pr = proc { pushd "d1/d2" }
564
+ ok {pr}.raise?(ArgumentError, "pushd: requires block argument.")
565
+ end
566
+ spec "[!rxtd0] changes directory and yields block." do
567
+ here = Dir.pwd
568
+ path = nil
569
+ sout, serr = capture_sio do
570
+ pushd "d1/d2" do
571
+ path = Dir.pwd
572
+ end
573
+ end
574
+ home = File.expand_path('~')
575
+ here2 = here.start_with?(home) ? here.sub(home, '~') : here
576
+ ok {path} != nil
577
+ ok {path} != here
578
+ ok {path} == File.join(here, "d1/d2")
579
+ ok {sout} == ("$ pushd d1/d2\n"\
580
+ "$ popd # back to #{here2}\n")
581
+ end
582
+ spec "[!9jszw] back to origin directory after yielding block." do
583
+ here = Dir.pwd
584
+ path = nil
585
+ sout, serr = capture_sio do
586
+ pushd "d1/d2" do
587
+ path = Dir.pwd
588
+ end
589
+ end
590
+ ok {path} != nil
591
+ ok {path} != here
592
+ ok {Dir.pwd} == here
593
+ end
594
+ end
595
+
596
+
597
+ topic 'cp()' do
598
+
599
+ spec "[!mtuec] echoback copy command and arguments." do
600
+ sout, serr = capture_sio do
601
+ cp "foo1.txt", "foo9.txt"
602
+ end
603
+ ok {sout} == "$ cp foo1.txt foo9.txt\n"
604
+ #
605
+ sout, serr = capture_sio do
606
+ cp :pr, "foo*.txt", to: "d1"
607
+ end
608
+ ok {sout} == "$ cp -pr foo*.txt d1\n"
609
+ end
610
+
611
+ case_when "[!u98f8] when `to:` keyword arg not specified..." do
612
+ spec "[!u39p0] error when number of arguments is not 2." do
613
+ sout, serr = capture_sio do
614
+ pr = proc { cp() }
615
+ ok {pr}.raise?(ArgumentError, "cp: requires two arguments.")
616
+ pr = proc { cp "foo1.txt" }
617
+ ok {pr}.raise?(ArgumentError, "cp: requires two arguments.")
618
+ pr = proc { cp "foo1.txt", "foo2.txt", "foo3.txt" }
619
+ ok {pr}.raise?(ArgumentError, "cp: too much arguments.")
620
+ end
621
+ end
622
+ spec "[!fux6x] error when source pattern matched to multiple files." do
623
+ sout, serr = capture_sio do
624
+ pr = proc { cp "foo?.txt", "blabla.txt" }
625
+ ok {pr}.raise?(ArgumentError, "cp: foo?.txt: unexpectedly matched to multiple files (foo1.txt, foo2.txt).")
626
+ end
627
+ end
628
+ spec "[!y74ux] error when destination pattern matched to multiple files." do
629
+ sout, serr = capture_sio do
630
+ pr = proc { cp "d1/bar.txt", "foo*.txt" }
631
+ ok {pr}.raise?(ArgumentError, "cp: foo*.txt: unexpectedly matched to multiple files (foo1.txt, foo2.txt).")
632
+ end
633
+ end
634
+ #
635
+ spec "[!qfidz] error when destination is a directory." do
636
+ sout, serr = capture_sio do
637
+ pr = proc { cp "foo1.txt", "d1" }
638
+ ok {pr}.raise?(ArgumentError, "cp: d1: cannot copy into directory (requires `to: 'd1'` keyword option).")
639
+ end
640
+ end
641
+ spec "[!073so] (cp) error when destination already exists to avoid overwriting it." do
642
+ sout, serr = capture_sio do
643
+ pr = proc { cp "foo1.txt", "foo2.txt" }
644
+ ok {pr}.raise?(ArgumentError, "cp: foo2.txt: file already exists (to overwrite it, call `cp!` instead of `cp`).")
645
+ end
646
+ end
647
+ spec "[!0tw8r] error when source is a directory but '-r' not specified." do
648
+ sout, serr = capture_sio do
649
+ pr = proc { cp "d1", "d9" }
650
+ ok {pr}.raise?(ArgumentError, "cp: d1: is a directory (requires `:-r` option).")
651
+ end
652
+ end
653
+ spec "[!lf6qi] error when target already exists." do
654
+ sout, serr = capture_sio do
655
+ dummy_dir("d9")
656
+ pr = proc { cp :r, "d1", "d9" }
657
+ ok {pr}.raise?(ArgumentError, "cp: d9: already exists.")
658
+ end
659
+ end
660
+ spec "[!4xxpe] error when source is a special file." do
661
+ sout, serr = capture_sio do
662
+ pr = proc { cp :r, "/dev/null", "d9" }
663
+ ok {pr}.raise?(ArgumentError, "cp: /dev/null: cannot copy special file.")
664
+ end
665
+ end
666
+ spec "[!lr2bj] error when source file not found and '-f' option not specified." do
667
+ sout, serr = capture_sio do
668
+ pr = proc { cp "blabla.txt", "blabla2.txt" }
669
+ ok {pr}.raise?(ArgumentError, "cp: blabla.txt: not found.")
670
+ end
671
+ end
672
+ spec "[!urh40] do nothing if source file not found and '-f' option specified." do
673
+ sout, serr = capture_sio do
674
+ cp :f, "blabla.txt", "blabla2.txt"
675
+ ok {"blabla2.txt"}.not_exist?
676
+ cp :f, "bla*.txt", "blabla2.txt"
677
+ ok {"blabla2.txt"}.not_exist?
678
+ end
679
+ end
680
+ spec "[!kqgdl] copy a directory recursively if '-r' option specified." do
681
+ sout, serr = capture_sio do
682
+ ok {"d9"}.not_exist?
683
+ cp :r, "d1", "d9"
684
+ ok {"d9"}.dir_exist?
685
+ ok {"d9/bar.txt"}.file_exist?
686
+ ok {"d9/d2/baz.txt"}.file_exist?
687
+ #
688
+ cp :r, "foo1.txt", "blabla.txt"
689
+ ok {"blabla.txt"}.file_exist?
690
+ end
691
+ end
692
+ spec "[!ko4he] copy a file into new file if '-r' option not specifieid." do
693
+ sout, serr = capture_sio do
694
+ cp "foo1.txt", "blabla.txt"
695
+ ok {"blabla.txt"}.file_exist?
696
+ end
697
+ end
698
+ spec "[!lac46] keeps file mtime if '-p' option specified." do
699
+ sout, serr = capture_sio do
700
+ ctime1 = File.ctime("d1/bar.txt")
701
+ mtime1 = File.mtime("d1/bar.txt")
702
+ atime1 = File.atime("d1/bar.txt")
703
+ #mtime2 = mtime1 - 900
704
+ #atime2 = atime1 - 600
705
+ mtime2 = (x = mtime1 - 900; Time.new(x.year, x.month, x.day, x.hour, x.min, x.sec))
706
+ atime2 = (x = atime1 - 600; Time.new(x.year, x.month, x.day, x.hour, x.min, x.sec))
707
+ #
708
+ File.utime(atime2, mtime2, "d1/bar.txt")
709
+ cp :p, "d1/bar.txt", "blabla.txt"
710
+ ok {File.atime("blabla.txt")} != atime1
711
+ ok {File.atime("blabla.txt")} != atime2
712
+ ok {File.mtime("blabla.txt")} != mtime1
713
+ ok {File.mtime("blabla.txt")} == mtime2 # !!!
714
+ ok {File.ctime("blabla.txt")} != ctime1
715
+ #
716
+ cp :pr, "d1", "d9"
717
+ ok {File.atime("d9/bar.txt")} != atime1
718
+ ok {File.atime("d9/bar.txt")} != atime2
719
+ ok {File.mtime("d9/bar.txt")} != mtime1
720
+ ok {File.mtime("d9/bar.txt")} == mtime2 # !!!
721
+ ok {File.ctime("d9/bar.txt")} != ctime1
722
+ end
723
+ end
724
+ spec "[!d49vw] not keep file mtime if '-p' option not specified." do
725
+ sout, serr = capture_sio do
726
+ ctime1 = File.ctime("d1/bar.txt")
727
+ mtime1 = File.mtime("d1/bar.txt")
728
+ atime1 = File.atime("d1/bar.txt")
729
+ #mtime2 = mtime1 - 900
730
+ #atime2 = atime1 - 600
731
+ mtime2 = (x = mtime1 - 900; Time.new(x.year, x.month, x.day, x.hour, x.min, x.sec))
732
+ atime2 = (x = atime1 - 600; Time.new(x.year, x.month, x.day, x.hour, x.min, x.sec))
733
+ #
734
+ File.utime(atime2, mtime2, "d1/bar.txt")
735
+ cp "d1/bar.txt", "blabla.txt"
736
+ ok {File.atime("blabla.txt")} != atime1
737
+ ok {File.atime("blabla.txt")} != atime2
738
+ ok {File.mtime("blabla.txt")} != mtime1
739
+ ok {File.mtime("blabla.txt")} != mtime2 # !!!
740
+ ok {File.ctime("blabla.txt")} != ctime1
741
+ #
742
+ cp :r, "d1", "d9"
743
+ ok {File.atime("d9/bar.txt")} != atime1
744
+ ok {File.atime("d9/bar.txt")} != atime2
745
+ ok {File.mtime("d9/bar.txt")} != mtime1
746
+ ok {File.mtime("d9/bar.txt")} != mtime2 # !!!
747
+ ok {File.ctime("d9/bar.txt")} != ctime1
748
+ end
749
+ end
750
+ spec "[!ubthp] creates hard link instead of copy if '-l' option specified." do
751
+ sout, serr = capture_sio do
752
+ cp "foo1.txt", "foo8.txt"
753
+ ok {File.identical?("foo1.txt", "foo8.txt")} == false
754
+ cp :l, "foo1.txt", "foo9.txt"
755
+ ok {File.identical?("foo1.txt", "foo9.txt")} == true
756
+ end
757
+ end
758
+ spec "[!yu51t] error when copying supecial files such as character device." do
759
+ sout, serr = capture_sio do
760
+ pr = proc { cp "/dev/null", "null" }
761
+ ok {pr}.raise?(ArgumentError, "cp: /dev/null: cannot copy special file.")
762
+ end
763
+ end
764
+ end
765
+
766
+ case_else "[!z8xce] when `to:` keyword arg specified..." do
767
+ spec "[!ms2sv] error when destination directory not exist." do
768
+ sout, serr = capture_sio do
769
+ pr = proc { cp "foo?.txt", to: "dir9" }
770
+ ok {pr}.raise?(ArgumentError, "cp: dir9: directory not found.")
771
+ end
772
+ end
773
+ spec "[!q9da3] error when destination pattern matched to multiple filenames." do
774
+ sout, serr = capture_sio do
775
+ pr = proc { cp "d1", to: "foo?.txt" }
776
+ ok {pr}.raise?(ArgumentError, "cp: foo?.txt: unexpectedly matched to multiple filenames (foo1.txt, foo2.txt).")
777
+ end
778
+ end
779
+ spec "[!lg3uz] error when destination is not a directory." do
780
+ sout, serr = capture_sio do
781
+ pr = proc { cp "d1", to: "foo1.txt" }
782
+ ok {pr}.raise?(ArgumentError, "cp: foo1.txt: Not a directory.")
783
+ end
784
+ end
785
+ spec "[!slavo] error when file not exist but '-f' option not specified." do
786
+ sout, serr = capture_sio do
787
+ pr = proc { cp "blabla", to: "d1" }
788
+ ok {pr}.raise?(ArgumentError, "cp: blabla: file or directory not found (add '-f' option to ignore missing files).")
789
+ end
790
+ end
791
+ spec "[!1ceaf] (cp) error when target file or directory already exists." do
792
+ sout, serr = capture_sio do
793
+ dummy_file("d1/foo1.txt", "tmp")
794
+ pr = proc { cp "foo?.txt", to: "d1" }
795
+ ok {pr}.raise?(ArgumentError, "cp: d1/foo1.txt: file or directory already exists (to overwrite it, call 'cp!' instead of 'cp').")
796
+ end
797
+ end
798
+ #
799
+ spec "[!bi897] error when copying directory but '-r' option not specified." do
800
+ sout, serr = capture_sio do
801
+ dummy_dir("d9")
802
+ pr = proc { cp "d1", to: "d9" }
803
+ ok {pr}.raise?(ArgumentError, "cp: d1: cannot copy directory (add '-r' option to copy it).")
804
+ end
805
+ end
806
+ spec "[!654d2] copy files recursively if '-r' option specified." do
807
+ sout, serr = capture_sio do
808
+ dummy_dir("d9")
809
+ ok {"d9"}.dir_exist?
810
+ cp :r, "foo*.txt", "d1", to: "d9"
811
+ ok {"d9/foo1.txt"}.file_exist?
812
+ ok {"d9/foo2.txt"}.file_exist?
813
+ ok {"d9/d1/bar.txt"}.file_exist?
814
+ ok {"d9/d1/d2/baz.txt"}.file_exist?
815
+ end
816
+ end
817
+ spec "[!i5g8r] copy files non-recursively if '-r' option not specified." do
818
+ sout, serr = capture_sio do
819
+ dummy_dir("d9")
820
+ ok {"d9"}.dir_exist?
821
+ cp "foo*.txt", "d1/**/*.txt", to: "d9"
822
+ ok {"d9/foo1.txt"}.file_exist?
823
+ ok {"d9/foo2.txt"}.file_exist?
824
+ ok {"d9/bar.txt"}.file_exist?
825
+ ok {"d9/baz.txt"}.file_exist?
826
+ end
827
+ end
828
+ spec "[!k8gyx] keeps file timestamp (mtime) if '-p' option specified." do
829
+ sout, serr = capture_sio do
830
+ ctime1 = File.ctime("d1/bar.txt")
831
+ mtime1 = File.mtime("d1/bar.txt")
832
+ atime1 = File.atime("d1/bar.txt")
833
+ #mtime2 = mtime1 - 30
834
+ #atime2 = atime1 - 90
835
+ mtime2 = (x = mtime1 - 900; Time.new(x.year, x.month, x.day, x.hour, x.min, x.sec))
836
+ atime2 = (x = atime1 - 600; Time.new(x.year, x.month, x.day, x.hour, x.min, x.sec))
837
+ File.utime(atime2, mtime2, "d1/bar.txt")
838
+ #
839
+ dummy_dir("d9")
840
+ cp :p, "foo*.txt", "d1/**/*.txt", to: "d9"
841
+ ok {File.ctime("d9/bar.txt")} != ctime1
842
+ ok {File.mtime("d9/bar.txt")} != mtime1
843
+ ok {File.mtime("d9/bar.txt")} == mtime2 # !!!
844
+ ok {File.atime("d9/bar.txt")} != atime1
845
+ ok {File.atime("d9/bar.txt")} != atime2
846
+ #
847
+ cp :pr, "d1", to: "d9"
848
+ ok {File.ctime("d9/d1/bar.txt")} != ctime1
849
+ ok {File.mtime("d9/d1/bar.txt")} != mtime1
850
+ ok {File.mtime("d9/d1/bar.txt")} == mtime2 # !!!
851
+ ok {File.atime("d9/d1/bar.txt")} != atime1
852
+ ok {File.atime("d9/d1/bar.txt")} != atime2
853
+ end
854
+ end
855
+ spec "[!zoun9] not keep file timestamp (mtime) if '-p' option not specified." do
856
+ sout, serr = capture_sio do
857
+ ctime1 = File.ctime("d1/bar.txt")
858
+ mtime1 = File.mtime("d1/bar.txt")
859
+ atime1 = File.atime("d1/bar.txt")
860
+ mtime2 = mtime1 - 30
861
+ atime2 = atime1 - 90
862
+ File.utime(atime2, mtime2, "d1/bar.txt")
863
+ #
864
+ dummy_dir("d9")
865
+ cp "foo*.txt", "d1/**/*.txt", to: "d9"
866
+ ok {File.ctime("d9/bar.txt")} != ctime1
867
+ ok {File.mtime("d9/bar.txt")} != mtime1
868
+ ok {File.mtime("d9/bar.txt")} != mtime2 # !!!
869
+ ok {File.atime("d9/bar.txt")} != atime1
870
+ ok {File.atime("d9/bar.txt")} != atime2
871
+ #
872
+ cp :r, "d1", to: "d9"
873
+ ok {File.ctime("d9/d1/bar.txt")} != ctime1
874
+ ok {File.mtime("d9/d1/bar.txt")} != mtime1
875
+ ok {File.mtime("d9/d1/bar.txt")} != mtime2 # !!!
876
+ ok {File.atime("d9/d1/bar.txt")} != atime1
877
+ ok {File.atime("d9/d1/bar.txt")} != atime2
878
+ end
879
+ end
880
+ spec "[!p7ah8] creates hard link instead of copy if '-l' option specified." do
881
+ sout, serr = capture_sio do
882
+ cp :l, "foo*.txt", to: "d1"
883
+ ok {File.identical?("foo1.txt", "d1/foo1.txt")} == true
884
+ ok {File.identical?("foo2.txt", "d1/foo2.txt")} == true
885
+ end
886
+ end
887
+ spec "[!e90ii] error when copying supecial files such as character device." do
888
+ sout, serr = capture_sio do
889
+ pr = proc { cp "/dev/null", to: "d1" }
890
+ ok {pr}.raise?(ArgumentError, "cp: /dev/null: cannot copy characterSpecial file.")
891
+ end
892
+ end
893
+ end
894
+
895
+ end
896
+
897
+
898
+ topic 'cp!()' do
899
+ spec "[!cpr7l] (cp!) overwrites existing destination file." do
900
+ sout, serr = capture_sio do
901
+ dummy_file("foo9.txt", "")
902
+ ok {"foo9.txt"}.file_exist?
903
+ cp! "foo1.txt", "foo9.txt"
904
+ ok {"foo9.txt"}.file_exist?
905
+ ok {File.read("foo9.txt")} == File.read("foo1.txt")
906
+ #
907
+ pr = proc { cp "foo1.txt", "foo9.txt" }
908
+ ok {pr}.raise?(ArgumentError, "cp: foo9.txt: file already exists (to overwrite it, call `cp!` instead of `cp`).")
909
+ end
910
+ end
911
+ spec "[!melhx] (cp!) overwrites existing files." do
912
+ sout, serr = capture_sio do
913
+ dummy_dir("d9")
914
+ dummy_file("d9/foo1.txt", "")
915
+ ok {"d9/foo1.txt"}.file_exist?
916
+ cp! "foo1.txt", to: "d9"
917
+ ok {"d9/foo1.txt"}.file_exist?
918
+ ok {File.read("d9/foo1.txt")} == File.read("foo1.txt")
919
+ #
920
+ pr = proc { cp "foo1.txt", to: "d9" }
921
+ ok {pr}.raise?(ArgumentError, "cp: d9/foo1.txt: file or directory already exists (to overwrite it, call 'cp!' instead of 'cp').")
922
+ end
923
+ end
924
+ end
925
+
926
+
927
+ topic 'mv()' do
928
+
929
+ spec "[!ajm59] echoback command and arguments." do
930
+ sout, serr = capture_sio do
931
+ mv "foo1.txt", "foo9.txt"
932
+ mv "foo2.txt", to: "d1"
933
+ end
934
+ ok {sout} == ("$ mv foo1.txt foo9.txt\n"\
935
+ "$ mv foo2.txt d1\n")
936
+ end
937
+
938
+ case_when "[!g732t] when `to:` keyword argument not specified..." do
939
+ spec "[!0f106] error when number of arguments is not 2." do
940
+ sout, serr = capture_sio do
941
+ pr = proc { mv() }
942
+ ok {pr}.raise?(ArgumentError, "mv: requires two arguments.")
943
+ pr = proc { mv "foo1.txt" }
944
+ ok {pr}.raise?(ArgumentError, "mv: requires two arguments.")
945
+ pr = proc { mv "foo1.txt", "foo2.txt", "foo3.txt" }
946
+ ok {pr}.raise?(ArgumentError, "mv: too much arguments.")
947
+ end
948
+ end
949
+ spec "[!xsti2] error when source pattern matched to multiple files." do
950
+ sout, serr = capture_sio do
951
+ pr = proc { mv "foo?.txt", "blabla.txt" }
952
+ ok {pr}.raise?(ArgumentError, "mv: foo?.txt: unexpectedly matched to multiple files (foo1.txt, foo2.txt).")
953
+ end
954
+ end
955
+ spec "[!4wam3] error when destination pattern matched to multiple files." do
956
+ sout, serr = capture_sio do
957
+ pr = proc { mv "d1/bar.txt", "foo*.txt" }
958
+ ok {pr}.raise?(ArgumentError, "mv: foo*.txt: unexpectedly matched to multiple files (foo1.txt, foo2.txt).")
959
+ end
960
+ end
961
+ #
962
+ spec "[!ude1j] cannot move file into existing directory." do
963
+ sout, serr = capture_sio do
964
+ pr = proc { mv "foo1.txt", "d1" }
965
+ ok {pr}.raise?(ArgumentError, "mv: cannot move file 'foo1.txt' into directory 'd1' without 'to:' keyword option.")
966
+ end
967
+ end
968
+ spec "[!2aws0] cannt rename directory into existing file or directory." do
969
+ sout, serr = capture_sio do
970
+ pr = proc { mv "d1", "foo1.txt" }
971
+ ok {pr}.raise?(ArgumentError, "mv: cannot rename directory 'd1' to existing file or directory.")
972
+ end
973
+ end
974
+ spec "[!3fbpu] (mv) error when destination file already exists." do
975
+ sout, serr = capture_sio do
976
+ pr = proc { mv "foo1.txt", "foo2.txt" }
977
+ ok {pr}.raise?(ArgumentError, "mv: foo2.txt: already exists (to overwrite it, call `mv!` instead of `mv`).")
978
+ end
979
+ end
980
+ spec "[!397kn] do nothing when file or directory not found but '-f' option specified." do
981
+ sout, serr = capture_sio do
982
+ mv :f, "blabla.txt", "blabla2.txt"
983
+ ok {"blabla2.txt"}.not_exist?
984
+ end
985
+ end
986
+ spec "[!1z89i] error when source file or directory not found." do
987
+ sout, serr = capture_sio do
988
+ pr = proc { mv "blabla.txt", "blabla2.txt" }
989
+ ok {pr}.raise?(ArgumentError, "mv: blabla.txt: not found.")
990
+ end
991
+ end
992
+ spec "[!9eqt3] rename file or directory." do
993
+ sout, serr = capture_sio do
994
+ s = File.read("foo1.txt")
995
+ mv "foo1.txt", "blabla.txt"
996
+ ok {"foo1.txt"}.not_exist?
997
+ ok {"blabla.txt"}.file_exist?
998
+ ok {File.read("blabla.txt")} == s
999
+ #
1000
+ mv "d1", "d9"
1001
+ ok {"d1"}.not_exist?
1002
+ ok {"d9"}.dir_exist?
1003
+ end
1004
+ end
1005
+ end
1006
+
1007
+ case_else "[!iu87y] when `to:` keyword argument specified..." do
1008
+ spec "[!wf6pc] error when destination directory not exist." do
1009
+ sout, serr = capture_sio do
1010
+ pr = proc { mv "foo?.txt", to: "dir9" }
1011
+ ok {pr}.raise?(ArgumentError, "mv: dir9: directory not found.")
1012
+ end
1013
+ end
1014
+ spec "[!8v4dn] error when destination pattern matched to multiple filenames." do
1015
+ sout, serr = capture_sio do
1016
+ pr = proc { mv "d1", to: "foo?.txt" }
1017
+ ok {pr}.raise?(ArgumentError, "mv: foo?.txt: unexpectedly matched to multiple filenames (foo1.txt, foo2.txt).")
1018
+ end
1019
+ end
1020
+ spec "[!ppr6n] error when destination is not a directory." do
1021
+ sout, serr = capture_sio do
1022
+ pr = proc { mv "d1", to: "foo1.txt" }
1023
+ ok {pr}.raise?(ArgumentError, "mv: foo1.txt: Not a directory.")
1024
+ end
1025
+ end
1026
+ spec "[!bjqwi] error when file not exist but '-f' option not specified." do
1027
+ sout, serr = capture_sio do
1028
+ pr = proc { mv "blabla", to: "d1" }
1029
+ ok {pr}.raise?(ArgumentError, "mv: blabla: file or directory not found (add '-f' option to ignore missing files).")
1030
+ end
1031
+ end
1032
+ spec "[!k21ns] (mv) error when target file or directory already exists." do
1033
+ sout, serr = capture_sio do
1034
+ dummy_file("d1/foo1.txt", "tmp")
1035
+ pr = proc { mv "foo?.txt", to: "d1" }
1036
+ ok {pr}.raise?(ArgumentError, "mv: d1/foo1.txt: file or directory already exists (to overwrite it, call 'mv!' instead of 'mv').")
1037
+ end
1038
+ end
1039
+ #
1040
+ spec "[!ri2ia] move files into existing directory." do
1041
+ sout, serr = capture_sio do
1042
+ mv "foo?.txt", to: "d1/d2"
1043
+ ok {"foo1.txt"}.not_exist?
1044
+ ok {"foo2.txt"}.not_exist?
1045
+ ok {"d1/d2/foo1.txt"}.file_exist?
1046
+ ok {"d1/d2/foo2.txt"}.file_exist?
1047
+ end
1048
+ end
1049
+ end
1050
+
1051
+ end
1052
+
1053
+
1054
+ topic 'mv!()' do
1055
+ spec "[!zpojx] (mv!) overwrites existing files." do
1056
+ sout, serr = capture_sio do
1057
+ pr = proc { mv "foo1.txt", "foo2.txt" }
1058
+ ok {pr}.raise?(ArgumentError, "mv: foo2.txt: already exists (to overwrite it, call `mv!` instead of `mv`).")
1059
+ #
1060
+ s = File.read("foo2.txt")
1061
+ mv! "foo1.txt", "foo2.txt"
1062
+ ok {"foo1.txt"}.not_exist?
1063
+ ok {"foo2.txt"}.file_exist?
1064
+ ok {File.read("foo2.txt")} != s
1065
+ end
1066
+ end
1067
+ spec "[!vcaf5] (mv!) overwrites existing files." do
1068
+ sout, serr = capture_sio do
1069
+ mv "foo1.txt", to: "d1"
1070
+ ok {"d1/foo1.txt"}.file_exist?
1071
+ #
1072
+ mv "d1/foo1.txt", "d1/foo2.txt"
1073
+ pr = proc { mv "foo2.txt", to: "d1" }
1074
+ ok {pr}.raise?(ArgumentError, "mv: d1/foo2.txt: file or directory already exists (to overwrite it, call 'mv!' instead of 'mv').")
1075
+ end
1076
+ end
1077
+ end
1078
+
1079
+
1080
+ topic 'rm()' do
1081
+ spec "[!bikrs] echoback command and arguments." do
1082
+ sout, serr = capture_sio do
1083
+ rm "foo*.txt"
1084
+ end
1085
+ ok {sout} == "$ rm foo*.txt\n"
1086
+ end
1087
+ spec "[!va1j0] error when file not exist but '-f' option not specified." do
1088
+ sout, serr = capture_sio do
1089
+ pr = proc { rm "foo*.txt", "blabla*.txt" }
1090
+ ok {pr}.raise?(ArgumentError, "rm: blabla*.txt: file or directory not found (add '-f' option to ignore missing files).")
1091
+ ok {"foo1.txt"}.file_exist?
1092
+ #
1093
+ end
1094
+ end
1095
+ spec "[!t6vhx] ignores missing files if '-f' option specified." do
1096
+ sout, serr = capture_sio do
1097
+ rm :f, "foo*.txt", "blabla*.txt"
1098
+ ok {"foo1.txt"}.not_exist?
1099
+ end
1100
+ end
1101
+ spec "[!o92yi] cannot remove directory unless '-r' option specified." do
1102
+ sout, serr = capture_sio do
1103
+ pr = proc { rm "d1" }
1104
+ ok {pr}.raise?(ArgumentError, "rm: d1: cannot remove directory (add '-r' option to remove it).")
1105
+ end
1106
+ end
1107
+ spec "[!srx8w] remove directories recursively if '-r' option specified." do
1108
+ sout, serr = capture_sio do
1109
+ ok {"d1"}.dir_exist?
1110
+ rm :r, "d1"
1111
+ ok {"d1"}.not_exist?
1112
+ end
1113
+ end
1114
+ spec "[!mdgjc] remove files if '-r' option not specified." do
1115
+ sout, serr = capture_sio do
1116
+ rm "foo*.txt"
1117
+ ok {"foo1.txt"}.not_exist?
1118
+ ok {"foo2.txt"}.not_exist?
1119
+ end
1120
+ end
1121
+ end
1122
+
1123
+
1124
+ topic 'mkdir()' do
1125
+ spec "[!wd7rm] error when mode is invalid." do
1126
+ sout, serr = capture_sio do
1127
+ pr = proc { mkdir :m, "a+x" }
1128
+ ok {pr}.raise?(ArgumentError, "mkdir: a+x: '-m' option doesn't support this style mode (use '0755' tyle instead).")
1129
+ #
1130
+ pr = proc { mkdir :m, "+x" }
1131
+ ok {pr}.raise?(ArgumentError, "mkdir: +x: invalid mode.")
1132
+ end
1133
+ end
1134
+ spec "[!xusor] raises error when argument not specified." do
1135
+ sout, serr = capture_sio do
1136
+ pr = proc { mkdir() }
1137
+ ok {pr}.raise?(ArgumentError, "mkdir: argument required.")
1138
+ end
1139
+ end
1140
+ spec "[!51pmg] error when directory already exists but '-p' option not specified." do
1141
+ sout, serr = capture_sio do
1142
+ pr = proc { mkdir "d1" }
1143
+ ok {pr}.raise?(ArgumentError, "mkdir: d1: directory already exists.")
1144
+ end
1145
+ end
1146
+ spec "[!pydy1] ignores existing directories if '-p' option specified." do
1147
+ sout, serr = capture_sio do
1148
+ ok {"d1"}.dir_exist?
1149
+ mkdir :p, "d1"
1150
+ ok {"d1"}.dir_exist?
1151
+ end
1152
+ end
1153
+ spec "[!om8a6] error when file already exists." do
1154
+ sout, serr = capture_sio do
1155
+ pr = proc { mkdir "foo1.txt" }
1156
+ ok {pr}.raise?(ArgumentError, "mkdir: foo1.txt: file exists.")
1157
+ end
1158
+ end
1159
+ spec "[!xx7mv] error when parent directory not exist but '-p' option not specified." do
1160
+ sout, serr = capture_sio do
1161
+ pr = proc { mkdir "d1/a/b" }
1162
+ ok {pr}.raise?(ArgumentError, "mkdir: d1/a/b: parent directory not exists (add '-p' to create it).")
1163
+ end
1164
+ end
1165
+ spec "[!jc8hm] '-m' option specifies mode of new directories." do
1166
+ sout, serr = capture_sio do
1167
+ ok {"d9"}.not_exist?
1168
+ mkdir :m, 0750, "d9"
1169
+ ok {"d9"}.dir_exist?
1170
+ ok {File.stat("d9").mode & 0777} == 0750
1171
+ #
1172
+ mkdir :pm, 0705, "d9/a/b"
1173
+ ok {"d9/a/b"}.dir_exist?
1174
+ ok {File.stat("d9/a/b").mode & 0777} == 0705
1175
+ end
1176
+ end
1177
+ spec "[!0zeu3] create intermediate path if '-p' option specified." do
1178
+ sout, serr = capture_sio do
1179
+ ok {"d1/a/b"}.not_exist?
1180
+ mkdir :p, "d1/a/b"
1181
+ ok {"d1/a/b"}.dir_exist?
1182
+ #
1183
+ ok {"d9/a/b"}.not_exist?
1184
+ mkdir :p, "d9/a/b"
1185
+ ok {"d9/a/b"}.dir_exist?
1186
+ end
1187
+ end
1188
+ spec "[!l0pr8] create directories if '-p' option not specified." do
1189
+ sout, serr = capture_sio do
1190
+ mkdir :p, "aa", "bb", "cc"
1191
+ ok {"aa"}.dir_exist?
1192
+ ok {"bb"}.dir_exist?
1193
+ ok {"cc"}.dir_exist?
1194
+ end
1195
+ end
1196
+ end
1197
+
1198
+
1199
+ topic 'rmdir()' do
1200
+ spec "[!bqhdd] error when argument not specified." do
1201
+ sout, serr = capture_sio do
1202
+ pr = proc { rmdir() }
1203
+ ok {pr}.raise?(ArgumentError, "rmdir: argument required.")
1204
+ end
1205
+ end
1206
+ spec "[!o1k3g] error when directory not exist." do
1207
+ sout, serr = capture_sio do
1208
+ pr = proc { rmdir "d9" }
1209
+ ok {pr}.raise?(ArgumentError, "rmdir: d9: No such file or directory.")
1210
+ end
1211
+ end
1212
+ spec "[!ch5rq] error when directory is a symbolic link." do
1213
+ sout, serr = capture_sio do
1214
+ File.symlink("foo1.txt", "foo1.lnk")
1215
+ pr = proc { rmdir "foo1.lnk" }
1216
+ ok {pr}.raise?(ArgumentError, "rmdir: foo1.lnk: Not a directory.")
1217
+ end
1218
+ end
1219
+ spec "[!igfti] error when directory is not empty." do
1220
+ sout, serr = capture_sio do
1221
+ pr = proc { rmdir "d1" }
1222
+ #ok {pr}.raise?(Errno::ENOTEMPTY, "Directory not empty @ dir_s_rmdir - d9")
1223
+ ok {pr}.raise?(ArgumentError, "rmdir: d1: Directory not empty.")
1224
+ end
1225
+ end
1226
+ spec "[!qnnqy] error when argument is not a directory." do
1227
+ sout, serr = capture_sio do
1228
+ pr = proc { rmdir "foo1.txt" }
1229
+ ok {pr}.raise?(ArgumentError, "rmdir: foo1.txt: Not a directory.")
1230
+ end
1231
+ end
1232
+ spec "[!jgmw7] remove empty directories." do
1233
+ sout, serr = capture_sio do
1234
+ dummy_dir "d9/a/b"
1235
+ ok {"d9/a/b"}.dir_exist?
1236
+ rmdir "d9/a/b"
1237
+ ok {"d9/a/b"}.not_exist?
1238
+ rmdir "d9/a"
1239
+ ok {"d9/a"}.not_exist?
1240
+ end
1241
+ end
1242
+ end
1243
+
1244
+
1245
+ topic 'ln()' do
1246
+ spec "[!ycp6e] echobacks command and arguments." do
1247
+ sout, serr = capture_sio do
1248
+ ln "foo1.txt", "foo8.txt"
1249
+ ln :s, "foo2.txt", "foo9.txt"
1250
+ end
1251
+ ok {sout} == ("$ ln -n foo1.txt foo8.txt\n"\
1252
+ "$ ln -sn foo2.txt foo9.txt\n")
1253
+ end
1254
+ spec "[!umk6m] keyword arg `to: xx` is echobacked as `-t xx`." do
1255
+ sout, serr = capture_sio do
1256
+ ln "foo*.txt", to: "d1"
1257
+ end
1258
+ ok {sout} == ("$ ln -t d1 -n foo*.txt\n")
1259
+ end
1260
+
1261
+ case_when "[!qtbp4] when `to:` keyword argument not specified..." do
1262
+ spec "[!n1zpi] error when number of arguments is not 2." do
1263
+ sout, serr = capture_sio do
1264
+ pr = proc { ln() }
1265
+ ok {pr}.raise?(ArgumentError, "ln: requires two arguments.")
1266
+ pr = proc { ln "foo1.txt" }
1267
+ ok {pr}.raise?(ArgumentError, "ln: requires two arguments.")
1268
+ end
1269
+ end
1270
+ spec "[!2rxqo] error when source pattern matched to multiple files." do
1271
+ sout, serr = capture_sio do
1272
+ pr = proc { ln "foo*.txt", "bar.txt" }
1273
+ ok {pr}.raise?(ArgumentError, "ln: foo*.txt: unexpectedly matched to multiple files (foo1.txt, foo2.txt).")
1274
+ end
1275
+ end
1276
+ spec "[!ysxdq] error when destination pattern matched to multiple files." do
1277
+ sout, serr = capture_sio do
1278
+ pr = proc { ln "d9/bar.txt", "foo*.txt" }
1279
+ ok {pr}.raise?(ArgumentError, "ln: foo*.txt: unexpectedly matched to multiple files (foo1.txt, foo2.txt).")
1280
+ end
1281
+ end
1282
+ #
1283
+ spec "[!4ry8j] (hard link) error when source file not exists." do
1284
+ sout, serr = capture_sio do
1285
+ pr = proc { ln "foo8.txt", "foo9.txt" }
1286
+ ok {pr}.raise?(ArgumentError, "ln: foo8.txt: No such file or directory.")
1287
+ end
1288
+ end
1289
+ spec "[!tf29w] (hard link) error when source is a directory." do
1290
+ sout, serr = capture_sio do
1291
+ pr = proc { ln "d1", "d2" }
1292
+ ok {pr}.raise?(ArgumentError, "ln: d1: Is a directory.")
1293
+ end
1294
+ end
1295
+ spec "[!zmijh] error when destination is a directory without `to:` keyword argument." do
1296
+ sout, serr = capture_sio do
1297
+ pr = proc { ln "foo1.txt", "d1" }
1298
+ ok {pr}.raise?(ArgumentError, "ln: d1: cannot create link under directory without `to:` keyword option.")
1299
+ end
1300
+ end
1301
+ spec "[!nzci0] (ln) error when destination already exists." do
1302
+ sout, serr = capture_sio do
1303
+ pr = proc { ln "foo1.txt", "d1" }
1304
+ ok {pr}.raise?(ArgumentError, "ln: d1: cannot create link under directory without `to:` keyword option.")
1305
+ pr = proc { ln :s, "foo1.txt", "d1" }
1306
+ ok {pr}.raise?(ArgumentError, "ln: d1: cannot create link under directory without `to:` keyword option.")
1307
+ end
1308
+ end
1309
+ spec "[!oxjqv] create symbolic link if '-s' option specified." do
1310
+ sout, serr = capture_sio do
1311
+ ln :s, "foo1.txt", "foo9.txt"
1312
+ ok {"foo9.txt"}.file_exist?
1313
+ ok {"foo9.txt"}.symlink_exist?
1314
+ ln :s, "d1", "d9"
1315
+ ok {"d9"}.dir_exist?
1316
+ ok {"d9"}.symlink_exist?
1317
+ end
1318
+ end
1319
+ spec "[!awig1] (symlink) can create symbolic link to non-existing file." do
1320
+ sout, serr = capture_sio do
1321
+ ok {"foo8.txt"}.not_exist?
1322
+ ln :s, "foo8.txt", "foo9.txt"
1323
+ ok {"foo9.txt"}.symlink_exist?
1324
+ end
1325
+ end
1326
+ spec "[!5kl3w] (symlink) can create symbolic link to directory." do
1327
+ sout, serr = capture_sio do
1328
+ ln :s, "d1", "d9"
1329
+ ok {"d9"}.symlink_exist?
1330
+ end
1331
+ end
1332
+ spec "[!sb29p] create hard link if '-s' option not specified." do
1333
+ sout, serr = capture_sio do
1334
+ ln "foo1.txt", "foo9.txt"
1335
+ ok {"foo9.txt"}.file_exist?
1336
+ ok {"foo9.txt"}.NOT.symlink_exist?
1337
+ end
1338
+ end
1339
+ end
1340
+
1341
+ case_else "[!5x2wr] when `to:` keyword argument specified..." do
1342
+ spec "[!5gfxk] error when destination directory not exist." do
1343
+ sout, serr = capture_sio do
1344
+ pr = proc { ln "foo*.txt", to: "d9" }
1345
+ ok {pr}.raise?(ArgumentError, "ln: d9: directory not found.")
1346
+ end
1347
+ end
1348
+ spec "[!euu5d] error when destination pattern matched to multiple filenames." do
1349
+ sout, serr = capture_sio do
1350
+ pr = proc { ln "d1/bar.txt", to: "foo*.txt" }
1351
+ ok {pr}.raise?(ArgumentError, "ln: foo*.txt: unexpectedly matched to multiple filenames (foo1.txt, foo2.txt).")
1352
+ end
1353
+ end
1354
+ spec "[!42nb7] error when destination is not a directory." do
1355
+ sout, serr = capture_sio do
1356
+ pr = proc { ln "foo1.txt", to: "foo2.txt" }
1357
+ ok {pr}.raise?(ArgumentError, "ln: foo2.txt: Not a directory.")
1358
+ end
1359
+ end
1360
+ #
1361
+ spec "[!x7wh5] (symlink) can create symlink to unexisting file." do
1362
+ sout, serr = capture_sio do
1363
+ ln :s, "foo8.txt", to: "d1"
1364
+ ok {"d1/foo8.txt"}.not_exist?
1365
+ ok {"d1/foo8.txt"}.symlink_exist?
1366
+ end
1367
+ end
1368
+ spec "[!ml1vm] (hard link) error when source file not exist." do
1369
+ sout, serr = capture_sio do
1370
+ pr = proc { ln "foo8.txt", to: "d1" }
1371
+ ok {pr}.raise?(ArgumentError, "ln: foo8.txt: No such file or directory.")
1372
+ end
1373
+ end
1374
+ #
1375
+ spec "[!mwukw] (ln) error when target file or directory already exists." do
1376
+ sout, serr = capture_sio do
1377
+ dummy_file("d1/foo1.txt", "foo1")
1378
+ pr = proc { ln "foo*.txt", to: "d1" } # hard link
1379
+ ok {pr}.raise?(ArgumentError, "ln: d1/foo1.txt: File exists (to overwrite it, call `ln!` instead of `ln`).")
1380
+ #
1381
+ pr = proc { ln :s, "foo*.txt", to: "d1" } # symbolic link
1382
+ ok {pr}.raise?(ArgumentError, "ln: d1/foo1.txt: File exists (to overwrite it, call `ln!` instead of `ln`).")
1383
+ end
1384
+ end
1385
+ spec "[!c8hpp] (hard link) create hard link under directory if '-s' option not specified." do
1386
+ sout, serr = capture_sio do
1387
+ ln "foo*.txt", to: "d1"
1388
+ ok {"d1/foo1.txt"}.file_exist?
1389
+ ok {"d1/foo2.txt"}.file_exist?
1390
+ ok {"d1/foo1.txt"}.NOT.symlink_exist?
1391
+ ok {"d1/foo2.txt"}.NOT.symlink_exist?
1392
+ end
1393
+ end
1394
+ spec "[!9tv9g] (symlik) create symbolic link under directory if '-s' option specified." do
1395
+ sout, serr = capture_sio do
1396
+ ln :s, "foo*.txt", to: "d1"
1397
+ ok {"d1/foo1.txt"}.symlink_exist?
1398
+ ok {"d1/foo2.txt"}.symlink_exist?
1399
+ ok {"d1/foo1.txt"}.NOT.file_exist?
1400
+ ok {"d1/foo2.txt"}.NOT.file_exist?
1401
+ end
1402
+ end
1403
+ end
1404
+ end
1405
+
1406
+
1407
+ topic 'ln!()' do
1408
+ spec "[!dkqgq] (ln!) overwrites existing destination file." do
1409
+ sout, serr = capture_sio do
1410
+ ln :s, "foo1.txt", "foo9.txt"
1411
+ ok {"foo9.txt"}.symlink_exist?
1412
+ #
1413
+ pr = proc { ln :s, "foo2.txt", "foo9.txt" } # ln, symbolic link
1414
+ ok {pr}.raise?(ArgumentError, "ln: foo9.txt: File exists (to overwrite it, call `ln!` instead of `ln`).")
1415
+ ln! :s, "foo1.txt", "foo9.txt" # ln!, symbolic link
1416
+ ok {"foo9.txt"}.symlink_exist?
1417
+ ok {File.readlink("foo9.txt")} == "foo1.txt"
1418
+ #
1419
+ pr = proc { ln "foo2.txt", "foo9.txt" } # ln, hard link
1420
+ ok {pr}.raise?(ArgumentError, "ln: foo9.txt: File exists (to overwrite it, call `ln!` instead of `ln`).")
1421
+ ln! "foo2.txt", "foo9.txt" # ln!, hard link
1422
+ ok {"foo9.txt"}.file_exist?
1423
+ ok {"foo9.txt"}.NOT.symlink_exist?
1424
+ end
1425
+ end
1426
+ spec "[!c3vwn] (ln!) error when target file is a directory." do
1427
+ sout, serr = capture_sio do
1428
+ dummy_dir("d1/foo1.txt")
1429
+ pr = proc { ln! "foo1.txt", to: "d1" } # hard link
1430
+ ok {pr}.raise?(ArgumentError, "ln!: d1/foo1.txt: directory already exists.")
1431
+ #
1432
+ pr = proc { ln! :s, "foo1.txt", to: "d1" } # symbolic link
1433
+ ok {pr}.raise?(ArgumentError, "ln!: d1/foo1.txt: directory already exists.")
1434
+ end
1435
+ end
1436
+ spec "[!bfcki] (ln!) overwrites existing symbolic links." do
1437
+ sout, serr = capture_sio do
1438
+ ln :s, "d1/bar.txt", "d1/foo1.txt"
1439
+ ln :s, "d1/bar.txt", "d1/foo2.txt"
1440
+ ok {"d1/foo1.txt"}.symlink_exist?
1441
+ ok {"d1/foo2.txt"}.symlink_exist?
1442
+ #
1443
+ pr = proc { ln "foo1.txt", to: "d1" }
1444
+ ok {pr}.raise?(ArgumentError, "ln: d1/foo1.txt: symbolic link already exists (to overwrite it, call `ln!` instead of `ln`).")
1445
+ ln! "foo1.txt", to: "d1" # hard link
1446
+ ok {"d1/foo1.txt"}.file_exist?
1447
+ ok {"d1/foo1.txt"}.NOT.symlink_exist?
1448
+ #
1449
+ pr = proc { ln :s, "foo2.txt", to: "d1" }
1450
+ ok {pr}.raise?(ArgumentError, "ln: d1/foo2.txt: symbolic link already exists (to overwrite it, call `ln!` instead of `ln`).")
1451
+ ln! :s, "foo2.txt", to: "d1" # symbolic link
1452
+ ok {"d1/foo2.txt"}.symlink_exist?
1453
+ ok {"d1/foo2.txt"}.NOT.file_exist?
1454
+ end
1455
+ end
1456
+ spec "[!ipy2c] (ln!) overwrites existing files." do
1457
+ dummy_file "d1/foo1.txt"
1458
+ dummy_file "d1/foo2.txt"
1459
+ sout, serr = capture_sio do
1460
+ ## hard link
1461
+ pr = proc { ln "foo1.txt", to: "d1" }
1462
+ ok {pr}.raise?(ArgumentError, "ln: d1/foo1.txt: File exists (to overwrite it, call `ln!` instead of `ln`).")
1463
+ ln! "foo1.txt", to: "d1"
1464
+ ok {"d1/foo1.txt"}.file_exist?
1465
+ ok {"d1/foo1.txt"}.NOT.symlink_exist?
1466
+ ## symbolic link
1467
+ pr = proc { ln :s, "foo2.txt", to: "d1" }
1468
+ ok {pr}.raise?(ArgumentError, "ln: d1/foo2.txt: File exists (to overwrite it, call `ln!` instead of `ln`).")
1469
+ ln! :s, "foo2.txt", to: "d1" # symbolic link
1470
+ ok {"d1/foo2.txt"}.symlink_exist?
1471
+ ok {"d1/foo2.txt"}.NOT.file_exist?
1472
+ end
1473
+ end
1474
+ end
1475
+
1476
+ topic 'atomic_symlink!()' do
1477
+ spec "[!gzp4a] creates temporal symlink and rename it when symlink already exists." do
1478
+ File.symlink("foo1.txt", "tmp.link")
1479
+ sout, serr = capture_sio do
1480
+ atomic_symlink! "foo2.txt", "tmp.link"
1481
+ end
1482
+ ok {File.readlink("tmp.link")} == "foo2.txt"
1483
+ ok {sout} =~ /\A\$ ln -s foo2\.txt tmp\.link\.\d+ \&\& mv -Tf tmp\.link\.\d+ tmp.link\n\z/
1484
+ end
1485
+ spec "[!lhomw] creates temporal symlink and rename it when symlink not exist." do
1486
+ sout, serr = capture_sio do
1487
+ atomic_symlink! "d1", "d1.link"
1488
+ end
1489
+ ok {File.readlink("d1.link")} == "d1"
1490
+ ok {sout} =~ /\A\$ ln -s d1 d1\.link\.\d+ \&\& mv -Tf d1\.link\.\d+ d1\.link\n\z/
1491
+ end
1492
+ spec "[!h75kp] error when destination is normal file or directory." do
1493
+ sout, serr = capture_sio do
1494
+ pr = proc { atomic_symlink! "foo1.txt", "foo2.txt" }
1495
+ ok {pr}.raise?(ArgumentError, "atomic_symlink!: foo2.txt: not a symbolic link.")
1496
+ pr = proc { atomic_symlink! "foo1.txt", "d1" }
1497
+ ok {pr}.raise?(ArgumentError, "atomic_symlink!: d1: not a symbolic link.")
1498
+ end
1499
+ end
1500
+ end
1501
+
1502
+
1503
+ topic 'pwd()' do
1504
+ spec "[!aelx6] echoback command and arguments." do
1505
+ here = Dir.pwd()
1506
+ sout, serr = capture_sio do
1507
+ pwd()
1508
+ end
1509
+ ok {sout} == ("$ pwd\n"\
1510
+ "#{here}\n")
1511
+ end
1512
+ spec "[!kh3l2] prints current directory path."do
1513
+ here = Dir.pwd()
1514
+ sout, serr = capture_sio do
1515
+ pwd()
1516
+ end
1517
+ ok {sout} == ("$ pwd\n"\
1518
+ "#{here}\n")
1519
+ end
1520
+ end
1521
+
1522
+
1523
+ topic 'touch()' do
1524
+ fixture :ts do
1525
+ ts = Time.new(2000, 1, 1, 0, 0, 0)
1526
+ File.utime(ts, ts, "foo1.txt")
1527
+ File.utime(ts, ts, "foo2.txt")
1528
+ #File.utime(ts, ts, "d1/bar.txt")
1529
+ ts
1530
+ end
1531
+ #
1532
+ spec "[!ifxob] echobacks command and arguments." do
1533
+ sout, serr = capture_sio do
1534
+ touch "foo1.txt", "foo2.txt"
1535
+ end
1536
+ ok {sout} == "$ touch foo1.txt foo2.txt\n"
1537
+ end
1538
+ spec "[!c7e51] error when reference file not exist." do
1539
+ sout, serr = capture_sio do
1540
+ pr = proc { touch :r, "foo9.txt", "foo1.txt" }
1541
+ ok {pr}.raise?(ArgumentError, "touch: foo9.txt: not exist.")
1542
+ end
1543
+ end
1544
+ spec "[!pggnv] changes both access time and modification time in default." do |ts|
1545
+ sout, serr = capture_sio do
1546
+ ok {File.atime("foo1.txt")} == ts
1547
+ ok {File.mtime("foo1.txt")} == ts
1548
+ #
1549
+ now1 = Time.now
1550
+ touch "foo1.txt"
1551
+ now2 = Time.now
1552
+ ok {File.atime("foo1.txt")}.between?(now1, now2)
1553
+ ok {File.mtime("foo1.txt")}.between?(now1, now2)
1554
+ end
1555
+ end
1556
+ spec "[!o9h74] expands file name pattern." do |ts|
1557
+ sout, serr = capture_sio do
1558
+ ok {File.atime("foo1.txt")} == ts
1559
+ ok {File.mtime("foo1.txt")} == ts
1560
+ ok {File.atime("foo2.txt")} == ts
1561
+ ok {File.mtime("foo2.txt")} == ts
1562
+ #
1563
+ now1 = Time.now
1564
+ touch "foo*.txt"
1565
+ now2 = Time.now
1566
+ ok {File.atime("foo1.txt")}.between?(now1, now2)
1567
+ ok {File.mtime("foo1.txt")}.between?(now1, now2)
1568
+ ok {File.atime("foo2.txt")}.between?(now1, now2)
1569
+ ok {File.mtime("foo2.txt")}.between?(now1, now2)
1570
+ end
1571
+ end
1572
+ spec "[!9ahsu] changes timestamp of files to current datetime." do |ts|
1573
+ sout, serr = capture_sio do
1574
+ ok {File.atime("foo1.txt")} == ts
1575
+ ok {File.mtime("foo1.txt")} == ts
1576
+ #
1577
+ now1 = Time.now
1578
+ touch "foo1.txt"
1579
+ now2 = Time.now
1580
+ ok {File.atime("foo1.txt")}.between?(now1, now2)
1581
+ ok {File.mtime("foo1.txt")}.between?(now1, now2)
1582
+ end
1583
+ end
1584
+ spec "[!wo080] if reference file specified, use it's timestamp." do |ts|
1585
+ sout, serr = capture_sio do
1586
+ ok {File.atime("foo1.txt")} == ts
1587
+ ok {File.mtime("foo1.txt")} == ts
1588
+ touch :r, "foo1.txt", "d1/bar.txt"
1589
+ ok {File.atime("d1/bar.txt")} == ts
1590
+ ok {File.mtime("d1/bar.txt")} == ts
1591
+ end
1592
+ end
1593
+ spec "[!726rq] creates empty file if file not found and '-c' option not specified." do |ts|
1594
+ sout, serr = capture_sio do
1595
+ ok {"foo9.txt"}.not_exist?
1596
+ touch "foo9.txt"
1597
+ ok {"foo9.txt"}.file_exist?
1598
+ end
1599
+ end
1600
+ spec "[!cfc40] skips non-existing files if '-c' option specified." do
1601
+ sout, serr = capture_sio do
1602
+ ok {"foo9.txt"}.not_exist?
1603
+ touch :c, "foo9.txt"
1604
+ ok {"foo9.txt"}.not_exist?
1605
+ end
1606
+ end
1607
+ spec "[!s50bp] changes only access timestamp if '-a' option specified." do |ts|
1608
+ sout, serr = capture_sio do
1609
+ ok {File.atime("foo1.txt")} == ts
1610
+ ok {File.mtime("foo1.txt")} == ts
1611
+ now1 = Time.now
1612
+ touch :a, "foo1.txt"
1613
+ now2 = Time.now
1614
+ ok {File.atime("foo1.txt")}.between?(now1, now2)
1615
+ ok {File.mtime("foo1.txt")} == ts
1616
+ end
1617
+ end
1618
+ spec "[!k7zap] changes only modification timestamp if '-m' option specified." do |ts|
1619
+ sout, serr = capture_sio do
1620
+ ok {File.atime("foo1.txt")} == ts
1621
+ ok {File.mtime("foo1.txt")} == ts
1622
+ now1 = Time.now
1623
+ touch :m, "foo1.txt"
1624
+ now2 = Time.now
1625
+ ok {File.atime("foo1.txt")} == ts
1626
+ ok {File.mtime("foo1.txt")}.between?(now1, now2)
1627
+ end
1628
+ end
1629
+ spec "[!b5c1n] changes both access and modification timestamps in default." do |ts|
1630
+ sout, serr = capture_sio do
1631
+ ok {File.atime("foo1.txt")} == ts
1632
+ ok {File.mtime("foo1.txt")} == ts
1633
+ now1 = Time.now
1634
+ touch "foo1.txt"
1635
+ now2 = Time.now
1636
+ ok {File.atime("foo1.txt")} != ts
1637
+ ok {File.mtime("foo1.txt")} != ts
1638
+ ok {File.atime("foo1.txt")}.between?(now1, now2)
1639
+ ok {File.mtime("foo1.txt")}.between?(now1, now2)
1640
+ end
1641
+ end
1642
+ end
1643
+
1644
+
1645
+ topic 'chmod()' do
1646
+ spec "[!pmmvj] echobacks command and arguments." do
1647
+ sout, serr = capture_sio do
1648
+ chmod "644", "foo1.txt", "foo2.txt"
1649
+ end
1650
+ ok {sout} == "$ chmod 644 foo1.txt foo2.txt\n"
1651
+ end
1652
+ spec "[!94hl9] error when mode not specified." do
1653
+ sout, serr = capture_sio do
1654
+ pr = proc { chmod() }
1655
+ ok {pr}.raise?(ArgumentError, "chmod: argument required.")
1656
+ end
1657
+ end
1658
+ spec "[!c8zhu] mode can be integer or octal string." do
1659
+ sout, serr = capture_sio do
1660
+ mode_i, mask = __chmod("chmod", [0644, "foo1.txt"], true)
1661
+ ok {mode_i} == 0644
1662
+ ok {mask} == nil
1663
+ mode_i, mask = __chmod("chmod", ["644", "foo1.txt"], true)
1664
+ ok {mode_i} == 0644
1665
+ ok {mask} == nil
1666
+ end
1667
+ end
1668
+ spec "[!j3nqp] error when integer mode is invalid." do
1669
+ sout, serr = capture_sio do
1670
+ pr = proc { chmod 888, "foo1.txt" }
1671
+ ok {pr}.raise?(ArgumentError, "chmod: 888: Invalid file mode.")
1672
+ end
1673
+ end
1674
+ spec "[!ox3le] converts 'u+r' style mode into mask." do
1675
+ sout, serr = capture_sio do
1676
+ [
1677
+ ["u+r", 0400], ["u+w", 0200], ["u+x", 0100], ["u+s", 04000], ["u+t", 00000],
1678
+ ["g+r", 0040], ["g+w", 0020], ["g+x", 0010], ["g+s", 02000], ["g+t", 00000],
1679
+ ["o+r", 0004], ["o+w", 0002], ["o+x", 0001], ["o+s", 00000], ["o+t", 00000],
1680
+ ["a+r", 0444], ["a+w", 0222], ["a+x", 0111], ["a+s", 06000], ["a+t", 01000],
1681
+ ].each do |mode, expected|
1682
+ mode_i, mask = __chmod("chmod", [mode, "foo1.txt"], true)
1683
+ ok {mode_i} == nil
1684
+ ok {mask} == expected
1685
+ end
1686
+ end
1687
+ end
1688
+ spec "[!axqed] error when mode is invalid." do
1689
+ sout, serr = capture_sio do
1690
+ pr = proc { chmod "888", "foo1.txt" }
1691
+ ok {pr}.raise?(ArgumentError, "chmod: 888: Invalid file mode.")
1692
+ pr = proc { chmod "+r", "foo1.txt" }
1693
+ ok {pr}.raise?(ArgumentError, "chmod: +r: Invalid file mode.")
1694
+ end
1695
+ end
1696
+ spec "[!ru371] expands file pattern." do
1697
+ sout, serr = capture_sio do
1698
+ ok {File.readable?("foo1.txt")} == true
1699
+ ok {File.readable?("foo2.txt")} == true
1700
+ chmod "u-r", "foo*.txt"
1701
+ ok {File.readable?("foo1.txt")} == false
1702
+ ok {File.readable?("foo2.txt")} == false
1703
+ end
1704
+ end
1705
+ spec "[!ou3ih] error when file not exist." do
1706
+ sout, serr = capture_sio do
1707
+ pr = proc { chmod "u+r", "blabla" }
1708
+ ok {pr}.raise?(ArgumentError, "chmod: blabla: No such file or directory.")
1709
+ end
1710
+ end
1711
+ spec "[!8sd4b] error when file pattern not matched to anything." do
1712
+ sout, serr = capture_sio do
1713
+ pr = proc { chmod "u+r", "foobar*.txt" }
1714
+ ok {pr}.raise?(ArgumentError, "chmod: foobar*.txt: No such file or directory.")
1715
+ end
1716
+ end
1717
+ spec "[!q1psx] changes file mode." do
1718
+ sout, serr = capture_sio do
1719
+ mode1 = File.stat("foo1.txt").mode
1720
+ chmod "432", "foo1.txt"
1721
+ mode2 = File.stat("foo1.txt").mode
1722
+ ok {mode2} != mode1
1723
+ ok {mode2 & 0777} == 0432
1724
+ #
1725
+ chmod "u+w", "foo1.txt"
1726
+ ok {File.stat("foo1.txt").mode & 0777} == 0632
1727
+ chmod "g-x", "foo1.txt"
1728
+ ok {File.stat("foo1.txt").mode & 0777} == 0622
1729
+ chmod "a+x", "foo1.txt"
1730
+ ok {File.stat("foo1.txt").mode & 0777} == 0733
1731
+ #
1732
+ chmod "u+s", "foo1.txt"
1733
+ ok {File.stat("foo1.txt").mode & 07777} == 04733
1734
+ chmod "g+s", "foo1.txt"
1735
+ ok {File.stat("foo1.txt").mode & 07777} == 06733
1736
+ chmod "a-s", "foo1.txt"
1737
+ ok {File.stat("foo1.txt").mode & 07777} == 00733
1738
+ chmod "o+s", "foo1.txt"
1739
+ ok {File.stat("foo1.txt").mode & 07777} == 00733
1740
+ #
1741
+ chmod "u+t", "foo1.txt"
1742
+ ok {File.stat("foo1.txt").mode & 07777} == 00733
1743
+ chmod "g+t", "foo1.txt"
1744
+ ok {File.stat("foo1.txt").mode & 07777} == 00733
1745
+ chmod "o+t", "foo1.txt"
1746
+ ok {File.stat("foo1.txt").mode & 07777} == 00733
1747
+ chmod "a+t", "foo1.txt"
1748
+ ok {File.stat("foo1.txt").mode & 07777} == 01733
1749
+ chmod "a-t", "foo1.txt"
1750
+ ok {File.stat("foo1.txt").mode & 07777} == 00733
1751
+ end
1752
+ end
1753
+ spec "[!4en6n] skips symbolic links." do
1754
+ sout, serr = capture_sio do
1755
+ File.symlink("foo1.txt", "foo1.link")
1756
+ mode = File.stat("foo1.txt").mode
1757
+ chmod 0765, "foo1.link"
1758
+ ok {File.stat("foo1.txt").mode} == mode
1759
+ ok {File.stat("foo1.txt").mode | 0777} != 0765
1760
+ end
1761
+ end
1762
+ spec "[!4e7ve] changes mode recursively if '-R' option specified." do
1763
+ sout, serr = capture_sio do
1764
+ chmod :R, 0775, "d1"
1765
+ ok {File.stat("d1" ).mode & 0777} == 0775
1766
+ ok {File.stat("d1/bar.txt" ).mode & 0777} == 0775
1767
+ ok {File.stat("d1/d2/baz.txt").mode & 0777} == 0775
1768
+ end
1769
+ end
1770
+ end
1771
+
1772
+
1773
+ topic 'chown()' do
1774
+ fixture :usr do
1775
+ ENV['USER'] # TODO
1776
+ end
1777
+ fixture :grp do
1778
+ "staff" # TODO
1779
+ end
1780
+ fixture :uid do |usr|
1781
+ Etc.getpwnam(usr).uid
1782
+ end
1783
+ fixture :gid do |grp|
1784
+ Etc.getgrnam(grp).gid
1785
+ end
1786
+ #
1787
+ spec "[!5jqqv] echobacks command and arguments." do |usr, grp|
1788
+ sout, serr = capture_sio do
1789
+ chown "#{usr}:#{grp}", "foo*.txt"
1790
+ end
1791
+ ok {sout} == "$ chown #{usr}:#{grp} foo*.txt\n"
1792
+ end
1793
+ spec "[!hkxgu] error when owner not specified." do
1794
+ sout, serr = capture_sio do
1795
+ pr = proc { chown() }
1796
+ ok {pr}.raise?(ArgumentError, "chown: argument required.")
1797
+ end
1798
+ end
1799
+ spec "[!0a35v] accepts integer as user id." do |usr, uid|
1800
+ sout, serr = capture_sio do
1801
+ ok {uid}.is_a?(Integer)
1802
+ chown uid, "foo*.txt"
1803
+ ok {File.stat("foo1.txt").uid} == uid
1804
+ end
1805
+ end
1806
+ spec "[!b5qud] accepts 'user:group' argument." do |usr, grp, uid, gid|
1807
+ sout, serr = capture_sio do
1808
+ chown "#{usr}:#{grp}", "foo*.txt"
1809
+ ok {File.stat("foo1.txt").uid} == uid
1810
+ ok {File.stat("foo1.txt").gid} == gid
1811
+ end
1812
+ end
1813
+ spec "[!18gf0] accepts 'user' argument." do |usr, grp, uid|
1814
+ sout, serr = capture_sio do
1815
+ chown usr, "foo*.txt"
1816
+ ok {File.stat("foo1.txt").uid} == uid
1817
+ #
1818
+ chown usr+":", "foo*.txt"
1819
+ ok {File.stat("foo1.txt").uid} == uid
1820
+ end
1821
+ end
1822
+ spec "[!mw5tg] accepts ':group' argument." do |usr, grp, gid|
1823
+ sout, serr = capture_sio do
1824
+ chown ":#{grp}", "foo*.txt"
1825
+ ok {File.stat("foo1.txt").gid} == gid
1826
+ end
1827
+ end
1828
+ spec "[!jyecc] converts user name into user id." do |usr, grp, uid|
1829
+ sout, serr = capture_sio do
1830
+ uid, gid = __chown("chown", [usr, "foo*.txt"], true)
1831
+ ok {uid} == uid
1832
+ end
1833
+ end
1834
+ spec "[!kt7mp] error when invalid user name specified." do |usr, grp|
1835
+ sout, serr = capture_sio do
1836
+ pr = proc { chown "honyara", "foo*.txt" }
1837
+ ok {pr}.raise?(ArgumentError, "chown: honyara: unknown user name.")
1838
+ end
1839
+ end
1840
+ spec "[!f7ye0] converts group name into group id." do |usr, grp, uid, gid|
1841
+ sout, serr = capture_sio do
1842
+ uid, gid = __chown("chown", ["#{usr}:#{grp}", "foo*.txt"], true)
1843
+ ok {uid} == uid
1844
+ ok {gid} == gid
1845
+ end
1846
+ end
1847
+ spec "[!szlsb] error when invalid group name specified." do
1848
+ sout, serr = capture_sio do
1849
+ pr = proc { chown ":honyara", "foo*.txt" }
1850
+ ok {pr}.raise?(ArgumentError, "chown: honyara: unknown group name.")
1851
+ end
1852
+ end
1853
+ spec "[!138eh] expands file pattern." do |usr, grp, uid|
1854
+ sout, serr = capture_sio do
1855
+ chown usr, "foo*.txt"
1856
+ ok {File.stat("foo1.txt").uid} == uid
1857
+ ok {File.stat("foo2.txt").uid} == uid
1858
+ end
1859
+ end
1860
+ spec "[!tvpey] error when file not exist." do |usr, grp|
1861
+ sout, serr = capture_sio do
1862
+ pr = proc { chown usr, "blabla.txt" }
1863
+ ok {pr}.raise?(ArgumentError, "chown: blabla.txt: No such file or directory.")
1864
+ end
1865
+ end
1866
+ spec "[!ovkk8] error when file pattern not matched to anything." do |usr, grp|
1867
+ sout, serr = capture_sio do
1868
+ pr = proc { chown usr, "blabla*.txt" }
1869
+ ok {pr}.raise?(ArgumentError, "chown: blabla*.txt: No such file or directory.")
1870
+ end
1871
+ end
1872
+ spec "[!7tf3k] changes file mode." do |usr, grp, uid, gid|
1873
+ sout, serr = capture_sio do
1874
+ chown "#{usr}:#{grp}", "foo1.txt"
1875
+ ok {File.stat("foo1.txt").uid} == uid
1876
+ ok {File.stat("foo1.txt").gid} == gid
1877
+ end
1878
+ end
1879
+ spec "[!m6mrg] skips symbolic links." do |usr, grp|
1880
+ sout, serr = capture_sio do
1881
+ File.symlink "foo1.txt", "foo1.link"
1882
+ File.unlink "foo1.txt"
1883
+ chown "#{usr}:#{grp}", "foo1.link" # not raise error
1884
+ end
1885
+ end
1886
+ spec "[!b07ff] changes file mode recursively if '-R' option specified." do |usr, grp, uid|
1887
+ sout, serr = capture_sio do
1888
+ chown :R, "#{usr}", "d1"
1889
+ ok {File.stat("d1/d2/baz.txt").uid} == uid
1890
+ end
1891
+ end
1892
+ end
1893
+
1894
+
1895
+ topic 'store()' do
1896
+ spec "[!9wr1o] error when `to:` keyword argument not specified." do
1897
+ sout, serr = capture_sio do
1898
+ pr = proc { store "foo*.txt", "d1" }
1899
+ ok {pr}.raise?(ArgumentError, /^missing keyword: :?to$/)
1900
+ end
1901
+ end
1902
+ spec "[!n43u2] echoback command and arguments." do
1903
+ sout, serr = capture_sio do
1904
+ store "foo*.txt", to: "d1"
1905
+ end
1906
+ ok {sout} == "$ store foo*.txt d1\n"
1907
+ end
1908
+ spec "[!588e5] error when destination directory not exist." do
1909
+ sout, serr = capture_sio do
1910
+ pr = proc { store "foo*.txt", to: "d9" }
1911
+ ok {pr}.raise?(ArgumentError, "store: d9: directory not found.")
1912
+ end
1913
+ end
1914
+ spec "[!lm43y] error when destination pattern matched to multiple filenames." do
1915
+ sout, serr = capture_sio do
1916
+ pr = proc { store "d1", to: "foo*.txt" }
1917
+ ok {pr}.raise?(ArgumentError, "store: foo*.txt: unexpectedly matched to multiple filenames (foo1.txt, foo2.txt).")
1918
+ end
1919
+ end
1920
+ spec "[!u5zoy] error when destination is not a directory." do
1921
+ sout, serr = capture_sio do
1922
+ pr = proc { store "foo*.txt", to: "d1/bar.txt" }
1923
+ ok {pr}.raise?(ArgumentError, "store: d1/bar.txt: Not a directory.")
1924
+ end
1925
+ end
1926
+ spec "[!g1duw] error when absolute path specified." do
1927
+ sout, serr = capture_sio do
1928
+ pr = proc { store "/tmp", to: "d1" }
1929
+ ok {pr}.raise?(ArgumentError, "store: /tmp: absolute path not expected (only relative path expected).")
1930
+ end
1931
+ end
1932
+ spec "[!je1i2] error when file not exist but '-f' option not specified." do
1933
+ sout, serr = capture_sio do
1934
+ pr = proc { store "blabla*.txt", to: "d1"}
1935
+ ok {pr}.raise?(ArgumentError, "store: blabla*.txt: file or directory not found (add '-f' option to ignore missing files).")
1936
+ end
1937
+ end
1938
+ spec "[!5619q] (store) error when target file or directory already exists." do
1939
+ sout, serr = capture_sio do
1940
+ dummy_file "d1/foo2.txt", "dummy"
1941
+ pr = proc { store "foo*.txt", to: "d1" }
1942
+ ok {pr}.raise?(ArgumentError, "store: d1/foo2.txt: destination file or directory already exists.")
1943
+ end
1944
+ end
1945
+ spec "[!4y4zy] copy files with keeping filepath." do
1946
+ sout, serr = capture_sio do
1947
+ dummy_dir("d9")
1948
+ store "foo*.txt", "d1", to: "d9"
1949
+ ok {"d9/foo1.txt"}.file_exist?
1950
+ ok {"d9/foo2.txt"}.file_exist?
1951
+ ok {"d9/d1/bar.txt"}.file_exist?
1952
+ ok {"d9/d1/d2/baz.txt"}.file_exist?
1953
+ end
1954
+ end
1955
+ spec "[!f0n0y] copy timestamps if '-p' option specified." do
1956
+ sout, serr = capture_sio do
1957
+ dummy_dir "d9"
1958
+ atime1 = File.atime("d1/d2/baz.txt")
1959
+ mtime1 = File.mtime("d1/d2/baz.txt")
1960
+ atime2 = (x = atime1 - 600; Time.new(x.year, x.month, x.day, x.hour, x.min, x.sec))
1961
+ mtime2 = (x = mtime1 - 900; Time.new(x.year, x.month, x.day, x.hour, x.min, x.sec))
1962
+ File.utime(atime2, mtime2, "d1/d2/baz.txt")
1963
+ store :p, "d1/**/*.txt", to: "d9"
1964
+ ok {File.atime("d1/d2/baz.txt")} != atime1
1965
+ ok {File.mtime("d1/d2/baz.txt")} != mtime1
1966
+ ok {File.atime("d1/d2/baz.txt")} != atime2
1967
+ ok {File.mtime("d1/d2/baz.txt")} == mtime2
1968
+ end
1969
+ end
1970
+ spec "[!w8oq6] creates hard links if '-l' option specified." do
1971
+ sout, serr = capture_sio do
1972
+ dummy_dir "d9"
1973
+ store :l, "foo*.txt", "d1/**/*.txt", to: "d9"
1974
+ ok {File.identical?("foo1.txt", "d9/foo1.txt")} == true
1975
+ ok {File.identical?("foo2.txt", "d9/foo2.txt")} == true
1976
+ ok {File.identical?("d1/bar.txt", "d9/d1/bar.txt")} == true
1977
+ ok {File.identical?("d1/d2/baz.txt", "d9/d1/d2/baz.txt")} == true
1978
+ end
1979
+ end
1980
+ spec "[!7n869] error when copying supecial files such as character device." do
1981
+ sout, serr = capture_sio do
1982
+ dummy_dir "d9"
1983
+ dir = File.join(Dir.pwd(), "d9")
1984
+ Dir.chdir "/dev" do
1985
+ pr = proc { store "./null", to: dir }
1986
+ ok {pr}.raise?(ArgumentError, "store: ./null: cannot copy characterSpecial file.")
1987
+ end
1988
+ end
1989
+ end
1990
+ end
1991
+
1992
+ topic 'store!()' do
1993
+ spec "[!cw08t] (store!) overwrites existing files." do
1994
+ dummy_file "d1/foo2.txt", "dummy"
1995
+ sout, serr = capture_sio do
1996
+ store! "foo*.txt", to: "d1"
1997
+ ok {"d1/foo2.txt"}.file_exist?
1998
+ ok {File.read("d1/foo2.txt")} != "dummy"
1999
+ ok {File.read("d1/foo2.txt")} == File.read("foo2.txt")
2000
+ end
2001
+ end
2002
+ end
2003
+
2004
+
2005
+ topic 'zip()' do
2006
+ spec "[!zzvuk] requires 'zip' gem automatically." do
2007
+ skip_when defined?(::Zip) != nil, "zip gem already required."
2008
+ ok {defined?(::Zip)} == nil
2009
+ sout, serr = capture_sio do
2010
+ zip "foo.zip", "foo*.txt"
2011
+ end
2012
+ ok {defined?(::Zip)} != false
2013
+ ok {defined?(::Zip)} == 'constant'
2014
+ end
2015
+ spec "[!zk1qt] echoback command and arguments." do
2016
+ sout, serr = capture_sio do
2017
+ zip "foo.zip", "foo*.txt"
2018
+ end
2019
+ ok {sout} == "$ zip foo.zip foo*.txt\n"
2020
+ end
2021
+ spec "[!lrnj7] zip filename required." do
2022
+ sout, serr = capture_sio do
2023
+ pr = proc { zip :r }
2024
+ ok {pr}.raise?(ArgumentError, "zip: zip filename required.")
2025
+ end
2026
+ end
2027
+ spec "[!umbal] error when zip file glob pattern matched to mutilple filenames." do
2028
+ sout, serr = capture_sio do
2029
+ dummy_file "foo1.zip"
2030
+ dummy_file "foo2.zip"
2031
+ pr = proc { zip! "foo*.zip", "foo*.txt" }
2032
+ ok {pr}.raise?(ArgumentError, "zip: foo*.zip: matched to multiple filenames (foo1.zip, foo2.zip).")
2033
+ end
2034
+ end
2035
+ spec "[!oqzna] (zip) raises error if zip file already exists." do
2036
+ sout, serr = capture_sio do
2037
+ dummy_file "foo.zip"
2038
+ pr = proc { zip "foo.zip", "foo*.txt" }
2039
+ ok {pr}.raise?(ArgumentError, "zip: foo.zip: already exists (to overwrite it, call `zip!` command instead of `zip` command).")
2040
+ end
2041
+ end
2042
+ spec "[!uu8uz] expands glob pattern." do
2043
+ sout, serr = capture_sio do
2044
+ pr = proc { zip "foo.zip", "foo*.txt" }
2045
+ ok {pr}.NOT.raise?(ArgumentError)
2046
+ end
2047
+ end
2048
+ spec "[!nahxa] error if file not exist." do
2049
+ sout, serr = capture_sio do
2050
+ pr = proc { zip "foo.zip", "blabla*.txt" }
2051
+ ok {pr}.raise?(ArgumentError, "zip: blabla*.txt: file or directory not found.")
2052
+ end
2053
+ end
2054
+ spec "[!qsp7c] cannot specify absolute path." do
2055
+ sout, serr = capture_sio do
2056
+ pr = proc { zip "foo.zip", "/tmp" }
2057
+ ok {pr}.raise?(ArgumentError, "zip: /tmp: not support absolute path.")
2058
+ end
2059
+ end
2060
+ spec "[!p8alf] creates zip file." do
2061
+ sout, serr = capture_sio do
2062
+ zip "foo.zip", "foo*.txt"
2063
+ ok {"foo.zip"}.file_exist?
2064
+ unzip_cmd = capture2 "which unzip"
2065
+ if ! unzip_cmd.strip.empty?
2066
+ output = capture2 "#{unzip_cmd.strip} -l foo.zip"
2067
+ ok {output} =~ /foo1\.txt/
2068
+ ok {output} =~ /foo2\.txt/
2069
+ end
2070
+ end
2071
+ end
2072
+ spec "[!3sxmg] supports complession level (0~9)." do
2073
+ sout, serr = capture_sio do
2074
+ dummy_file "foo3.txt", "foobar"*10
2075
+ zip :'0', "foo0.zip", "foo*.txt"
2076
+ ok {"foo0.zip"}.file_exist?
2077
+ zip :'1', "foo1.zip", "foo*.txt"
2078
+ ok {"foo1.zip"}.file_exist?
2079
+ zip :'9', "foo9.zip", "foo*.txt"
2080
+ ok {"foo9.zip"}.file_exist?
2081
+ #
2082
+ ok {File.size("foo9.zip")} <= File.size("foo1.zip")
2083
+ ok {File.size("foo1.zip")} < File.size("foo0.zip")
2084
+ end
2085
+ end
2086
+ spec "[!h7yxl] restores value of `Zip.default_compression`." do
2087
+ val = Zip.default_compression
2088
+ sout, serr = capture_sio do
2089
+ zip :'9', "foo9.zip", "foo*.txt"
2090
+ end
2091
+ ok {Zip.default_compression} == val
2092
+ end
2093
+ spec "[!bgdg7] adds files recursively into zip file if '-r' option specified." do
2094
+ sout, serr = capture_sio do
2095
+ zip :r, "foo.zip", "d1"
2096
+ unzip_cmd = capture2 "which unzip"
2097
+ if ! unzip_cmd.strip.empty?
2098
+ output = capture2 "#{unzip_cmd.strip} -l foo.zip"
2099
+ ok {output} =~ /d1\/bar\.txt/
2100
+ ok {output} =~ /d1\/d2\/baz\.txt/
2101
+ end
2102
+ end
2103
+ end
2104
+ spec "[!jgt96] error when special file specified." do
2105
+ sout, serr = capture_sio do
2106
+ here = Dir.pwd
2107
+ Dir.chdir "/dev" do
2108
+ pr = proc { zip File.join(here, "foo.zip"), "./null" }
2109
+ ok {pr}.raise?(ArgumentError, "zip: ./null: characterSpecial file not supported.")
2110
+ end
2111
+ end
2112
+ end
2113
+ spec "[!fvvn8] returns zip file object." do
2114
+ sout, serr = capture_sio do
2115
+ ret = zip "foo.zip", "foo*.txt"
2116
+ ok {ret}.is_a?(Zip::File)
2117
+ end
2118
+ end
2119
+ end
2120
+
2121
+ topic 'zip!()' do
2122
+ spec "[!khbiq] zip filename can be glob pattern." do
2123
+ sout, serr = capture_sio do
2124
+ dummy_file "foo.zip"
2125
+ pr = proc { zip! "*.zip", "foo*.txt" }
2126
+ ok {pr}.NOT.raise?(ArgumentError)
2127
+ end
2128
+ end
2129
+ spec "[!e995z] (zip!) removes zip file if exists." do
2130
+ sout, serr = capture_sio do
2131
+ dummy_file "foo.zip"
2132
+ pr = proc { zip! "foo.zip", "foo*.txt" }
2133
+ ok {pr}.NOT.raise?(ArgumentError)
2134
+ end
2135
+ end
2136
+ end
2137
+
2138
+
2139
+ topic 'unzip()' do
2140
+ spec "[!eqx48] requires 'zip' gem automatically." do
2141
+ skip_when defined?(::Zip) != nil, "zip gem already required."
2142
+ ok {defined?(::Zip)} == nil
2143
+ sout, serr = capture_sio do
2144
+ begin
2145
+ unzip "foo.zip"
2146
+ rescue
2147
+ end
2148
+ end
2149
+ ok {defined?(::Zip)} == 'constant'
2150
+ end
2151
+ spec "[!0tedi] extract zip file." do
2152
+ sout, serr = capture_sio do
2153
+ zip "foo.zip", "foo*.txt"
2154
+ rm "foo*.txt"
2155
+ ok {"foo1.txt"}.not_exist?
2156
+ ok {"foo2.txt"}.not_exist?
2157
+ unzip "foo.zip"
2158
+ ok {"foo1.txt"}.file_exist?
2159
+ ok {"foo2.txt"}.file_exist?
2160
+ end
2161
+ end
2162
+ spec "[!ednxk] echoback command and arguments." do
2163
+ sout, serr = capture_sio do
2164
+ zip "foo.zip", "foo*.txt"
2165
+ File.unlink("foo1.txt", "foo2.txt")
2166
+ unzip "foo.zip"
2167
+ end
2168
+ ok {sout} == ("$ zip foo.zip foo*.txt\n"\
2169
+ "$ unzip foo.zip\n")
2170
+ end
2171
+ spec "[!1lul7] error if zip file not specified." do
2172
+ sout, serr = capture_sio do
2173
+ pr = proc { unzip() }
2174
+ ok {pr}.raise?(ArgumentError, "unzip: zip filename required.")
2175
+ pr = proc { unzip :d }
2176
+ ok {pr}.raise?(ArgumentError, "unzip: zip filename required.")
2177
+ end
2178
+ end
2179
+ spec "[!0yyg8] target directory should not exist, or be empty." do
2180
+ sout, serr = capture_sio do
2181
+ zip "foo.zip", "foo*.txt"
2182
+ mkdir "d8"
2183
+ unzip :d, "d8", "foo.zip" # empty dir
2184
+ unzip :d, "d9", "foo.zip" # non-existing dir
2185
+ end
2186
+ end
2187
+ spec "[!1ls2h] error if target directory not empty." do
2188
+ sout, serr = capture_sio do
2189
+ zip "foo.zip", "foo*.txt"
2190
+ pr = proc { unzip :d, "d1", "foo.zip" }
2191
+ ok {pr}.raise?(ArgumentError, "unzip: d1: directory not empty.")
2192
+ end
2193
+ end
2194
+ spec "[!lb6r5] error if target directory is not a directory." do
2195
+ sout, serr = capture_sio do
2196
+ pr = proc { unzip :d, "foo1.txt", "foo2.txt" }
2197
+ ok {pr}.raise?(ArgumentError, "unzip: foo1.txt: not a directory.")
2198
+ end
2199
+ end
2200
+ spec "[!dzk7c] creates target directory if not exists." do
2201
+ sout, serr = capture_sio do
2202
+ zip "foo.zip", "foo*.txt"
2203
+ unzip :d, "d8/d9", "*.zip"
2204
+ ok {"d8/d9/foo1.txt"}.file_exist?
2205
+ ok {"d8/d9/foo2.txt"}.file_exist?
2206
+ end
2207
+ end
2208
+ spec "[!o1ot5] expands glob pattern." do
2209
+ sout, serr = capture_sio do
2210
+ zip "foo.zip", "foo*.txt"; File.unlink("foo1.txt", "foo2.txt")
2211
+ unzip "*.zip"
2212
+ ok {"foo1.txt"}.file_exist?
2213
+ ok {"foo2.txt"}.file_exist?
2214
+ end
2215
+ end
2216
+ spec "[!92bh4] error if glob pattern matched to multiple filenames." do
2217
+ sout, serr = capture_sio do
2218
+ pr = proc { unzip "*.txt" }
2219
+ ok {pr}.raise?(ArgumentError, "unzip: *.txt: matched to multiple filenames (foo1.txt foo2.txt).")
2220
+ end
2221
+ end
2222
+ spec "[!esnke] error if zip file not found." do
2223
+ sout, serr = capture_sio do
2224
+ pr = proc { unzip "*.zip" }
2225
+ ok {pr}.raise?(ArgumentError, "unzip: *.zip: zip file not found.")
2226
+ end
2227
+ end
2228
+ spec "[!ekllx] (unzip) error when file already exists." do
2229
+ sout, serr = capture_sio do
2230
+ zip "foo.zip", "foo*.txt"
2231
+ pr = proc { unzip "foo.zip" }
2232
+ ok {pr}.raise?(ArgumentError, "unzip: foo1.txt: file already exists (to overwrite it, call `unzip!` command instead of `unzip` command).")
2233
+ end
2234
+ end
2235
+ spec "[!zg60i] error if file has absolute path." do
2236
+ skip_when true, "cannot create zip file containing absolute path."
2237
+ end
2238
+ spec "[!ikq5w] if filenames are specified, extracts files matched to them." do
2239
+ sout, serr = capture_sio do
2240
+ zip "foo.zip", "foo*.txt"; File.unlink("foo1.txt", "foo2.txt")
2241
+ unzip "foo.zip", "*2.txt"
2242
+ ok {"foo1.txt"}.not_exist?
2243
+ ok {"foo2.txt"}.file_exist?
2244
+ end
2245
+ end
2246
+ spec "[!dy4r4] if '-d' option specified, extracts files under target directory." do
2247
+ sout, serr = capture_sio do
2248
+ zip "foo.zip", "foo*.txt"
2249
+ unzip :d, "d9", "foo.zip"
2250
+ ok {"d9/foo1.txt"}.file_exist?
2251
+ ok {"d9/foo2.txt"}.file_exist?
2252
+ end
2253
+ end
2254
+ spec "[!5u645] if '-d' option not specified, extracts files under current directory." do
2255
+ sout, serr = capture_sio do
2256
+ zip "foo.zip", "foo*.txt"; File.unlink("foo1.txt", "foo2.txt")
2257
+ unzip "foo.zip"
2258
+ ok {"foo1.txt"}.file_exist?
2259
+ ok {"foo2.txt"}.file_exist?
2260
+ end
2261
+ end
2262
+ end
2263
+
2264
+ topic 'unzip!()' do
2265
+ spec "[!06nyv] (unzip!) overwrites existing files." do
2266
+ sout, serr = capture_sio do
2267
+ zip "foo.zip", "foo*.txt"
2268
+ File.unlink("foo2.txt")
2269
+ ok {"foo1.txt"}.file_exist?
2270
+ ok {"foo2.txt"}.not_exist?
2271
+ pr = proc { unzip! "foo.zip" }
2272
+ ok {pr}.NOT.raise?(ArgumentError)
2273
+ ok {"foo1.txt"}.file_exist?
2274
+ ok {"foo2.txt"}.file_exist?
2275
+ end
2276
+ end
2277
+ end
2278
+
2279
+
2280
+ topic 'time()' do
2281
+ spec "[!ddl3a] measures elapsed time of block and reports into stderr." do
2282
+ sout, serr = capture_sio do
2283
+ time do
2284
+ puts "sleep 1..."
2285
+ sleep 1
2286
+ end
2287
+ end
2288
+ ok {sout} == "sleep 1...\n"
2289
+ ok {serr} =~ /\A\n 1\.\d\d\ds real 0\.\d\d\ds user 0\.\d\d\ds sys\n\z/
2290
+ end
2291
+ spec "[!sjf80] (unzip!) `Zip.on_exists_proc` should be recovered." do
2292
+ sout, serr = capture_sio do
2293
+ ok {Zip.on_exists_proc} == false
2294
+ zip "foo.zip", "foo1.txt", "foo2.txt"
2295
+ unzip! "foo.zip"
2296
+ ok {Zip.on_exists_proc} == false
2297
+ end
2298
+ end
2299
+ end
2300
+
2301
+
2302
+ end
2303
+
2304
+
2305
+ end