cotta 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,90 @@
1
+ module Cotta
2
+ class PhysicalSystem
3
+ def initialize
4
+ end
5
+
6
+ # Invoke the command passed in through the CommandRunner
7
+ # and pass in the closure.
8
+ def shell(command, &block)
9
+ runner = CommandRunner.new(command)
10
+ runner.execute(&block)
11
+ end
12
+
13
+ def environment!(variable)
14
+ value = ENV[variable]
15
+ raise "#{variable} environment variable not found" unless value
16
+ return value
17
+ end
18
+
19
+ def environment(variable, default)
20
+ value = ENV[variable]
21
+ value = default unless value
22
+ return value
23
+ end
24
+
25
+ def dir_exists?(dir_path)
26
+ return FileTest.directory?(dir_path)
27
+ end
28
+
29
+ def file_exists?(file_path)
30
+ return FileTest.file?(file_path)
31
+ end
32
+
33
+ def dir_stat(path)
34
+ File.stat(path)
35
+ end
36
+
37
+ def file_stat(path)
38
+ File.stat(path)
39
+ end
40
+
41
+ def list(dir_path)
42
+ Dir.entries(dir_path).find_all {|item| item != '.' && item != '..'}
43
+ end
44
+
45
+ def mkdir(dir_path)
46
+ Dir.mkdir(dir_path)
47
+ end
48
+
49
+ def io(file_path, argument)
50
+ return File.open(file_path, argument)
51
+ end
52
+
53
+ def delete_file(file_path)
54
+ return File.delete(file_path)
55
+ end
56
+
57
+ def delete_dir(dir_path)
58
+ return Dir.delete(dir_path)
59
+ end
60
+
61
+ def copy_file(source, target)
62
+ FileUtils.copy(source, target)
63
+ end
64
+
65
+ def move_file(source, target)
66
+ FileUtils.move(source, target)
67
+ end
68
+
69
+ def copy_dir(source, target)
70
+ FileUtils.copy_entry(source, target)
71
+ end
72
+
73
+ def move_dir(source, target)
74
+ FileUtils.mv(source, target)
75
+ end
76
+
77
+ def chdir(target, &block)
78
+ Dir.chdir(target, &block)
79
+ end
80
+
81
+ def pwd
82
+ Dir.pwd
83
+ end
84
+
85
+ def ==(other)
86
+ other.class == PhysicalSystem
87
+ end
88
+
89
+ end
90
+ end
@@ -0,0 +1 @@
1
+ 1.0.0
@@ -0,0 +1,42 @@
1
+ require File.dirname(__FILE__) + '/../test'
2
+
3
+ module Cotta
4
+ describe CommandInterface do
5
+ it 'delegate output to io' do
6
+ io = mock('io')
7
+ io.should_receive(:puts).with('content to output')
8
+ interface = CommandInterface.new(io)
9
+ interface.puts('content to output')
10
+ end
11
+
12
+ it 'prompt outputs a message and get the response from io' do
13
+ io = mock('io')
14
+ io.should_receive(:puts).with('question')
15
+ io.should_receive(:gets).and_return('answer')
16
+ interface = CommandInterface.new(io)
17
+ actual = interface.prompt('question')
18
+ actual.should == 'answer'
19
+ end
20
+
21
+ it 'prompt for choice' do
22
+ io = mock('io')
23
+ io.should_receive(:puts).once.with('select one of the following')
24
+ io.should_receive(:puts).once.with('[1] item one')
25
+ io.should_receive(:puts).once.with('[2] item two')
26
+ io.should_receive(:gets).once.and_return('2')
27
+ interface = CommandInterface.new(io)
28
+ actual = interface.prompt_for_choice('select one of the following', ['item one', 'item two'])
29
+ actual.should == 'item two'
30
+ end
31
+
32
+ it 'prompt for choice returns nil for invalid choice' do
33
+ io = mock('io')
34
+ io.should_receive(:puts).with('select one')
35
+ io.should_receive(:puts).exactly(2).times
36
+ io.should_receive(:gets).and_return('9')
37
+ interface = CommandInterface.new(io)
38
+ actual = interface.prompt_for_choice('select one', ['one', 'two'])
39
+ actual.should be_nil
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,24 @@
1
+ require File.dirname(__FILE__) + '/../test'
2
+
3
+ module Cotta
4
+ describe CommandRunner do
5
+ it 'return content' do
6
+ runner = CommandRunner.new('ruby --version')
7
+ runner.execute[0..3].should == 'ruby'
8
+ end
9
+
10
+ it 'raise error on abnormal exits' do
11
+ runner = CommandRunner.new('ruby ----')
12
+ Proc.new{runner.execute}.should raise_error(CommandError)
13
+ end
14
+
15
+ it 'take closure as io processor' do
16
+ runner = CommandRunner.new('echo test')
17
+ message_logged = nil
18
+ runner.execute {|io| message_logged = io.gets}
19
+ message_logged.should == "test\n"
20
+ end
21
+
22
+ end
23
+
24
+ end
@@ -0,0 +1,3 @@
1
+ line one
2
+ line two
3
+ line three
@@ -0,0 +1,202 @@
1
+ require File.dirname(__FILE__) + '/../test'
2
+ require 'spec'
3
+ require 'pathname'
4
+
5
+ describe 'CottaDirBehaviors', :shared=>true do
6
+ before do
7
+ @system = create_system
8
+ @root = Cotta::CottaDir.new(@system, Pathname.new('.'))
9
+ @dir = @root.dir('dir')
10
+ @current = Dir.pwd
11
+ end
12
+
13
+ after do
14
+ Dir.chdir @current
15
+ end
16
+
17
+ it 'load dir with basic information' do
18
+ @dir.name.should == 'dir'
19
+ end
20
+
21
+ it 'should show path on to_s for convenience in scripting' do
22
+ @dir.to_s.should == 'dir'
23
+ "#{@dir}".should == 'dir'
24
+ end
25
+
26
+ it 'dir objects are value objects, equal on system and path' do
27
+ (Cotta::CottaDir.new(@system, Pathname.new('dir')) == @dir).should == true
28
+ end
29
+
30
+ it 'dir should not be equal if path different' do
31
+ (Cotta::CottaDir.new(@system, Pathname.new('/dir')) == @dir).should == false
32
+ end
33
+
34
+ it 'should support relative path' do
35
+ sub_dir = @dir.dir('one/two/three')
36
+ sub_dir.relative_path_from(@dir).to_s.should == 'one/two/three'
37
+ end
38
+
39
+ it 'should know its parent' do
40
+ @dir.parent.name.should == '.'
41
+ @dir.parent.parent.should be_nil
42
+ end
43
+
44
+ it 'should look up parent' do
45
+ sub_dir = @dir.dir('one/two/three')
46
+ result = sub_dir.parent {|dir| dir.name == 'one'}
47
+ result.should == @dir.dir('one')
48
+ end
49
+
50
+ it 'should return nil if parent lookup fails' do
51
+ sub_dir = @dir.dir('one/two/three')
52
+ sub_dir.parent {|dir| false}.should be_nil
53
+ end
54
+
55
+ it 'should raise error if not exits stat' do
56
+ Proc.new {
57
+ @dir.stat
58
+ }.should raise_error(Errno::ENOENT)
59
+ end
60
+
61
+ it 'support stat' do
62
+ @dir.mkdirs
63
+ @dir.stat.should_not be_nil
64
+ end
65
+
66
+ it 'dir should handle root dir' do
67
+ dir = Cotta::CottaDir.new(@system, Pathname.new('/root'))
68
+ dir.parent.path.should == Pathname.new('/')
69
+ dir.parent.name.should == '/'
70
+ dir.parent.exists?.should == true
71
+ dir.parent.root?.should == true
72
+ dir.parent.parent.should be_nil
73
+ end
74
+
75
+ it 'dir should handle root dir for drive letters' do
76
+ dir = Cotta::CottaDir.new(@system, Pathname.new('C:/Windows'))
77
+ dir.name.should == 'Windows'
78
+ dir.parent.path.should == Pathname.new('C:/')
79
+ dir.parent.name.should == 'C:/'
80
+ end
81
+
82
+ it 'dir should return sub directory' do
83
+ @dir.dir('sub').path.should == Pathname.new('dir/sub')
84
+ @dir.dir('sub').parent.should == @dir
85
+ end
86
+
87
+ it 'dir should return a directory from a relative pathname' do
88
+ @dir.dir(Pathname.new('one/two/three')).should == @dir.dir('one').dir('two').dir('three')
89
+ end
90
+
91
+ it 'should get file in current directory' do
92
+ file = @dir.file('file.txt')
93
+ file.name.should == 'file.txt'
94
+ file.path.should == Pathname.new('dir/file.txt')
95
+ file.parent.should == @dir
96
+ end
97
+
98
+ it 'should create dir and its parent' do
99
+ dir = @dir.dir('one').dir('two')
100
+ dir.exists?.should == false
101
+ dir.parent.exists?.should == false
102
+ dir.mkdirs
103
+ dir.exists?.should == true
104
+ dir.parent.exists?.should == true
105
+ end
106
+
107
+ it 'should delete dir and its children' do
108
+ dir = @dir.dir('one').dir('two').dir('three')
109
+ dir.mkdirs
110
+ @dir.exists?.should == true
111
+ @dir.delete
112
+ dir.exists?.should == false
113
+ @dir.exists?.should == false
114
+ end
115
+
116
+ it 'should do nothing if dir does not exist' do
117
+ dir = @dir.dir('one/two')
118
+ dir.delete
119
+ dir.delete
120
+ end
121
+
122
+ it 'should do nothing on mkdir if dir already exists' do
123
+ dir = @dir.dir('one').dir('two')
124
+ dir.mkdirs
125
+ dir.mkdirs
126
+ end
127
+
128
+ it 'should list dirs' do
129
+ @dir.dir('one').mkdirs
130
+ @dir.file('one.txt').save
131
+ actual_dir_list = @dir.list
132
+ actual_dir_list.size.should == 2
133
+ actual_dir_list[0].name.should == 'one'
134
+ actual_dir_list[0].list.size.should == 0
135
+ actual_dir_list[1].name.should == 'one.txt'
136
+ actual_dir_list[1].save
137
+ end
138
+
139
+ it 'should move directory with its children' do
140
+ dir = Cotta::CottaDir.new(@system, Pathname.new('targetdir/child_dir'))
141
+ @dir.file('file.txt').save('file.txt')
142
+ @dir.dir('subdir').mkdirs
143
+ @dir.list.size.should == 2
144
+ @dir.move_to(dir)
145
+ @dir.exists?.should == false
146
+ dir.list.size.should == 2
147
+ dir.file('file.txt').load.should == 'file.txt'
148
+ dir.dir('subdir').exists?.should == true
149
+ end
150
+
151
+ it 'should copy directory with its children' do
152
+ dir = Cotta::CottaDir.new(@system, Pathname.new('targetdir/child_dir'))
153
+ @dir.file('file.txt').save('file.txt')
154
+ @dir.dir('subdir').mkdirs
155
+ @dir.list.size.should == 2
156
+ @dir.copy_to(dir)
157
+ @dir.exists?.should == true
158
+ dir.list.size.should == 2
159
+ dir.file('file.txt').load.should == 'file.txt'
160
+ dir.dir('subdir').exists?.should == true
161
+ end
162
+
163
+ it 'dir takes relative path' do
164
+ dir = Cotta::CottaDir.new(@system, Pathname.new('targetdir/dir'))
165
+ @dir.dir('one/two/three').mkdirs
166
+ @dir.dir('one').exists?.should == true
167
+ @dir.dir('one').dir('two').exists?.should == true
168
+ @dir.dir('one').dir('two').dir('three').exists?.should == true
169
+ end
170
+
171
+ it 'list on not existing directory' do
172
+ dir = Cotta::CottaDir.new(@system, Pathname.new('no/such/directory'))
173
+ Proc.new do
174
+ dir.list
175
+ end.should raise_error(Errno::ENOENT)
176
+ end
177
+
178
+ it 'allow filter for archive' do
179
+ @dir.file('in/in.txt').save('test')
180
+ @dir.file('out/out.txt').save('test')
181
+ result = @dir.archive {|entry|entry.name != 'out' }
182
+ target = result.extract(@dir.dir('extract'))
183
+ target.dir('out').should_not be_exist
184
+ target.dir('in').should be_exist
185
+ end
186
+
187
+ it 'should support changing directory' do
188
+ dir = @dir.dir('one/two')
189
+ file = @dir.file('marker').save('marker')
190
+ dir.mkdirs
191
+ result = dir.chdir
192
+ result.should == 0
193
+ current = dir.factory.pwd
194
+ value = @root.chdir do
195
+ dir.factory.pwd.should == @root
196
+ 'value'
197
+ end
198
+ value.should == 'value'
199
+ dir.factory.pwd.should == current
200
+ end
201
+
202
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec'
2
+
3
+ dir = File.dirname(__FILE__)
4
+ require dir + '/../test'
5
+ require dir + '/cotta_dir_behaviors'
6
+
7
+ describe Cotta::InMemorySystem, 'with Cotta Dir' do
8
+ it_should_behave_like 'CottaDirBehaviors'
9
+
10
+ def create_system
11
+ @system = Cotta.in_memory
12
+ end
13
+
14
+ it 'dir should not be equal if system different' do
15
+ (Cotta::CottaDir.new(Cotta.in_memory, Pathname.new('dir')) == @dir).should == false
16
+ end
17
+
18
+ it 'to_s and inspect' do
19
+ file = Cotta::CottaFile.new(@system, '/one/two/file.txt')
20
+ "#{file.to_s}".should == '/one/two/file.txt'
21
+ end
22
+
23
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec'
2
+ require File.join(File.dirname(__FILE__)) + '/../test'
3
+ require File.join(File.dirname(__FILE__)) + '/cotta_dir_behaviors'
4
+
5
+ module Cotta
6
+ describe PhysicalSystem, 'work with CottaDir' do
7
+ it_should_behave_like 'CottaDirBehaviors'
8
+
9
+ def create_system
10
+ @system = Cotta::FileFactory.new(PhysicalSystemStub.new)
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,139 @@
1
+ require File.dirname(__FILE__) + '/../test'
2
+ require 'spec'
3
+ require 'pathname'
4
+
5
+ module Cotta
6
+ describe 'CottaFileBehaviors', :shared => true do
7
+ before do
8
+ @system = create_system
9
+ @file = CottaFile.new(@system, Pathname.new('dir/file.txt'))
10
+ end
11
+ it 'file can be created with system and pathname' do
12
+ @file.name.should == 'file.txt'
13
+ @file.path.should == Pathname.new('dir/file.txt')
14
+ @file.exists?.should == false
15
+ end
16
+
17
+ it 'should return path on to_s for scripting convenience' do
18
+ @file.to_s.should == 'dir/file.txt'
19
+ "#{@file}".should == 'dir/file.txt'
20
+ end
21
+
22
+ it 'file should know properties like parent, name, etc.' do
23
+ @file.parent.should == CottaDir.new(@system, Pathname.new('dir'))
24
+ @file.name.should == 'file.txt'
25
+ @file.path.should == Pathname.new('dir/file.txt')
26
+ @file.extname.should == '.txt'
27
+ @file.basename.should == 'file'
28
+ end
29
+
30
+ it 'should support relative path' do
31
+ parent = @file.parent
32
+ file = parent.file('one/two/three.txt')
33
+ file.relative_path_from(parent).to_s.should == 'one/two/three.txt'
34
+ end
35
+
36
+ it 'file should support stat' do
37
+ @file.save('test')
38
+ @file.stat.should_not be_nil
39
+ @file.stat.size.should == 4
40
+ @file.stat.writable?.should == true
41
+ end
42
+
43
+ it 'should raise error if does not exist' do
44
+ Proc.new {
45
+ @file.stat
46
+ }.should raise_error(Errno::ENOENT)
47
+ end
48
+
49
+ it 'should load and save file content' do
50
+ @file.exists?.should == false
51
+ @file.parent.exists?.should == false
52
+ @file.save("content to save\nsecond line")
53
+ @file.exists?.should == true
54
+ @file.load.should ==("content to save\nsecond line")
55
+ end
56
+
57
+ it 'should open file to read' do
58
+ @file.save("one\ntwo")
59
+ @file.read do |file|
60
+ file.gets.should ==("one\n")
61
+ file.gets.should ==('two')
62
+ end
63
+ end
64
+
65
+ it 'should equal if same system and pathname' do
66
+ file2 = CottaFile.new(@system, Pathname.new('dir/file.txt'))
67
+ file2.should == @file
68
+ end
69
+
70
+ it 'should copy to another file' do
71
+ file2 = CottaFile.new(@system, Pathname.new('dir2/file.txt'))
72
+ file2.exists?.should == false
73
+ @file.save('my content')
74
+ @file.copy_to(file2)
75
+ file2.exists?.should == true
76
+ file2.load.should == 'my content'
77
+ end
78
+
79
+ it 'should move file' do
80
+ file2 = CottaFile.new(@system, Pathname.new('dir2/file.txt'))
81
+ file2.exists?.should == false
82
+ @file.save('content')
83
+ @file.move_to(file2)
84
+ file2.exists?.should == true
85
+ file2.load.should == 'content'
86
+ @file.exists?.should == false
87
+ end
88
+
89
+ it 'should support foreach' do
90
+ @file.write do |file|
91
+ file.puts 'line one'
92
+ file.puts 'line two'
93
+ end
94
+ collected = Array.new
95
+ @file.foreach do |line|
96
+ collected.push line
97
+ end
98
+ collected.size.should == 2
99
+ collected[0].should == "line one\n"
100
+ collected[1].should == "line two\n"
101
+ end
102
+
103
+ it 'should delete file' do
104
+ @file.save
105
+ @file.exists?.should == true
106
+ @file.delete
107
+ @file.exists?.should == false
108
+ end
109
+
110
+ it 'should raise error if file to delete does not exist' do
111
+ lambda {
112
+ @file.delete
113
+ }.should raise_error(Errno::ENOENT)
114
+ end
115
+
116
+ it 'should check timestamp to see which one is older' do
117
+ @file.save
118
+ file2 = @file.parent.file('another.txt')
119
+ sleep 1
120
+ file2.save
121
+ file2.older_than?(@file).should == false
122
+ @file.older_than?(file2).should == true
123
+ end
124
+
125
+ it 'copy binary io' do
126
+ file = FileFactory.parent_dir(__FILE__).file('logo.gif')
127
+ target = @file
128
+ file.read_binary do |input|
129
+ target.write_binary do |output|
130
+ CottaFile.copy_io(input, output)
131
+ end
132
+ end
133
+ expect_stat = file.stat
134
+ actual_stat = target.stat
135
+ actual_stat.size.should == expect_stat.size
136
+ end
137
+ end
138
+
139
+ end