tf 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +13 -0
- data/README.md +71 -0
- data/bin/tf +7 -0
- data/lib/plugins/tf/comment_test_input.rb +40 -0
- data/lib/plugins/tf/env_match_test.rb +18 -0
- data/lib/plugins/tf/error_summary_output.rb +93 -0
- data/lib/plugins/tf/output_match_test.rb +17 -0
- data/lib/plugins/tf/stats_output.rb +73 -0
- data/lib/plugins/tf/status_test.rb +17 -0
- data/lib/plugins/tf/text_output.rb +52 -0
- data/lib/tf.rb +94 -0
- data/lib/tf/active_patches.rb +21 -0
- data/lib/tf/plugins.rb +102 -0
- metadata +74 -0
data/LICENSE
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Copyright (c) 2011 Michal Papis
|
|
2
|
+
|
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
you may not use this file except in compliance with the License.
|
|
5
|
+
You may obtain a copy of the License at
|
|
6
|
+
|
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
|
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
See the License for the specific language governing permissions and
|
|
13
|
+
limitations under the License.
|
data/README.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Testing Framework
|
|
2
|
+
|
|
3
|
+
TF is a pluggable framework for testing shell scripts (at least now).
|
|
4
|
+
TF also is an umbrella which incorporates (eventually) multiple gems, each of which provides additional functionality
|
|
5
|
+
to TF. TF is the skeleton upon which all other tf-* gems build.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
## Usage
|
|
9
|
+
|
|
10
|
+
$ gem install tf
|
|
11
|
+
$ tf <path/to/file>_comment_test.sh
|
|
12
|
+
$ tf --text <path/to/file>_comment_test.sh
|
|
13
|
+
|
|
14
|
+
## Comment tests
|
|
15
|
+
|
|
16
|
+
Filename has to end with _comment_test.sh
|
|
17
|
+
|
|
18
|
+
Example test file:
|
|
19
|
+
|
|
20
|
+
## User comments start with double #
|
|
21
|
+
## command can be writen in one line with multiple tests:
|
|
22
|
+
true # status=0; match=/^$/
|
|
23
|
+
## or tests can be placed in following lines:
|
|
24
|
+
false
|
|
25
|
+
# status=1
|
|
26
|
+
|
|
27
|
+
### Matchers
|
|
28
|
+
|
|
29
|
+
The test can be negated by replacing `=` with `!=`
|
|
30
|
+
|
|
31
|
+
- status=<number> - check if command returned given status (0 is success)
|
|
32
|
+
- match=/<regexp>/ - regexp match command output
|
|
33
|
+
- env[<var_name>]=/<regexp>/ - regexp match the given environment variable name
|
|
34
|
+
|
|
35
|
+
## Example
|
|
36
|
+
|
|
37
|
+
$ bin/tf example_tests/comment/*
|
|
38
|
+
F..
|
|
39
|
+
##### Processed commands 2 of 2, success tests 2 of 3, failure tests 1 of 3.
|
|
40
|
+
$ false
|
|
41
|
+
# failed: status = 0 # was 1
|
|
42
|
+
|
|
43
|
+
$ bin/tf example_tests/comment/* --text
|
|
44
|
+
##### starting test failure.
|
|
45
|
+
$ false
|
|
46
|
+
# failed: status = 0 # was 1
|
|
47
|
+
##### starting test success.
|
|
48
|
+
$ true
|
|
49
|
+
# passed: status = 0
|
|
50
|
+
# passed: status != 1
|
|
51
|
+
##### Processed commands 2 of 2, success tests 2 of 3, failure tests 1 of 3.
|
|
52
|
+
|
|
53
|
+
## Internal architecture
|
|
54
|
+
|
|
55
|
+
Framework will load plugins from any available gem and local `lib/` path, for example:
|
|
56
|
+
|
|
57
|
+
lib/plugins/tf/text_output.rb
|
|
58
|
+
lib/plugins/tf/status_test.rb
|
|
59
|
+
lib/plugins/tf/comment_test_input.rb
|
|
60
|
+
|
|
61
|
+
The search pattern is:
|
|
62
|
+
|
|
63
|
+
lib/plugins/tf/*.rb
|
|
64
|
+
|
|
65
|
+
And plugins are selected with:
|
|
66
|
+
|
|
67
|
+
lib/plugins/tf/*_{input,test,output}.rb
|
|
68
|
+
|
|
69
|
+
## Thanks
|
|
70
|
+
|
|
71
|
+
- Deryl R. Doucette
|
data/bin/tf
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
class TF::CommentTestInput
|
|
2
|
+
def initialize
|
|
3
|
+
end
|
|
4
|
+
|
|
5
|
+
def self.argument_matches? argument
|
|
6
|
+
if argument =~ /_comment_test\.sh$/ && File.exist?(argument)
|
|
7
|
+
[:load, :input]
|
|
8
|
+
else
|
|
9
|
+
nil
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def load file_name
|
|
14
|
+
lines = []
|
|
15
|
+
File.readlines(file_name).each{|line|
|
|
16
|
+
# Fix jruby-1.6.6-d19 bug with empty strings from files
|
|
17
|
+
line = "#{line}"
|
|
18
|
+
# remove human comments
|
|
19
|
+
line.sub!(/##.*$/,'')
|
|
20
|
+
# reject empty lines
|
|
21
|
+
line.strip!
|
|
22
|
+
next if line =~ /^$/
|
|
23
|
+
# extract command and tests
|
|
24
|
+
cmd, tests = line.split("#")
|
|
25
|
+
cmd.strip!
|
|
26
|
+
tests = if tests.blank?
|
|
27
|
+
[]
|
|
28
|
+
else
|
|
29
|
+
tests.split(";").map(&:strip)
|
|
30
|
+
end
|
|
31
|
+
if cmd.blank?
|
|
32
|
+
lines.last[:tests] += tests unless lines.last.nil?
|
|
33
|
+
else
|
|
34
|
+
lines << { :cmd => cmd, :tests => tests }
|
|
35
|
+
end
|
|
36
|
+
}
|
|
37
|
+
name = file_name.gsub(/^.*\//,'').sub(/_comment_test\.sh$/,'')
|
|
38
|
+
{ :name => name, :commands => lines }
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
class TF::EnvMatchTest
|
|
2
|
+
MATCHER = /^env\[(.*)\]([!]?=)[~]?\/(.*)\//
|
|
3
|
+
|
|
4
|
+
def matches? test
|
|
5
|
+
test =~ TF::EnvMatchTest::MATCHER
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def execute test, _stdout, _stderr, _stdboth, _status, env
|
|
9
|
+
test =~ TF::EnvMatchTest::MATCHER
|
|
10
|
+
variable, sign, value = $1.strip, $2, $3
|
|
11
|
+
var_val = env[ variable ]
|
|
12
|
+
if ( sign == "=" ) ^ ( Regexp.new(value) =~ "#{var_val}" )
|
|
13
|
+
[ false, "failed: env #{variable} #{sign} /#{value}/ # was '#{var_val}'" ]
|
|
14
|
+
else
|
|
15
|
+
[ true, "passed: env #{variable} #{sign} /#{value}/" ]
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
class TF::ErrorSummaryOutput
|
|
2
|
+
RED = `tput setaf 1`
|
|
3
|
+
GREEN = `tput setaf 2`
|
|
4
|
+
YELLOW = `tput setaf 3`
|
|
5
|
+
BLUE = `tput setaf 4`
|
|
6
|
+
RESET = `tput setaf 9`
|
|
7
|
+
|
|
8
|
+
def self.argument_matches? argument
|
|
9
|
+
[:load] if argument == "--dotted"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def initialize output=nil
|
|
13
|
+
@counts={}
|
|
14
|
+
@counts[:commands] = 0
|
|
15
|
+
@counts[:tests] = 0
|
|
16
|
+
@counts[:commands_started] = 0
|
|
17
|
+
@counts[:commands_finished] = 0
|
|
18
|
+
@counts[:tests_success] = 0
|
|
19
|
+
@counts[:tests_failure] = 0
|
|
20
|
+
@counter_id = 0
|
|
21
|
+
@summary = {}
|
|
22
|
+
@output = output || $stdout
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def start_processing
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def status
|
|
29
|
+
text = "#{BLUE}##### Processed commands #{@counts[:commands_finished]} of #{@counts[:commands]}"
|
|
30
|
+
if @counts[:tests_success] > 0
|
|
31
|
+
text += ", #{GREEN}success tests #{@counts[:tests_success]} of #{@counts[:tests]}"
|
|
32
|
+
end
|
|
33
|
+
if @counts[:tests_failure] > 0
|
|
34
|
+
text += ", #{RED}failure tests #{@counts[:tests_failure]} of #{@counts[:tests]}"
|
|
35
|
+
end
|
|
36
|
+
skipped = @counts[:tests] - @counts[:tests_success] - @counts[:tests_failure]
|
|
37
|
+
if skipped > 0
|
|
38
|
+
text += ", #{YELLOW}skipped tests #{skipped} of #{@counts[:tests]}"
|
|
39
|
+
end
|
|
40
|
+
text += ".#{RESET}"
|
|
41
|
+
text
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def summary
|
|
45
|
+
@summary.sort{|a,b| ak,_=a ; bk,_=b ; ak <=> bk }.each{|k,v|
|
|
46
|
+
@output.puts "#{YELLOW}$ #{v[:cmd]}#{RESET}"
|
|
47
|
+
v[:failed_tests].each{|t| puts "#{RED}# #{t}#{RESET}" }
|
|
48
|
+
}
|
|
49
|
+
text = ""
|
|
50
|
+
text
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def end_processing
|
|
54
|
+
@output.printf "\n"
|
|
55
|
+
@output.puts status
|
|
56
|
+
@output.puts summary
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def start_test test, env
|
|
60
|
+
@counts[:commands] += test[:commands].size
|
|
61
|
+
tests_counts = test[:commands].map{|line| line[:tests].nil? ? 0 : line[:tests].size }
|
|
62
|
+
@counts[:tests] += tests_counts.empty? ? 0 : tests_counts.inject(&:+)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def end_test test
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def start_command line
|
|
69
|
+
@counts[:commands_started] += 1
|
|
70
|
+
@current_line = line.merge(:counter_id => @counts[:commands_started])
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def end_command line, status, env
|
|
74
|
+
@counts[:commands_finished] += 1
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def command_out out
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def command_err err
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def test_processed test, status, msg
|
|
84
|
+
@output.printf status ? "." : "F"
|
|
85
|
+
if status
|
|
86
|
+
@counts[:tests_success] += 1
|
|
87
|
+
else
|
|
88
|
+
@counts[:tests_failure] += 1
|
|
89
|
+
@summary[@current_line[:counter_id]] ||= @current_line.merge({:failed_tests=>[]})
|
|
90
|
+
@summary[@current_line[:counter_id]][:failed_tests] << msg
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
class TF::OutputMatchTest
|
|
2
|
+
MATCHER = /^match([!]?=)[~]?\/(.*)\//
|
|
3
|
+
|
|
4
|
+
def matches? test
|
|
5
|
+
test =~ TF::OutputMatchTest::MATCHER
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def execute test, _stdout, _stderr, _stdboth, _status, env
|
|
9
|
+
test =~ TF::OutputMatchTest::MATCHER
|
|
10
|
+
sign, value = $1, $2
|
|
11
|
+
if ( sign == "=" ) ^ ( Regexp.new(value) =~ "#{_stdboth}" )
|
|
12
|
+
[ false, "failed: match #{sign} /#{value}/" ]
|
|
13
|
+
else
|
|
14
|
+
[ true, "passed: match #{sign} /#{value}/" ]
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
class TF::StatsOutput
|
|
2
|
+
RED = `tput setaf 1`
|
|
3
|
+
GREEN = `tput setaf 2`
|
|
4
|
+
YELLOW = `tput setaf 3`
|
|
5
|
+
BLUE = `tput setaf 4`
|
|
6
|
+
RESET = `tput setaf 9`
|
|
7
|
+
|
|
8
|
+
def self.argument_matches? argument
|
|
9
|
+
[:load] if argument == "--text"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def initialize
|
|
13
|
+
@counts={}
|
|
14
|
+
@counts[:commands] = 0
|
|
15
|
+
@counts[:tests] = 0
|
|
16
|
+
@counts[:commands_finished] = 0
|
|
17
|
+
@counts[:tests_success] = 0
|
|
18
|
+
@counts[:tests_failure] = 0
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def start_processing
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def status
|
|
25
|
+
text = "#{BLUE}##### Processed commands #{@counts[:commands_finished]} of #{@counts[:commands]}"
|
|
26
|
+
if @counts[:tests_success] > 0
|
|
27
|
+
text += ", #{GREEN}success tests #{@counts[:tests_success]} of #{@counts[:tests]}"
|
|
28
|
+
end
|
|
29
|
+
if @counts[:tests_failure] > 0
|
|
30
|
+
text += ", #{RED}failure tests #{@counts[:tests_failure]} of #{@counts[:tests]}"
|
|
31
|
+
end
|
|
32
|
+
skipped = @counts[:tests] - @counts[:tests_success] - @counts[:tests_failure]
|
|
33
|
+
if skipped > 0
|
|
34
|
+
text += ", #{YELLOW}skipped tests #{skipped} of #{@counts[:tests]}"
|
|
35
|
+
end
|
|
36
|
+
text += ".#{RESET}"
|
|
37
|
+
text
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def end_processing
|
|
41
|
+
puts status
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def start_test test, env
|
|
45
|
+
@counts[:commands] += test[:commands].size
|
|
46
|
+
tests_counts = test[:commands].map{|line| line[:tests].nil? ? 0 : line[:tests].size }
|
|
47
|
+
@counts[:tests] += tests_counts.empty? ? 0 : tests_counts.inject(&:+)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def end_test test
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def start_command line
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def end_command line, status, env
|
|
57
|
+
@counts[:commands_finished] += 1
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def command_out out
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def command_err err
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def test_processed test, status, msg
|
|
67
|
+
if status
|
|
68
|
+
@counts[:tests_success] += 1
|
|
69
|
+
else
|
|
70
|
+
@counts[:tests_failure] += 1
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
class TF::StatusTest
|
|
2
|
+
MATCHER = /^status([!]?=)([[:digit:]]+)$/
|
|
3
|
+
|
|
4
|
+
def matches? test
|
|
5
|
+
test =~ TF::StatusTest::MATCHER
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def execute test, _stdout, _stderr, _stdboth, _status, env
|
|
9
|
+
test =~ TF::StatusTest::MATCHER
|
|
10
|
+
sign, value = $1, $2.to_i
|
|
11
|
+
if ( sign == "=" ) ^ ( _status == value )
|
|
12
|
+
[ false, "failed: status #{sign} #{value} # was #{_status}" ]
|
|
13
|
+
else
|
|
14
|
+
[ true, "passed: status #{sign} #{value}" ]
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
class TF::TextOutput
|
|
2
|
+
RED = `tput setaf 1`
|
|
3
|
+
GREEN = `tput setaf 2`
|
|
4
|
+
YELLOW = `tput setaf 3`
|
|
5
|
+
BLUE = `tput setaf 4`
|
|
6
|
+
RESET = `tput setaf 9`
|
|
7
|
+
|
|
8
|
+
def self.argument_matches? argument
|
|
9
|
+
[:load] if argument == "--text"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def initialize
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def start_processing
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def end_processing
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def start_test test, env
|
|
22
|
+
puts "#{BLUE}##### starting test #{test[:name]}.#{RESET}"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def end_test test
|
|
26
|
+
#puts "#{BLUE}##### finished test #{test[:name]}.#{RESET}"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def start_command line
|
|
30
|
+
puts "#{YELLOW}$ #{line[:cmd]}#{RESET}"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def end_command line, status, env
|
|
34
|
+
#puts ": $?=#{status}"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def command_out out
|
|
38
|
+
puts out
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def command_err err
|
|
42
|
+
puts err
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def test_processed test, status, msg
|
|
46
|
+
if status
|
|
47
|
+
puts "#{GREEN}# #{msg}#{RESET}"
|
|
48
|
+
else
|
|
49
|
+
puts "#{RED}# #{msg}#{RESET}"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
data/lib/tf.rb
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'singleton'
|
|
3
|
+
require 'yaml'
|
|
4
|
+
require 'session'
|
|
5
|
+
|
|
6
|
+
lib_root = File.dirname( __FILE__ )
|
|
7
|
+
|
|
8
|
+
# include lib in path so plugins get found with Gem.find_files
|
|
9
|
+
$:.unshift "#{lib_root}"
|
|
10
|
+
|
|
11
|
+
class TF; end
|
|
12
|
+
# load tf/*.rb
|
|
13
|
+
Dir["#{lib_root}/tf/*.rb"].each{|lib| require lib }
|
|
14
|
+
|
|
15
|
+
class TF
|
|
16
|
+
def initialize
|
|
17
|
+
@ruby = File.join(RbConfig::CONFIG["bindir"],RbConfig::CONFIG["ruby_install_name"])
|
|
18
|
+
@plugins = TF::Plugins.instance
|
|
19
|
+
@failures = 0
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def run_tests args
|
|
23
|
+
@plugins.load(%w( all_test ))
|
|
24
|
+
input_files, not_processed = @plugins.parse_args(args)
|
|
25
|
+
if not_processed.size > 0
|
|
26
|
+
$stderr.puts "No plugin recognized this option '#{not_processed*" "}'."
|
|
27
|
+
exit 1
|
|
28
|
+
end
|
|
29
|
+
@plugins.load(%w( ErrorSummaryOutput )) if @plugins.output_plugins.empty?
|
|
30
|
+
process(input_files)
|
|
31
|
+
@failures == 0
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def process input_files
|
|
35
|
+
@plugins.output_plugins(:start_processing)
|
|
36
|
+
input_files.each do |plugin,file|
|
|
37
|
+
process_test( plugin.load(file) )
|
|
38
|
+
end
|
|
39
|
+
@plugins.output_plugins(:end_processing)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def env shell
|
|
43
|
+
Hash[ shell.execute(
|
|
44
|
+
@ruby + ' -e \'ENV.each{|k,v| printf "#{k}=#{v}\0"}\''
|
|
45
|
+
)[0].split("\0").map{|var| var.split('=', 2) } ]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def process_test test
|
|
49
|
+
name, commands = test[:name], test[:commands]
|
|
50
|
+
shell = Session::Bash.new
|
|
51
|
+
_env = env(shell)
|
|
52
|
+
@plugins.output_plugins(:start_test, test, _env)
|
|
53
|
+
commands.each do |line|
|
|
54
|
+
command, tests = line[:cmd], line[:tests]
|
|
55
|
+
@plugins.output_plugins(:start_command, line)
|
|
56
|
+
_stdout = StringIO.new
|
|
57
|
+
_stderr = StringIO.new
|
|
58
|
+
_stdboth = StringIO.new
|
|
59
|
+
shell.execute "#{command}" do |out, err|
|
|
60
|
+
if out
|
|
61
|
+
@plugins.output_plugins(:command_out, out)
|
|
62
|
+
_stdout << out
|
|
63
|
+
_stdboth << out
|
|
64
|
+
end
|
|
65
|
+
if err
|
|
66
|
+
@plugins.output_plugins(:command_err, err)
|
|
67
|
+
_stderr << err
|
|
68
|
+
_stdboth << err
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
_status = shell.status
|
|
72
|
+
_env = env(shell)
|
|
73
|
+
@plugins.output_plugins(:end_command, line, _status, _env)
|
|
74
|
+
process_command_tests _stdout.string, _stderr.string, _stdboth.string, _status, _env, tests
|
|
75
|
+
end
|
|
76
|
+
@plugins.output_plugins(:end_test, test)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def process_command_tests _stdout, _stderr, _stdboth, _status, env, tests
|
|
80
|
+
tests.each do |test|
|
|
81
|
+
plugin = @plugins.test_plugins.find{|_plugin| _plugin.matches? test }
|
|
82
|
+
if plugin.nil?
|
|
83
|
+
status, msg = false, "Could not find plugin for test '#{test}'."
|
|
84
|
+
else
|
|
85
|
+
status, msg = plugin.execute(test, _stdout, _stderr, _stdboth, _status, env)
|
|
86
|
+
end
|
|
87
|
+
@failures+=1 unless status
|
|
88
|
+
@plugins.output_plugins(:test_processed, test, status, msg)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
class << self
|
|
93
|
+
end
|
|
94
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
unless String.method_defined? :blank?
|
|
2
|
+
String.class_eval do
|
|
3
|
+
def blank?
|
|
4
|
+
self == ""
|
|
5
|
+
end
|
|
6
|
+
end
|
|
7
|
+
end
|
|
8
|
+
unless Array.method_defined? :blank?
|
|
9
|
+
Array.class_eval do
|
|
10
|
+
def blank?
|
|
11
|
+
size == 0
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
unless NilClass.method_defined? :blank?
|
|
16
|
+
NilClass.class_eval do
|
|
17
|
+
def blank?
|
|
18
|
+
true
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
data/lib/tf/plugins.rb
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
class TF::Plugins
|
|
2
|
+
include Singleton
|
|
3
|
+
|
|
4
|
+
def initialize
|
|
5
|
+
detect
|
|
6
|
+
@additional_plugins = []
|
|
7
|
+
@input_plugins = []
|
|
8
|
+
@output_plugins = []
|
|
9
|
+
@test_plugins = []
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def detect
|
|
13
|
+
@plugins = Gem.find_files('plugins/tf/*.rb')
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def add plugin
|
|
17
|
+
@additional_plugins << plugin
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def delete plugin
|
|
21
|
+
@additional_plugins.delete plugin
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def file_to_class item
|
|
25
|
+
File.basename(item,'.rb').capitalize.gsub(/_(.)/){ $1.upcase }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def list pattern=nil
|
|
29
|
+
# collect to lists
|
|
30
|
+
_list = @plugins + @additional_plugins
|
|
31
|
+
# filter by pattern if given
|
|
32
|
+
_list = _list.select{|item| item.match("_#{pattern}.rb$") } unless pattern.nil?
|
|
33
|
+
# get path and class name
|
|
34
|
+
_list.map!{|item| [ item, file_to_class(item), pattern ] }
|
|
35
|
+
# TODO: limit plugin versions (highest || use bundler)
|
|
36
|
+
_list.each{|item, klass, type| require item }
|
|
37
|
+
_list
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def load wanted
|
|
41
|
+
[ :input, :test, :output ].each do |type|
|
|
42
|
+
_list = list(type)
|
|
43
|
+
if ! wanted.include?("all") && ! wanted.include?("all_#{type}")
|
|
44
|
+
_list = _list.select{|item, klass, _type| wanted.include?(klass) }
|
|
45
|
+
end
|
|
46
|
+
_list.each{|item, klass, _type|
|
|
47
|
+
klass = TF.const_get(klass)
|
|
48
|
+
instance_variable_get("@#{type}_plugins".to_sym) << klass.new
|
|
49
|
+
}
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def match_arg_klass arg, klass, type
|
|
54
|
+
klass = TF.const_get(klass)
|
|
55
|
+
return nil unless klass.respond_to? :argument_matches?
|
|
56
|
+
matches = klass.argument_matches? arg
|
|
57
|
+
return nil if matches.nil?
|
|
58
|
+
matches.each do |match|
|
|
59
|
+
case match
|
|
60
|
+
when :load
|
|
61
|
+
instance_variable_get("@#{type}_plugins".to_sym) << klass.new
|
|
62
|
+
when :input
|
|
63
|
+
@input_files << [klass.new, arg]
|
|
64
|
+
else
|
|
65
|
+
return nil
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
return matches
|
|
69
|
+
rescue NameError
|
|
70
|
+
return nil
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def parse_args args
|
|
74
|
+
@input_files, not_processed = [], []
|
|
75
|
+
available_plugins = [ :input, :test, :output ].map{ |type| list(type) }.flatten(1)
|
|
76
|
+
args.each do |arg|
|
|
77
|
+
matched = available_plugins.map do |item, klass, type|
|
|
78
|
+
match_arg_klass arg, klass, type
|
|
79
|
+
end.flatten.reject(&:nil?)
|
|
80
|
+
if matched.empty?
|
|
81
|
+
not_processed << arg
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
[ @input_files, not_processed ]
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def input_plugins
|
|
88
|
+
@input_plugins
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def output_plugins *args
|
|
92
|
+
if args.empty?
|
|
93
|
+
@output_plugins
|
|
94
|
+
else
|
|
95
|
+
@output_plugins.each{|plugin| plugin.send(*args) }
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def test_plugins
|
|
100
|
+
@test_plugins
|
|
101
|
+
end
|
|
102
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: tf
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.3.0
|
|
5
|
+
prerelease:
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- Michal Papis
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2012-05-28 00:00:00.000000000 Z
|
|
13
|
+
dependencies:
|
|
14
|
+
- !ruby/object:Gem::Dependency
|
|
15
|
+
name: session
|
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
|
17
|
+
none: false
|
|
18
|
+
requirements:
|
|
19
|
+
- - ~>
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: '3.0'
|
|
22
|
+
type: :runtime
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
25
|
+
none: false
|
|
26
|
+
requirements:
|
|
27
|
+
- - ~>
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
version: '3.0'
|
|
30
|
+
description: Testing Framework solely based on plugins. For now only tests using Bash.
|
|
31
|
+
email: mpapis+tf@gmail.com
|
|
32
|
+
executables:
|
|
33
|
+
- tf
|
|
34
|
+
extensions: []
|
|
35
|
+
extra_rdoc_files: []
|
|
36
|
+
files:
|
|
37
|
+
- lib/tf.rb
|
|
38
|
+
- lib/tf/active_patches.rb
|
|
39
|
+
- lib/tf/plugins.rb
|
|
40
|
+
- lib/plugins/tf/error_summary_output.rb
|
|
41
|
+
- lib/plugins/tf/text_output.rb
|
|
42
|
+
- lib/plugins/tf/output_match_test.rb
|
|
43
|
+
- lib/plugins/tf/env_match_test.rb
|
|
44
|
+
- lib/plugins/tf/stats_output.rb
|
|
45
|
+
- lib/plugins/tf/comment_test_input.rb
|
|
46
|
+
- lib/plugins/tf/status_test.rb
|
|
47
|
+
- bin/tf
|
|
48
|
+
- LICENSE
|
|
49
|
+
- README.md
|
|
50
|
+
homepage: http://github.com/mpapis/tf
|
|
51
|
+
licenses: []
|
|
52
|
+
post_install_message:
|
|
53
|
+
rdoc_options: []
|
|
54
|
+
require_paths:
|
|
55
|
+
- lib
|
|
56
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
57
|
+
none: false
|
|
58
|
+
requirements:
|
|
59
|
+
- - ! '>='
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '0'
|
|
62
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
63
|
+
none: false
|
|
64
|
+
requirements:
|
|
65
|
+
- - ! '>='
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '0'
|
|
68
|
+
requirements: []
|
|
69
|
+
rubyforge_project:
|
|
70
|
+
rubygems_version: 1.8.24
|
|
71
|
+
signing_key:
|
|
72
|
+
specification_version: 3
|
|
73
|
+
summary: Testing Framework
|
|
74
|
+
test_files: []
|