benry-unixcmd 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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