osp 0.1.0.pre.dev.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/osp/host.rb ADDED
@@ -0,0 +1,146 @@
1
+
2
+ module TheFox
3
+ module OSP
4
+
5
+ class Host
6
+
7
+ # attr_accessor :osp
8
+ # attr_accessor :version
9
+ attr_accessor :created_at
10
+ attr_accessor :updated_at
11
+ # attr_accessor :name
12
+ # attr_accessor :generation
13
+ # attr_accessor :length
14
+ # attr_accessor :symbols
15
+ # attr_accessor :hashes
16
+ # attr_accessor :password
17
+
18
+ def initialize(osp = nil)
19
+ @osp = osp
20
+
21
+ @version = 1
22
+ @created_at = DateTime.now
23
+ @updated_at = DateTime.now
24
+
25
+ @name = nil
26
+ @generation = 1
27
+ @length = 16
28
+ @symbols = 1
29
+ @hashes = !@osp.nil? ? @osp.hashes : nil
30
+ @password = nil
31
+ end
32
+
33
+ def osp=(v)
34
+ raise ArgumentError, 'Wrong type.' if !v.is_a?(TheFox::OSP::OSP)
35
+
36
+ @osp = v
37
+ end
38
+
39
+ def osp
40
+ @osp
41
+ end
42
+
43
+ def version=(v)
44
+ @version = v.to_i
45
+ end
46
+
47
+ def version
48
+ @version.to_i
49
+ end
50
+
51
+ # def created_at=(v)
52
+ # @created_at = v
53
+ # end
54
+
55
+ # def created_at
56
+ # @created_at
57
+ # end
58
+
59
+ def name=(v)
60
+ v = nil if v == ''
61
+ @name = v
62
+ end
63
+
64
+ def name
65
+ @name
66
+ end
67
+
68
+ def generation=(v)
69
+ @generation = v.to_i
70
+ end
71
+
72
+ def generation
73
+ @generation.to_i
74
+ end
75
+
76
+ def length=(v)
77
+ @length = v.to_i
78
+ end
79
+
80
+ def length
81
+ @length.to_i
82
+ end
83
+
84
+ def symbols=(v)
85
+ @symbols = v.to_i
86
+ end
87
+
88
+ def symbols
89
+ @symbols.to_i
90
+ end
91
+
92
+ def hashes=(v)
93
+ @hashes = v
94
+ end
95
+
96
+ def hashes
97
+ @hashes
98
+ end
99
+
100
+ def generate_password(regenerate = false)
101
+ if @password.nil? && !@osp.nil? || regenerate
102
+ # puts "host name: '#{@name}'"
103
+ @password = @osp.password(@name, @length, @generation, @symbols)
104
+ end
105
+ end
106
+
107
+ def password=(v)
108
+ @password = v
109
+ end
110
+
111
+ def password
112
+ generate_password
113
+ @password
114
+ end
115
+
116
+ def has_generated_password?
117
+ !@password.nil?
118
+ end
119
+
120
+ def to_h
121
+ {
122
+ 'version' => @version,
123
+ 'created_at' => @created_at.to_s,
124
+
125
+ 'name' => @name,
126
+ 'generation' => @generation,
127
+ 'length' => @length,
128
+ 'symbols' => @symbols,
129
+ 'hashes' => @hashes,
130
+
131
+ #'password' => @password,
132
+ }
133
+ end
134
+
135
+ def self.from_h(host)
136
+ h = self.new
137
+ host.each do |k, v|
138
+ h.method("#{k}=").call(v)
139
+ end
140
+ h
141
+ end
142
+
143
+ end
144
+ end
145
+ end
146
+
data/lib/osp/osp.rb ADDED
@@ -0,0 +1,183 @@
1
+
2
+ require 'base64'
3
+ require 'openssl'
4
+ require 'msgpack'
5
+ require 'pp'
6
+
7
+ require 'thefox-ext'
8
+
9
+ module TheFox
10
+ module OSP
11
+
12
+ ID = 'TheFox-OSP'
13
+ HASHES_EXP = 24
14
+ HASHES_N = 2 ** HASHES_EXP
15
+ PASSWORD_MIN_SIZE = 8
16
+ PASSWORD_MAX_SIZE = 32
17
+ SYMBOLS = 1
18
+
19
+ class OSP
20
+
21
+ attr_accessor :hashes
22
+
23
+ def initialize(email, password, hashes = HASHES_N)
24
+ @email = email
25
+ @password = password
26
+ @hashes = hashes
27
+ @dk = nil
28
+ @password_callback_method = nil
29
+ end
30
+
31
+ def key_derivation
32
+ # puts "password: #{@password}"
33
+ # puts "email: #{@email}"
34
+ # puts "hashes: #{@hashes}"
35
+
36
+ @dk = OpenSSL::PKCS5.pbkdf2_hmac(@password, @email, @hashes, 64, OpenSSL::Digest::SHA512.new)
37
+ # pp @dk.length
38
+ # pp @dk.length * 8
39
+ # pp @dk.to_hex
40
+ end
41
+
42
+ def password(host_name, length = 16, generation = 1, symbols = 1)
43
+ raise ArgumentError, "'host_name' can't be '' or nil" if host_name.nil? || host_name == '' || !host_name
44
+
45
+ key_derivation if @dk.nil?
46
+
47
+ pw = nil
48
+ step = 0
49
+ while pw.nil?
50
+ raw = [ID, @email, host_name, generation, step]
51
+ # pp raw
52
+ data = raw.to_msgpack
53
+ hmac_p = OpenSSL::HMAC.digest(OpenSSL::Digest::SHA512.new, @dk, data)
54
+ hmac_b64 = Base64.strict_encode64(hmac_p)
55
+ pw = hmac_b64 if is_ok_pw(hmac_b64)
56
+
57
+ # pp hmac_b64[0..16]
58
+ #sleep 0.1
59
+
60
+ @password_callback_method.call(step, hmac_b64) if !@password_callback_method.nil?
61
+ step += 1
62
+ end
63
+
64
+ if symbols > 0
65
+ sub_method = find_method_to_sub(pw)
66
+ #puts "sub_method = #{sub_method}"
67
+
68
+ _b64map = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='
69
+
70
+ indices = []
71
+ (0..PASSWORD_MIN_SIZE).each do |n|
72
+ c = pw[n]
73
+ #puts "#{n} = #{c}"
74
+ if c.method(sub_method).call
75
+ indices << n
76
+ if indices.count >= symbols
77
+ break
78
+ end
79
+ end
80
+ end
81
+
82
+ #pp indices
83
+
84
+ _map = "`~!@#$%^&*()-_+={}[]|;:,<>.?/"
85
+ _map_len = _map.length
86
+
87
+ last = 0
88
+ arr = []
89
+ indices.each do |index|
90
+ arr << pw[last...index]
91
+ c = pw[index]
92
+ i = _b64map.index(c)
93
+ x = _map[i % _map_len]
94
+ arr << x
95
+ last = index + 1
96
+
97
+ #puts "#{index} = '#{c}' '#{i}' '#{x}' #{last}"
98
+ end
99
+ arr << pw[last..-1]
100
+ #pp arr
101
+ pw = arr.join
102
+ end
103
+
104
+ #puts "length: #{length}"
105
+ pw[0...length]
106
+ end
107
+
108
+ def password_callback_method=(m)
109
+ @password_callback_method = m
110
+ end
111
+
112
+ private
113
+
114
+ def is_ok_pw(pw)
115
+ caps = 0
116
+ lowers = 0
117
+ digits = 0
118
+
119
+ (0...PASSWORD_MIN_SIZE).each do |n|
120
+ c = pw[n]
121
+
122
+ # puts "#{n} = #{c}"
123
+
124
+ if c.is_digit?
125
+ digits += 1
126
+ elsif c.is_upper?
127
+ caps += 1
128
+ elsif c.is_lower?
129
+ lowers += 1
130
+ else
131
+ return false
132
+ end
133
+ end
134
+
135
+ bad = lambda { |x| x == 0 || x > 5 }
136
+
137
+ if bad.call(caps) || bad.call(lowers) || bad.call(digits)
138
+ #puts 'return false'
139
+ return false
140
+ end
141
+
142
+ (PASSWORD_MIN_SIZE...PASSWORD_MAX_SIZE).each do |n|
143
+ if !pw[n].is_valid?
144
+ return false
145
+ end
146
+ end
147
+
148
+ true
149
+ end
150
+
151
+ def find_method_to_sub(pw)
152
+ caps = 0
153
+ lowers = 0
154
+ digits = 0
155
+
156
+ (0...PASSWORD_MIN_SIZE).each do |n|
157
+ c = pw[n]
158
+ if c.is_digit?
159
+ digits += 1
160
+ elsif c.is_upper?
161
+ caps += 1
162
+ elsif c.is_lower?
163
+ lowers += 1
164
+ end
165
+
166
+ #puts "#{n} = #{c} #{digits} #{caps} #{lowers}"
167
+ end
168
+
169
+ rv = ''
170
+ if lowers >= caps && lowers >= digits then
171
+ rv = 'is_lower?'
172
+ elsif digits > lowers && digits >= caps
173
+ rv = 'is_digit?'
174
+ else
175
+ rv = 'is_upper?'
176
+ end
177
+ #rv = 'is_upper?'
178
+ rv
179
+ end
180
+
181
+ end
182
+ end
183
+ end
@@ -0,0 +1,9 @@
1
+
2
+ module TheFox
3
+ module OSP
4
+ RELEASE_ID = 0
5
+ VERSION = '0.1.0-dev.1'
6
+ DATE = '2015-12-17'
7
+ HOMEPAGE = 'https://github.com/TheFox/osp'
8
+ end
9
+ end
data/lib/osp.rb ADDED
@@ -0,0 +1,6 @@
1
+
2
+ require 'ext/string'
3
+
4
+ require 'osp/version'
5
+ require 'osp/osp'
6
+ require 'osp/host'
data/osp.gemspec ADDED
@@ -0,0 +1,36 @@
1
+ # coding: UTF-8
2
+
3
+ lib = File.expand_path('../lib', __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+
6
+ require 'osp/version'
7
+
8
+ Gem::Specification.new do |spec|
9
+ spec.name = 'osp'
10
+ spec.version = TheFox::OSP::VERSION
11
+ spec.date = TheFox::OSP::DATE
12
+ spec.author = 'Christian Mayer'
13
+ spec.email = 'christian@fox21.at'
14
+
15
+ spec.summary = %q{One Shall Pass}
16
+ spec.description = %q{One Shall Pass}
17
+ spec.homepage = TheFox::OSP::HOMEPAGE
18
+ spec.license = 'GPL-3.0'
19
+
20
+ spec.files = `git ls-files -z`.split("\x0").reject{ |f| f.match(%r{^(test|spec|features)/}) }
21
+ spec.bindir = 'bin'
22
+ spec.executables = ['osp', 'catmp']
23
+ spec.require_paths = ['lib']
24
+ spec.required_ruby_version = '>=2.1.0'
25
+
26
+ #spec.requirements << ''
27
+
28
+ spec.add_development_dependency 'bundler', '~>1.10'
29
+ spec.add_development_dependency 'minitest', '~>5.8'
30
+
31
+ spec.add_dependency 'highline', '~>1.7'
32
+ spec.add_dependency 'rainbow', '~>2.0'
33
+ spec.add_dependency 'msgpack', '~>0.7'
34
+
35
+ spec.add_dependency 'thefox-ext', '~>1.2'
36
+ end
@@ -0,0 +1,10 @@
1
+ {
2
+ "folders":[
3
+ {
4
+ "path": ".",
5
+ "name": "OneShallPass",
6
+ "folder_exclude_patterns": [ ],
7
+ "file_exclude_patterns": [ ]
8
+ }
9
+ ]
10
+ }
data/tests/tc_host.rb ADDED
@@ -0,0 +1,120 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'minitest/autorun'
5
+ require 'osp'
6
+
7
+ class TestHost < MiniTest::Test
8
+ def test_base
9
+ osp = TheFox::OSP::Host.new
10
+
11
+ assert_equal('TheFox::OSP::Host', osp.class.to_s)
12
+ end
13
+
14
+ def test_initialize
15
+ host = TheFox::OSP::Host.new
16
+
17
+ assert_equal(nil, host.osp)
18
+ assert_equal(1, host.version)
19
+ # assert_equal(, host.created_at)
20
+ # assert_equal(, host.updated_at)
21
+ assert_equal(nil, host.name)
22
+ assert_equal(1, host.generation)
23
+ assert_equal(16, host.length)
24
+ assert_equal(1, host.symbols)
25
+ assert_equal(nil, host.hashes)
26
+ assert_equal(nil, host.password)
27
+ end
28
+
29
+ def test_version
30
+ host = TheFox::OSP::Host.new
31
+
32
+ host.version = 2
33
+ assert_equal(2, host.version)
34
+
35
+ host.version = '3'
36
+ assert_equal(3, host.version)
37
+ end
38
+
39
+ def test_created_at
40
+ host = TheFox::OSP::Host.new
41
+
42
+ now = DateTime.now
43
+ host.created_at = now
44
+ assert_equal(now, host.created_at)
45
+ end
46
+
47
+ def test_updated_at
48
+ host = TheFox::OSP::Host.new
49
+
50
+ now = DateTime.now
51
+ host.updated_at = now
52
+ assert_equal(now, host.updated_at)
53
+ end
54
+
55
+ def test_name
56
+ host = TheFox::OSP::Host.new
57
+
58
+ host.name = ''
59
+ assert_equal(nil, host.name)
60
+
61
+ host.name = 'host1'
62
+ assert_equal('host1', host.name)
63
+ end
64
+
65
+ def test_generation
66
+ host = TheFox::OSP::Host.new
67
+
68
+ host.generation = 2
69
+ assert_equal(2, host.generation)
70
+
71
+ host.generation = '3'
72
+ assert_equal(3, host.generation)
73
+ end
74
+
75
+ def test_length
76
+ host = TheFox::OSP::Host.new
77
+
78
+ host.length = 2
79
+ assert_equal(2, host.length)
80
+
81
+ host.length = '3'
82
+ assert_equal(3, host.length)
83
+ end
84
+
85
+ def test_symbols
86
+ host = TheFox::OSP::Host.new
87
+
88
+ host.symbols = 2
89
+ assert_equal(2, host.symbols)
90
+
91
+ host.symbols = '3'
92
+ assert_equal(3, host.symbols)
93
+ end
94
+
95
+ def test_hashes
96
+ host = TheFox::OSP::Host.new
97
+
98
+ host.hashes = nil
99
+ assert_equal(nil, host.hashes)
100
+
101
+ host.hashes = 2
102
+ assert_equal(2, host.hashes)
103
+
104
+ host.hashes = '3'
105
+ assert_equal('3', host.hashes)
106
+ end
107
+
108
+ def test_password
109
+ host = TheFox::OSP::Host.new
110
+
111
+ host.password = nil
112
+ assert_equal(nil, host.password)
113
+
114
+ host.password = 2
115
+ assert_equal(2, host.password)
116
+
117
+ host.password = '3'
118
+ assert_equal('3', host.password)
119
+ end
120
+ end
data/tests/tc_osp.rb ADDED
@@ -0,0 +1,137 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'minitest/autorun'
5
+ require 'osp'
6
+
7
+ class TestOsp < MiniTest::Test
8
+ def test_that_it_has_a_version_number
9
+ refute_nil ::TheFox::OSP::VERSION
10
+ end
11
+
12
+ def test_base
13
+ osp = TheFox::OSP::OSP.new('example@example.com', 'test1', 2 ** 10)
14
+
15
+ assert_equal('TheFox::OSP::OSP', osp.class.to_s)
16
+ end
17
+
18
+ def test_hashes
19
+ osp = TheFox::OSP::OSP.new('example@example.com', 'test1', 2 ** 10)
20
+ assert_equal(2 ** 10, osp.hashes)
21
+
22
+ osp = TheFox::OSP::OSP.new('example@example.com', 'test1', 2 ** 20)
23
+ assert_equal(2 ** 20, osp.hashes)
24
+ end
25
+
26
+ # def test_password1
27
+ # #ID = 'OneShallPass v2.0'
28
+ # osp = TheFox::OSP::OSP.new('example@example.com', 'test1', 2 ** 10)
29
+ # osp.key_derivation
30
+
31
+ # assert_equal('TA24hNn0', osp.password('host1', 8, 1, 0))
32
+ # assert_equal('TA>4hNn0', osp.password('host1', 8, 1, 1))
33
+ # assert_equal('TA>?hNn0', osp.password('host1', 8, 1, 2))
34
+ # assert_equal('TA>?hNn,', osp.password('host1', 8, 1, 3))
35
+
36
+ # assert_equal('DHKzN7uY', osp.password('host2', 8, 1, 0))
37
+ # assert_equal('@HKzN7uY', osp.password('host2', 8, 1, 1))
38
+ # assert_equal('@^KzN7uY', osp.password('host2', 8, 1, 2))
39
+ # assert_equal('@^(zN7uY', osp.password('host2', 8, 1, 3))
40
+
41
+ # assert_equal('Qf4lvgE7', osp.password('host3', 8, 1, 0))
42
+ # assert_equal('Q!4lvgE7', osp.password('host3', 8, 1, 1))
43
+ # assert_equal('Q!4&vgE7', osp.password('host3', 8, 1, 2))
44
+ # assert_equal('Q!4&[gE7', osp.password('host3', 8, 1, 3))
45
+
46
+ # assert_equal('DHKzN7uYUBc3l0wi', osp.password('host2', 16, 1, 0))
47
+ # assert_equal('@HKzN7uYUBc3l0wi', osp.password('host2', 16, 1, 1))
48
+ # assert_equal('@^KzN7uYUBc3l0wi', osp.password('host2', 16, 1, 2))
49
+ # assert_equal('@^(zN7uYUBc3l0wi', osp.password('host2', 16, 1, 3))
50
+
51
+ # assert_equal('wU0t38KE4tDQb3c0', osp.password('host2', 16, 2, 0))
52
+ # assert_equal('wU,t38KE4tDQb3c0', osp.password('host2', 16, 2, 1))
53
+ # assert_equal('wU,t.8KE4tDQb3c0', osp.password('host2', 16, 2, 2))
54
+ # assert_equal('wU,t.!KE4tDQb3c0', osp.password('host2', 16, 2, 3))
55
+
56
+ # assert_equal('OezcZk881M3Jxw9Z', osp.password('host2', 16, 3, 0))
57
+ # assert_equal('O~zcZk881M3Jxw9Z', osp.password('host2', 16, 3, 1))
58
+ # assert_equal('O~:cZk881M3Jxw9Z', osp.password('host2', 16, 3, 2))
59
+ # assert_equal('O~:/Zk881M3Jxw9Z', osp.password('host2', 16, 3, 3))
60
+ # end
61
+
62
+ # def test_password2
63
+ # #PASSWORD_MIN_SIZE = 8
64
+ # #PASSWORD_MAX_SIZE = 16
65
+
66
+ # osp = TheFox::OSP::OSP.new('example@example.com', 'test1', 2 ** 10)
67
+ # osp.key_derivation
68
+
69
+ # assert_equal('cXyE2Dq1', osp.password('host1', 8, 1, 0))
70
+ # assert_equal('/XyE2Dq1', osp.password('host1', 8, 1, 1))
71
+ # assert_equal('/X;E2Dq1', osp.password('host1', 8, 1, 2))
72
+ # assert_equal('/X;E2D_1', osp.password('host1', 8, 1, 3))
73
+
74
+ # assert_equal('kT455AIH', osp.password('host2', 8, 1, 0))
75
+ # assert_equal('k]455AIH', osp.password('host2', 8, 1, 1))
76
+ # assert_equal('k]455`IH', osp.password('host2', 8, 1, 2))
77
+ # assert_equal('k]455`&H', osp.password('host2', 8, 1, 3))
78
+
79
+ # assert_equal('RN1vFCCx', osp.password('host3', 8, 1, 0))
80
+ # assert_equal('}N1vFCCx', osp.password('host3', 8, 1, 1))
81
+ # assert_equal('}_1vFCCx', osp.password('host3', 8, 1, 2))
82
+ # assert_equal('}_1v$CCx', osp.password('host3', 8, 1, 3))
83
+
84
+ # assert_equal('kT455AIHkWRmXRN9', osp.password('host2', 16, 1, 0))
85
+ # assert_equal('k]455AIHkWRmXRN9', osp.password('host2', 16, 1, 1))
86
+ # assert_equal('k]455`IHkWRmXRN9', osp.password('host2', 16, 1, 2))
87
+ # assert_equal('k]455`&HkWRmXRN9', osp.password('host2', 16, 1, 3))
88
+
89
+ # assert_equal('SDBNsS7tMF4HjpPz', osp.password('host2', 16, 2, 0))
90
+ # assert_equal('[DBNsS7tMF4HjpPz', osp.password('host2', 16, 2, 1))
91
+ # assert_equal('[@BNsS7tMF4HjpPz', osp.password('host2', 16, 2, 2))
92
+ # assert_equal('[@~NsS7tMF4HjpPz', osp.password('host2', 16, 2, 3))
93
+
94
+ # assert_equal('GZ8SAox0xBvFM9QL', osp.password('host2', 16, 3, 0))
95
+ # assert_equal('%Z8SAox0xBvFM9QL', osp.password('host2', 16, 3, 1))
96
+ # assert_equal('%>8SAox0xBvFM9QL', osp.password('host2', 16, 3, 2))
97
+ # assert_equal('%>8[Aox0xBvFM9QL', osp.password('host2', 16, 3, 3))
98
+ # end
99
+
100
+ def test_password3
101
+ #PASSWORD_MIN_SIZE = 8
102
+ #PASSWORD_MAX_SIZE = 32
103
+
104
+ osp = TheFox::OSP::OSP.new('example@example.com', 'test1', 2 ** 10)
105
+ osp.key_derivation
106
+
107
+ assert_equal('cXyE2Dq1', osp.password('host1', 8, 1, 0))
108
+ assert_equal('/XyE2Dq1', osp.password('host1', 8, 1, 1))
109
+ assert_equal('/X;E2Dq1', osp.password('host1', 8, 1, 2))
110
+ assert_equal('/X;E2D_1', osp.password('host1', 8, 1, 3))
111
+
112
+ assert_equal('ar7JQegF', osp.password('host2', 8, 1, 0))
113
+ assert_equal('.r7JQegF', osp.password('host2', 8, 1, 1))
114
+ assert_equal('.+7JQegF', osp.password('host2', 8, 1, 2))
115
+ assert_equal('.+7JQ~gF', osp.password('host2', 8, 1, 3))
116
+
117
+ assert_equal('RN1vFCCx', osp.password('host3', 8, 1, 0))
118
+ assert_equal('}N1vFCCx', osp.password('host3', 8, 1, 1))
119
+ assert_equal('}_1vFCCx', osp.password('host3', 8, 1, 2))
120
+ assert_equal('}_1v$CCx', osp.password('host3', 8, 1, 3))
121
+
122
+ assert_equal('ar7JQegFyBk737gQ', osp.password('host2', 16, 1, 0))
123
+ assert_equal('.r7JQegFyBk737gQ', osp.password('host2', 16, 1, 1))
124
+ assert_equal('.+7JQegFyBk737gQ', osp.password('host2', 16, 1, 2))
125
+ assert_equal('.+7JQ~gFyBk737gQ', osp.password('host2', 16, 1, 3))
126
+
127
+ assert_equal('E3uJvG0rGFKwgpHD', osp.password('host2', 16, 2, 0))
128
+ assert_equal('E3}JvG0rGFKwgpHD', osp.password('host2', 16, 2, 1))
129
+ assert_equal('E3}J[G0rGFKwgpHD', osp.password('host2', 16, 2, 2))
130
+ assert_equal('E3}J[G0+GFKwgpHD', osp.password('host2', 16, 2, 3))
131
+
132
+ assert_equal('7p51OI3QRAKGUcUc', osp.password('host2', 16, 3, 0))
133
+ assert_equal('~p51OI3QRAKGUcUc', osp.password('host2', 16, 3, 1))
134
+ assert_equal('~p/1OI3QRAKGUcUc', osp.password('host2', 16, 3, 2))
135
+ assert_equal('~p/<OI3QRAKGUcUc', osp.password('host2', 16, 3, 3))
136
+ end
137
+ end
data/tests/ts_all.rb ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative 'tc_osp'
4
+ require_relative 'tc_host'