iocheck 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE.txt +20 -0
- data/README.md +41 -0
- data/lib/iocheck.rb +3 -0
- data/lib/iocheck/policy.rb +61 -0
- data/lib/iocheck/test.rb +215 -0
- data/lib/iocheck/tester.rb +74 -0
- metadata +100 -0
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Akira Hayakawa
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# iocheck
|
2
|
+
|
3
|
+
## Motivation "Make Refactoring Safe."
|
4
|
+
There are thousand of softwares without test codes and
|
5
|
+
you do not have much time to add test programs to the software which disrespectively called legacy.
|
6
|
+
Some kind of softwares, such as visualization softwares, are hard to test and these softwares are usually left untested.
|
7
|
+
|
8
|
+
iocheck is in the opposite side of QuickCheck, a Haskell de-facto standard testing framework for purely functional codes.
|
9
|
+
Instead of checking the pure property of the softwares like QuickCheck does,
|
10
|
+
iocheck checks the standard output with which any kind of programming languages equip.
|
11
|
+
|
12
|
+
iocheck helps engineers working on legacy softwares who wants to refactor these software safely.
|
13
|
+
If the software is written in COBOL or Fortran, it is OK. There is a hope called iocheck.
|
14
|
+
All you have to do is write a main program to
|
15
|
+
give some states of the program running to standard output,
|
16
|
+
create test file, refactor a little bit and run iocheck to make sure no behavior changed.
|
17
|
+
|
18
|
+
In summery, standard IO is equipped with any kind of languages even if they are almost out-dated.
|
19
|
+
With iocheck testing framework, you can test legacy softwares usually written in those languages
|
20
|
+
without any learning cost.
|
21
|
+
|
22
|
+
## Installation
|
23
|
+
iocheck can be installed from rubygems repository, just type
|
24
|
+
|
25
|
+
```bash
|
26
|
+
gem install iocheck
|
27
|
+
```
|
28
|
+
|
29
|
+
## Contributing to iocheck
|
30
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
31
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
32
|
+
* Fork the project
|
33
|
+
* Start a feature/bugfix branch
|
34
|
+
* Commit and push until you are happy with your contribution
|
35
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
36
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
37
|
+
|
38
|
+
## Copyright
|
39
|
+
Copyright (c) 2011 Akira Hayakawa (@akiradeveloper). See LICENSE.txt for
|
40
|
+
further details.
|
41
|
+
|
data/lib/iocheck.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
module IOCheck
|
2
|
+
|
3
|
+
class Policy
|
4
|
+
def initialize
|
5
|
+
@result = nil
|
6
|
+
end
|
7
|
+
attr_reader :result
|
8
|
+
|
9
|
+
# Expected -> Actual -> Either
|
10
|
+
def evaluate(expected, actual)
|
11
|
+
raise NoMethodError, "You must override this method."
|
12
|
+
end
|
13
|
+
|
14
|
+
def run!(expected, actual)
|
15
|
+
@result = evaluate(expected, actual)
|
16
|
+
end
|
17
|
+
|
18
|
+
class Success
|
19
|
+
end
|
20
|
+
|
21
|
+
class Failure
|
22
|
+
def initialize(log)
|
23
|
+
@log = log
|
24
|
+
end
|
25
|
+
attr_reader :log
|
26
|
+
end
|
27
|
+
|
28
|
+
class Either
|
29
|
+
def initialize(pred, log)
|
30
|
+
if pred
|
31
|
+
@stat = ::IOCheck::Policy::Success.new
|
32
|
+
else
|
33
|
+
@stat = ::IOCheck::Policy::Failure.new(log)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
def evaluate
|
37
|
+
@stat
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class Block < Policy
|
42
|
+
def initialize(&blk)
|
43
|
+
super
|
44
|
+
@blk = blk
|
45
|
+
end
|
46
|
+
|
47
|
+
def evaluate(expected, actual)
|
48
|
+
@blk.call(expected, actual)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.by_bytes
|
53
|
+
Block.new do |expected, actual|
|
54
|
+
# TODO: Better to show the diff to the user.
|
55
|
+
Either.new(
|
56
|
+
actual.bytes == expected.bytes,
|
57
|
+
"Bytes not exactly matched").evaluate
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end # end of class Policy
|
61
|
+
end # end of module IOCheck
|
data/lib/iocheck/test.rb
ADDED
@@ -0,0 +1,215 @@
|
|
1
|
+
require_relative "policy"
|
2
|
+
require_relative "tester"
|
3
|
+
|
4
|
+
module IOCheck
|
5
|
+
|
6
|
+
def self.name(cmd)
|
7
|
+
cn = cmd.clone
|
8
|
+
if cn.start_with?( "./" )
|
9
|
+
cn[0..1] = "dotslash"
|
10
|
+
end
|
11
|
+
cn.split(" ").join("_")
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.strip(bytes)
|
15
|
+
bytes.strip
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.readfile(name)
|
19
|
+
dir = ::IOCheck::Env["dir"]
|
20
|
+
if self.include?(name, "#{dir}/lock")
|
21
|
+
x = "#{dir}/lock/#{name}"
|
22
|
+
elsif self.include?(name, "#{dir}/unlock")
|
23
|
+
x = "#{dir}/unlock/#{name}"
|
24
|
+
else
|
25
|
+
raise ArgumentError, "readfile not found."
|
26
|
+
end
|
27
|
+
x
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.writefile(name)
|
31
|
+
dir = ::IOCheck::Env["dir"]
|
32
|
+
"#{dir}/unlock/#{name}"
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.include?(name, dir)
|
36
|
+
Dir.open(dir).each do |f|
|
37
|
+
return true if f == name
|
38
|
+
end
|
39
|
+
return false
|
40
|
+
end
|
41
|
+
|
42
|
+
class Test
|
43
|
+
def initialize(cmd)
|
44
|
+
@command = Command.new(cmd)
|
45
|
+
@policies = [Policy.by_bytes]
|
46
|
+
@repeat = 1
|
47
|
+
@desc = ""
|
48
|
+
end
|
49
|
+
|
50
|
+
def repeat(n)
|
51
|
+
@repeat = n
|
52
|
+
self
|
53
|
+
end
|
54
|
+
|
55
|
+
def describe(desc)
|
56
|
+
@desc = desc
|
57
|
+
self
|
58
|
+
end
|
59
|
+
|
60
|
+
def by(policy)
|
61
|
+
@policies << policy
|
62
|
+
self
|
63
|
+
end
|
64
|
+
|
65
|
+
def name
|
66
|
+
::IOCheck.name( @command.cmd )
|
67
|
+
end
|
68
|
+
|
69
|
+
def locked?
|
70
|
+
true if ::IOCheck.include?(name, "#{::IOCheck::Env["dir"]}/lock")
|
71
|
+
end
|
72
|
+
|
73
|
+
def update!
|
74
|
+
return if locked?
|
75
|
+
|
76
|
+
do_run!
|
77
|
+
f = File.open( ::IOCheck.writefile(name), "w")
|
78
|
+
f.write( @command.actual.bytes )
|
79
|
+
f.close
|
80
|
+
end
|
81
|
+
|
82
|
+
def do_run!
|
83
|
+
@command.run!(@repeat)
|
84
|
+
end
|
85
|
+
|
86
|
+
def run!
|
87
|
+
do_run!
|
88
|
+
@policies.each do |p|
|
89
|
+
p.run!( expected, @command.actual )
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def ready
|
94
|
+
RakeTask.new(self).create_tasks
|
95
|
+
end
|
96
|
+
|
97
|
+
def show
|
98
|
+
n_success = 0
|
99
|
+
n_failure = 0
|
100
|
+
log = []
|
101
|
+
@policies.each do |p|
|
102
|
+
if p.result.class == ::IOCheck::Policy::Success
|
103
|
+
n_success += 1
|
104
|
+
elsif p.result.class == ::IOCheck::Policy::Failure
|
105
|
+
n_failure += 1
|
106
|
+
log << p.result.log
|
107
|
+
else
|
108
|
+
raise Error
|
109
|
+
end
|
110
|
+
end
|
111
|
+
puts "Test Name : #{name}"
|
112
|
+
puts "Description : #{@desc}"
|
113
|
+
puts "Minor Success : #{n_success}"
|
114
|
+
puts "Minor Failure : #{n_failure}"
|
115
|
+
puts "# Log --------:"
|
116
|
+
puts log.join("\n")
|
117
|
+
end
|
118
|
+
|
119
|
+
private
|
120
|
+
|
121
|
+
def expected
|
122
|
+
Expected.new(self)
|
123
|
+
end
|
124
|
+
|
125
|
+
class Command
|
126
|
+
def initialize(cmd)
|
127
|
+
@cmd = cmd
|
128
|
+
@actual = Actual.new
|
129
|
+
end
|
130
|
+
attr_reader :cmd, :actual
|
131
|
+
|
132
|
+
def run!(repeat)
|
133
|
+
repeat.times do
|
134
|
+
@actual << Actual::Result.new( @cmd )
|
135
|
+
end
|
136
|
+
@actual.repeat = repeat
|
137
|
+
end
|
138
|
+
|
139
|
+
class Actual
|
140
|
+
def initialize
|
141
|
+
@results = []
|
142
|
+
end
|
143
|
+
attr_accessor :repeat
|
144
|
+
|
145
|
+
def <<(result)
|
146
|
+
@results << result
|
147
|
+
end
|
148
|
+
def bytes
|
149
|
+
# TODO: assert all the results are equal in bytes?
|
150
|
+
::IOCheck.strip( @results[0].bytes )
|
151
|
+
end
|
152
|
+
class Result
|
153
|
+
def initialize(cmd)
|
154
|
+
@cmd = cmd
|
155
|
+
run!
|
156
|
+
end
|
157
|
+
attr_reader :bytes
|
158
|
+
private
|
159
|
+
def run!
|
160
|
+
# TODO: more things like measuring the time.
|
161
|
+
x = `#{@cmd}`
|
162
|
+
@bytes = ::IOCheck.strip( x )
|
163
|
+
end
|
164
|
+
end # end of class Result
|
165
|
+
end # end of class Actual
|
166
|
+
end # end of class Command
|
167
|
+
|
168
|
+
class Expected
|
169
|
+
def initialize(test)
|
170
|
+
@test = test
|
171
|
+
end
|
172
|
+
def repeat
|
173
|
+
@test.repeat
|
174
|
+
end
|
175
|
+
def bytes
|
176
|
+
content = File.read( ::IOCheck.readfile(@test.name) )
|
177
|
+
::IOCheck.strip( content )
|
178
|
+
end
|
179
|
+
end # end of class Expected
|
180
|
+
|
181
|
+
class RakeTask
|
182
|
+
def initialize(test)
|
183
|
+
@test = test
|
184
|
+
end
|
185
|
+
def create_tasks
|
186
|
+
create_update_task
|
187
|
+
create_run_task
|
188
|
+
end
|
189
|
+
def create_update_task
|
190
|
+
namespace "iocheck" do
|
191
|
+
namespace "update" do
|
192
|
+
task @test.name do
|
193
|
+
@test.update!
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
def create_run_task
|
199
|
+
namespace "iocheck" do
|
200
|
+
task @test.name do
|
201
|
+
@test.run!
|
202
|
+
@test.show
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end # end of class RakeTask
|
207
|
+
end # end of class Test
|
208
|
+
end # end of module IOCheck
|
209
|
+
|
210
|
+
if __FILE__ == $0
|
211
|
+
p ::IOCheck.name("tree")
|
212
|
+
p ::IOCheck.writefile("tree")
|
213
|
+
p ::IOCheck.readfile("tree")
|
214
|
+
p ::IOCheck.strip("\n aa bbb\n ccff \n ")
|
215
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module IOCheck
|
2
|
+
|
3
|
+
Env = {}
|
4
|
+
::IOCheck::Env["dir"] = "./iocheck"
|
5
|
+
|
6
|
+
class Tester
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@tests = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def <<(test)
|
13
|
+
@tests << test
|
14
|
+
end
|
15
|
+
|
16
|
+
def run!
|
17
|
+
@tests.each { |t| t.run! }
|
18
|
+
end
|
19
|
+
|
20
|
+
def update!
|
21
|
+
@tests.each { |t| t.update! }
|
22
|
+
end
|
23
|
+
|
24
|
+
def ready
|
25
|
+
@tests.each do |t|
|
26
|
+
t.ready
|
27
|
+
end
|
28
|
+
namespace "iocheck" do
|
29
|
+
task "update" => @tests.map { |t| "iocheck:update:#{t.name}" }
|
30
|
+
end
|
31
|
+
task "iocheck" do
|
32
|
+
run!
|
33
|
+
show
|
34
|
+
end
|
35
|
+
create_lock_tasks
|
36
|
+
end
|
37
|
+
|
38
|
+
def create_lock_tasks
|
39
|
+
locked = @tests.select { |t| t.locked? }
|
40
|
+
unlocked = @tests.select { |t| ! t.locked? }
|
41
|
+
dir = ::IOCheck::Env["dir"]
|
42
|
+
|
43
|
+
namespace "iocheck" do
|
44
|
+
task "lock" => unlocked.map { |t| "iocheck:lock:#{t.name}" }
|
45
|
+
unlocked.each do |t|
|
46
|
+
namespace "lock" do
|
47
|
+
task t.name do
|
48
|
+
dest = dir + "/" + "lock"
|
49
|
+
mv ::IOCheck.readfile(t.name), dest
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
task "unlock" => locked.map { |t| "iocheck:unlock:#{t.name}" }
|
54
|
+
locked.each do |t|
|
55
|
+
namespace "unlock" do
|
56
|
+
task t.name do
|
57
|
+
dest = dir + "/" + "unlock"
|
58
|
+
mv ::IOCheck.readfile(t.name), dest
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
def show
|
67
|
+
# TODO: This is a temporary implementation.
|
68
|
+
@tests.each do |t|
|
69
|
+
t.show
|
70
|
+
end
|
71
|
+
# TODO: Here, summary result should be written.
|
72
|
+
end
|
73
|
+
end # end of class Tester
|
74
|
+
end # end of module IOCheck
|
metadata
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: iocheck
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Akira Hayakawa
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-09-06 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: &21844540 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 2.3.0
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *21844540
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: bundler
|
27
|
+
requirement: &21844060 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 1.0.0
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *21844060
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: jeweler
|
38
|
+
requirement: &21843580 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 1.6.4
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *21843580
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rcov
|
49
|
+
requirement: &21755800 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *21755800
|
58
|
+
description: Make refactoring for legacy softwares safe.
|
59
|
+
email: ruby.wktk@gmail.com
|
60
|
+
executables: []
|
61
|
+
extensions: []
|
62
|
+
extra_rdoc_files:
|
63
|
+
- LICENSE.txt
|
64
|
+
- README.md
|
65
|
+
files:
|
66
|
+
- lib/iocheck.rb
|
67
|
+
- lib/iocheck/policy.rb
|
68
|
+
- lib/iocheck/test.rb
|
69
|
+
- lib/iocheck/tester.rb
|
70
|
+
- LICENSE.txt
|
71
|
+
- README.md
|
72
|
+
homepage: http://github.com/akiradeveloper/iocheck
|
73
|
+
licenses:
|
74
|
+
- MIT
|
75
|
+
post_install_message:
|
76
|
+
rdoc_options: []
|
77
|
+
require_paths:
|
78
|
+
- lib
|
79
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
80
|
+
none: false
|
81
|
+
requirements:
|
82
|
+
- - ! '>='
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '0'
|
85
|
+
segments:
|
86
|
+
- 0
|
87
|
+
hash: -2015204035547287083
|
88
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
requirements: []
|
95
|
+
rubyforge_project:
|
96
|
+
rubygems_version: 1.8.6
|
97
|
+
signing_key:
|
98
|
+
specification_version: 3
|
99
|
+
summary: IO-based testing framework.
|
100
|
+
test_files: []
|