tac-cli 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +7 -0
- data/Rakefile +30 -0
- data/bin/tac +15 -0
- data/config/commands.yml +15 -0
- data/lib/cli.rb +30 -0
- data/lib/cli/base.rb +13 -0
- data/lib/cli/client_collector.rb +106 -0
- data/lib/cli/cmd_consts.rb +16 -0
- data/lib/cli/commands/help.rb +13 -0
- data/lib/cli/commands/multiple_tests.rb +135 -0
- data/lib/cli/commands/rerun.rb +29 -0
- data/lib/cli/commands/suites_configure.rb +97 -0
- data/lib/cli/commands/test_suite_management.rb +183 -0
- data/lib/cli/configs.rb +34 -0
- data/lib/cli/consts.rb +8 -0
- data/lib/cli/ctt_extensions.rb +36 -0
- data/lib/cli/errors.rb +24 -0
- data/lib/cli/report.rb +62 -0
- data/lib/cli/runner.rb +134 -0
- data/lib/cli/suites.rb +36 -0
- data/lib/cli/version.rb +6 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/unit/help_spec.rb +15 -0
- metadata +198 -0
data/README.md
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require "fileutils"
|
2
|
+
require "rake"
|
3
|
+
require "rspec/core/rake_task"
|
4
|
+
|
5
|
+
desc "build gem file"
|
6
|
+
task :build do
|
7
|
+
config_path = File.expand_path("./config")
|
8
|
+
unless Dir.exists?(config_path)
|
9
|
+
FileUtils.mkdir(config_path)
|
10
|
+
end
|
11
|
+
src = File.join(config_path, "template/commands.yml")
|
12
|
+
dest = File.join(config_path, "commands.yml")
|
13
|
+
FileUtils.cp(src, dest)
|
14
|
+
system("gem build tac-cli.gemspec -V")
|
15
|
+
end
|
16
|
+
|
17
|
+
if defined?(RSpec)
|
18
|
+
namespace :spec do
|
19
|
+
SPEC_OPTS = %w(--format documentation --colour)
|
20
|
+
|
21
|
+
desc "Run unit tests"
|
22
|
+
RSpec::Core::RakeTask.new(:unit) do |t|
|
23
|
+
t.pattern = "spec/unit/**/*_spec.rb"
|
24
|
+
t.rspec_opts = SPEC_OPTS
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
desc "Run tests"
|
29
|
+
task :spec => %w(spec:unit)
|
30
|
+
end
|
data/bin/tac
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$:.unshift(File.expand_path("../../lib", __FILE__))
|
4
|
+
require "cli"
|
5
|
+
|
6
|
+
begin
|
7
|
+
Thread.abort_on_exception = true
|
8
|
+
CTT::Cli::Runner.run(ARGV.dup)
|
9
|
+
rescue Errno::EPIPE
|
10
|
+
puts("pipe closed, exiting...")
|
11
|
+
exit(0)
|
12
|
+
rescue Interrupt
|
13
|
+
puts "\nExiting..."
|
14
|
+
exit(1)
|
15
|
+
end
|
data/config/commands.yml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
commands:
|
3
|
+
help:
|
4
|
+
usage: help
|
5
|
+
desc: list all avaiable commands
|
6
|
+
tests:
|
7
|
+
usage: tests
|
8
|
+
desc: run default multiple test suites.
|
9
|
+
"add suite":
|
10
|
+
usage: "add suite <Test Suite Path> [alias]"
|
11
|
+
desc: "add specific test suite to tac, and one alias can be given to resolve naming conflict"
|
12
|
+
suites:
|
13
|
+
usage: "suites"
|
14
|
+
desc: "list all available test suites registered "
|
15
|
+
|
data/lib/cli.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
require "optparse"
|
4
|
+
require "yaml"
|
5
|
+
require "paint"
|
6
|
+
require "interact"
|
7
|
+
require "terminal-table"
|
8
|
+
require "libxml"
|
9
|
+
require "tempfile"
|
10
|
+
require "uuidtools"
|
11
|
+
require "etc"
|
12
|
+
require "restclient"
|
13
|
+
|
14
|
+
|
15
|
+
|
16
|
+
require "cli/version"
|
17
|
+
require "cli/consts"
|
18
|
+
require "cli/ctt_extensions"
|
19
|
+
require "cli/cmd_consts"
|
20
|
+
require "cli/suites"
|
21
|
+
require "cli/configs"
|
22
|
+
require "cli/errors"
|
23
|
+
require "cli/runner"
|
24
|
+
require "cli/base"
|
25
|
+
require "cli/report"
|
26
|
+
require "cli/client_collector"
|
27
|
+
require "cli/commands/help"
|
28
|
+
require "cli/commands/suites_configure"
|
29
|
+
require "cli/commands/multiple_tests"
|
30
|
+
require "cli/commands/test_suite_management"
|
data/lib/cli/base.rb
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module CTT::Cli
|
4
|
+
class ClientCollector
|
5
|
+
|
6
|
+
def initialize(command, suite, runner)
|
7
|
+
@info = {}
|
8
|
+
@suite = suite
|
9
|
+
@suites = runner.suites
|
10
|
+
@uuid = runner.uuid
|
11
|
+
|
12
|
+
@info[:suite] = @suite
|
13
|
+
@info[:command] = command
|
14
|
+
end
|
15
|
+
|
16
|
+
def post
|
17
|
+
collect
|
18
|
+
|
19
|
+
payload = @info.dup
|
20
|
+
payload[:results_file] = @tar_file
|
21
|
+
payload[:multipart] = true
|
22
|
+
|
23
|
+
#retry 3 times
|
24
|
+
3.times do
|
25
|
+
begin
|
26
|
+
response = RestClient.post("#{RESULTS_SERVER_URL}/tac/upload", payload)
|
27
|
+
rescue
|
28
|
+
end
|
29
|
+
break if response.code == 200
|
30
|
+
end
|
31
|
+
@tar_file.unlink
|
32
|
+
end
|
33
|
+
|
34
|
+
def collect
|
35
|
+
get_os
|
36
|
+
get_test_reports
|
37
|
+
get_uuid
|
38
|
+
get_timestamp
|
39
|
+
get_hostname
|
40
|
+
get_username
|
41
|
+
get_ipaddr
|
42
|
+
end
|
43
|
+
|
44
|
+
def get_hostname
|
45
|
+
@info[:hostname] = `hostname`.strip
|
46
|
+
end
|
47
|
+
|
48
|
+
def get_username
|
49
|
+
@info[:username] = Etc.getlogin
|
50
|
+
end
|
51
|
+
|
52
|
+
def get_ipaddr
|
53
|
+
@info[:ip] = UDPSocket.open {|s| s.connect("64.233.187.99", 1); s.addr.last}
|
54
|
+
end
|
55
|
+
|
56
|
+
def get_uuid
|
57
|
+
@info[:uuid] = @uuid.to_s
|
58
|
+
end
|
59
|
+
|
60
|
+
def get_timestamp
|
61
|
+
@info[:time] = Time.now.getutc.to_i
|
62
|
+
end
|
63
|
+
|
64
|
+
def get_os
|
65
|
+
@info[:os] = RUBY_PLATFORM
|
66
|
+
|
67
|
+
case 1.size
|
68
|
+
when 4
|
69
|
+
@info[:platform] = "32bit"
|
70
|
+
when 8
|
71
|
+
@info[:platform] = "64bit"
|
72
|
+
else
|
73
|
+
@info[:platform] = nil
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
def get_test_reports
|
79
|
+
suite_config_path = File.absolute_path(File.join(@suites.suites["suites"][@suite], TEST_SUITE_CONFIG_FILE))
|
80
|
+
suite_config = YAML.load_file(suite_config_path)
|
81
|
+
unless suite_config["results"]
|
82
|
+
say("no results field in #{suite_config_path}. abort!", :red)
|
83
|
+
exit(1)
|
84
|
+
end
|
85
|
+
report_path = File.absolute_path(File.join(@suites.suites["suites"][@suite], suite_config["results"]))
|
86
|
+
unless File.exists?(report_path)
|
87
|
+
say("report path did not exists. abort!", :red)
|
88
|
+
exit(1)
|
89
|
+
end
|
90
|
+
@tar_file = zip_test_reports(report_path)
|
91
|
+
end
|
92
|
+
|
93
|
+
def zip_test_reports(reports_path)
|
94
|
+
tar_file = Tempfile.new(%w(reports .tgz))
|
95
|
+
`tar czf #{tar_file.path} #{reports_path} 2>&1`
|
96
|
+
unless $?.exitstatus == 0
|
97
|
+
say("fail to tarball test reports. abort!", :red)
|
98
|
+
tar_file.unlink
|
99
|
+
exit(1)
|
100
|
+
end
|
101
|
+
tar_file
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
|
2
|
+
module CTT::Cli
|
3
|
+
STATIC_COMMANDS = {"help" => {"usage" => "help",
|
4
|
+
"desc" => "list all available commands"},
|
5
|
+
"tests" => {"usage" => "tests",
|
6
|
+
"desc" => "run default multiple test suites."},
|
7
|
+
"rerun" => {"usage" => "rerun",
|
8
|
+
"desc" => "rerun failed cases for multiple test suites."},
|
9
|
+
"add suite" => {"usage" => "add suite",
|
10
|
+
"desc" => "add specific test suite to tac"},
|
11
|
+
"delete suite" => {"usage" => "delete suite",
|
12
|
+
"desc" => "list all test suites configuration"},
|
13
|
+
"suites" => {"usage" => "suites",
|
14
|
+
"desc" => "list all test suites configuration"}}
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
|
2
|
+
module CTT::Cli::Command
|
3
|
+
|
4
|
+
class MultipleTests < Base
|
5
|
+
|
6
|
+
MAX_RERUN_TIMES = 10
|
7
|
+
|
8
|
+
include CTT::Cli
|
9
|
+
|
10
|
+
def initialize(command, args, runner)
|
11
|
+
super(args, runner)
|
12
|
+
@suites = @runner.suites
|
13
|
+
@command = command
|
14
|
+
end
|
15
|
+
|
16
|
+
def run
|
17
|
+
eval(@command)
|
18
|
+
end
|
19
|
+
|
20
|
+
def tests
|
21
|
+
say("run multiple test suites", :green)
|
22
|
+
run_tests
|
23
|
+
show_summary
|
24
|
+
end
|
25
|
+
|
26
|
+
def rerun
|
27
|
+
say("rerun failed cases for multiple test suites", :green)
|
28
|
+
rerun_tests
|
29
|
+
show_summary(true)
|
30
|
+
end
|
31
|
+
|
32
|
+
def rerun_tests
|
33
|
+
index = 1
|
34
|
+
@suites.suites["suites"].each do |name, _|
|
35
|
+
say("#{index}) start to run failed cases for test suite: #{name}\n", :yellow)
|
36
|
+
args = @args.insert(0, @command)
|
37
|
+
cmd = TestSuite.new(name, args, @runner)
|
38
|
+
cmd.run
|
39
|
+
index += 1
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def run_tests
|
44
|
+
index = 1
|
45
|
+
@suites.suites["suites"].each do |name, _|
|
46
|
+
say("#{index}) start to run test suite: #{name}\n", :yellow)
|
47
|
+
cmd = TestSuite.new(name, @args, @runner)
|
48
|
+
cmd.run
|
49
|
+
index += 1
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def show_summary(rerun = false)
|
54
|
+
summary = {:total => 0,
|
55
|
+
:failed => 0,
|
56
|
+
:pending => 0,
|
57
|
+
:duration => 0.0,
|
58
|
+
:failed_cases => {},
|
59
|
+
:pending_cases => {}
|
60
|
+
}
|
61
|
+
@suites.suites["suites"].each do |name, path|
|
62
|
+
suite_config_path = File.absolute_path(File.join(path, TEST_SUITE_CONFIG_FILE))
|
63
|
+
suite_config = YAML.load_file(suite_config_path)
|
64
|
+
unless suite_config["results"]
|
65
|
+
say("no results field in #{suite_config_path}. abort!", :red)
|
66
|
+
exit(1)
|
67
|
+
end
|
68
|
+
|
69
|
+
result_path = File.join(path, suite_config["results"])
|
70
|
+
if rerun
|
71
|
+
result_file = File.join(get_rerun_folder(result_path), TEST_RESULT_FILE)
|
72
|
+
else
|
73
|
+
result_file = File.join(result_path, TEST_RESULT_FILE)
|
74
|
+
end
|
75
|
+
|
76
|
+
report = TestReport.new(result_file)
|
77
|
+
report.parse
|
78
|
+
|
79
|
+
summary[:total] += report.summary[:total]
|
80
|
+
summary[:failed] += report.summary[:failed]
|
81
|
+
summary[:pending] += report.summary[:pending]
|
82
|
+
summary[:duration] += report.summary[:duration]
|
83
|
+
summary[:failed_cases][name] = report.summary[:failed_cases]
|
84
|
+
summary[:pending_cases][name] = report.summary[:pending_cases]
|
85
|
+
end
|
86
|
+
|
87
|
+
print_cases_summary(summary)
|
88
|
+
print_failed_cases(summary)
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
def print_cases_summary(summary)
|
93
|
+
say("\nFinished in #{format_time(summary[:duration])}")
|
94
|
+
|
95
|
+
color = :green
|
96
|
+
color = :yellow if summary[:pending] > 0
|
97
|
+
color = :red if summary[:failed] > 0
|
98
|
+
say("#{summary[:total]} examples, #{summary[:failed]} failures, #{summary[:pending]} pendings", color)
|
99
|
+
end
|
100
|
+
|
101
|
+
def print_failed_cases(summary)
|
102
|
+
unless summary[:failed_cases].empty?
|
103
|
+
say("\nFailures:")
|
104
|
+
summary[:failed_cases].each do |suite, cases|
|
105
|
+
say(" Test Suite: #{suite}", :yellow)
|
106
|
+
cases.each do |c|
|
107
|
+
say(" #{c.strip}", :red)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
say("execute #{yellow("rerun")} command to run all failed cases.")
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def format_time(t)
|
115
|
+
time_str = ''
|
116
|
+
time_str += (t / 3600).to_i.to_s + " hours " if t > 3600
|
117
|
+
time_str += (t % 3600 / 60).to_i.to_s + " minutes " if t > 60
|
118
|
+
time_str += (t % 60).to_f.round(2).to_s + " seconds"
|
119
|
+
time_str
|
120
|
+
end
|
121
|
+
|
122
|
+
def get_rerun_folder(result_folder)
|
123
|
+
rerun_folder = result_folder
|
124
|
+
i = MAX_RERUN_TIMES
|
125
|
+
while(i > 0)
|
126
|
+
if File.exists?(File.join(result_folder, "rerun#{i}"))
|
127
|
+
rerun_folder = File.join(result_folder, "rerun#{i}")
|
128
|
+
break
|
129
|
+
end
|
130
|
+
i -= 1
|
131
|
+
end
|
132
|
+
rerun_folder
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
|
2
|
+
module CTT::Cli::Command
|
3
|
+
|
4
|
+
class RerunTests < Base
|
5
|
+
|
6
|
+
include CTT::Cli
|
7
|
+
|
8
|
+
def initialize(args, runner)
|
9
|
+
super(args, runner)
|
10
|
+
@suites = @runner.suites
|
11
|
+
end
|
12
|
+
|
13
|
+
def run
|
14
|
+
say("rerun failed cases", :green)
|
15
|
+
#run_tests
|
16
|
+
show_summary
|
17
|
+
end
|
18
|
+
|
19
|
+
def run_tests
|
20
|
+
index = 1
|
21
|
+
@suites.suites["suites"].each do |name, _|
|
22
|
+
say("#{index}) start to run test suite: #{name}\n", :yellow)
|
23
|
+
cmd = TestSuite.new(name, @args, @runner)
|
24
|
+
cmd.run
|
25
|
+
index += 1
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
|
2
|
+
module CTT::Cli::Command
|
3
|
+
|
4
|
+
class SuitesConfig < Base
|
5
|
+
|
6
|
+
#TEST_SUITE_CONFIG_FILE = "tac.yml"
|
7
|
+
|
8
|
+
include Interactive
|
9
|
+
|
10
|
+
def initialize(command, args, runner)
|
11
|
+
super(args, runner)
|
12
|
+
|
13
|
+
pieces = command.split(" ")
|
14
|
+
pieces.insert(0, "list") if pieces.size == 1
|
15
|
+
@action, _ = pieces
|
16
|
+
|
17
|
+
@configs = @runner.configs
|
18
|
+
@suites = @runner.suites
|
19
|
+
end
|
20
|
+
|
21
|
+
def run
|
22
|
+
eval(@action)
|
23
|
+
end
|
24
|
+
|
25
|
+
def add
|
26
|
+
puts "add suite"
|
27
|
+
#location = @configs.configs["suites"][@suite]["location"]
|
28
|
+
location = ""
|
29
|
+
invalid_input = true
|
30
|
+
3.times do
|
31
|
+
user_input = ask("test suite source directory").strip
|
32
|
+
if user_input =~ /^~/
|
33
|
+
user_input = File.expand_path(user_input)
|
34
|
+
else
|
35
|
+
user_input = File.absolute_path(user_input)
|
36
|
+
end
|
37
|
+
if Dir.exist?(user_input)
|
38
|
+
if File.exist?(File.join(user_input, TEST_SUITE_CONFIG_FILE))
|
39
|
+
invalid_input = false
|
40
|
+
location = user_input
|
41
|
+
break
|
42
|
+
else
|
43
|
+
say("the configure file: #{yellow(TEST_SUITE_CONFIG_FILE)} " +
|
44
|
+
"cannot be found under #{user_input}.")
|
45
|
+
end
|
46
|
+
else
|
47
|
+
say("the directory: #{user_input} is invalid.")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
if invalid_input
|
52
|
+
say("invalid inputs for 3 times. abort!", :red)
|
53
|
+
exit(1)
|
54
|
+
end
|
55
|
+
|
56
|
+
load_test_suite_config(location)
|
57
|
+
suite_alias = @suite_config["name"]
|
58
|
+
|
59
|
+
@suites.suites["suites"][suite_alias] = location
|
60
|
+
@suites.save
|
61
|
+
say("configure suite: #{suite_alias} successfully.", :green)
|
62
|
+
end
|
63
|
+
|
64
|
+
def list
|
65
|
+
print_suites
|
66
|
+
end
|
67
|
+
|
68
|
+
def load_test_suite_config(location)
|
69
|
+
path = File.join(location, TEST_SUITE_CONFIG_FILE)
|
70
|
+
@suite_config = YAML.load_file(path)
|
71
|
+
unless @suite_config.is_a?(Hash)
|
72
|
+
say("#{path} is not valid yml file.", :red)
|
73
|
+
exit(1)
|
74
|
+
end
|
75
|
+
|
76
|
+
# validate test suite config file
|
77
|
+
validate_points = %w(name commands results)
|
78
|
+
validate_points.each do |p|
|
79
|
+
unless @suite_config.keys.include?(p)
|
80
|
+
say("field: '#{p}' is not found in config file #{path}", :red)
|
81
|
+
exit(1)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def print_suites
|
87
|
+
header = ["alias", "path"]
|
88
|
+
rows = []
|
89
|
+
@suites.suites["suites"].each do |suite_alias, path|
|
90
|
+
rows << [suite_alias, path]
|
91
|
+
end
|
92
|
+
table = Terminal::Table.new(:headings => header, :rows => rows)
|
93
|
+
puts table
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,183 @@
|
|
1
|
+
|
2
|
+
module CTT::Cli::Command
|
3
|
+
|
4
|
+
class TestSuite < Base
|
5
|
+
|
6
|
+
#USER_INPUT = "USER_INPUT"
|
7
|
+
#TEST_SUITE_CONFIG_FILE = "tac.yml"
|
8
|
+
SUPPORT_OPTIONS = {"--force" => "bypass git dirty state check"}
|
9
|
+
|
10
|
+
include Interactive
|
11
|
+
|
12
|
+
def initialize(command, args, runner)
|
13
|
+
super(args, runner)
|
14
|
+
|
15
|
+
pieces = command.split(" ")
|
16
|
+
pieces.insert(0, "") if pieces.size == 1
|
17
|
+
action, suite = pieces
|
18
|
+
|
19
|
+
@action = action
|
20
|
+
@suite = suite
|
21
|
+
@configs = runner.configs
|
22
|
+
@suites = runner.suites
|
23
|
+
end
|
24
|
+
|
25
|
+
def run
|
26
|
+
@action = "test" if @action == ""
|
27
|
+
eval(@action)
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
def list
|
32
|
+
check_configuration
|
33
|
+
get_suite_configs
|
34
|
+
|
35
|
+
say("all subcommands for test suite: #{@suite}", :yellow)
|
36
|
+
say("Options:", :yellow)
|
37
|
+
SUPPORT_OPTIONS.each do |opt, helper|
|
38
|
+
say("\t[#{opt}] \t#{helper}")
|
39
|
+
end
|
40
|
+
nl
|
41
|
+
|
42
|
+
@suite_configs["commands"].each do |command, details|
|
43
|
+
say("#{@suite} #{command}", :green)
|
44
|
+
say("\t#{details["desc"]}\n")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def configure
|
49
|
+
puts "configure #{@suite}"
|
50
|
+
location = @configs.configs["suites"][@suite]["location"]
|
51
|
+
location = "" if location == USER_INPUT
|
52
|
+
invalid_input = true
|
53
|
+
3.times do
|
54
|
+
user_input = ask("suite: #{@suite} source directory:", :default => location).strip
|
55
|
+
if user_input =~ /^~/
|
56
|
+
user_input = File.expand_path(user_input)
|
57
|
+
else
|
58
|
+
user_input = File.absolute_path(user_input)
|
59
|
+
end
|
60
|
+
if Dir.exist?(user_input)
|
61
|
+
if File.exist?(File.join(user_input, TEST_SUITE_CONFIG_FILE))
|
62
|
+
invalid_input = false
|
63
|
+
location = user_input
|
64
|
+
break
|
65
|
+
else
|
66
|
+
say("the configure file: #{yellow(TEST_SUITE_CONFIG_FILE)} " +
|
67
|
+
"cannot be found under #{user_input}.")
|
68
|
+
end
|
69
|
+
else
|
70
|
+
say("the directory: #{user_input} is invalid.")
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
if invalid_input
|
75
|
+
say("invalid inputs for 3 times. abort!", :red)
|
76
|
+
exit(1)
|
77
|
+
else
|
78
|
+
@configs.configs["suites"][@suite]["location"] = location
|
79
|
+
@configs.save
|
80
|
+
say("configure suite: #{@suite} successfully.", :green)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def test
|
85
|
+
check_configuration
|
86
|
+
parse_options
|
87
|
+
check_if_dirty_state unless @options["--force"]
|
88
|
+
get_suite_configs
|
89
|
+
|
90
|
+
dependencies, command = parse_command
|
91
|
+
|
92
|
+
threads = []
|
93
|
+
threads << Thread.new do
|
94
|
+
Dir.chdir(@suites.suites["suites"][@suite])
|
95
|
+
# dependency should be successful before run testing command
|
96
|
+
say("preparing test suite: #{@suite}...")
|
97
|
+
dependencies.each do |d|
|
98
|
+
`#{d}`
|
99
|
+
exit(1) unless $? == 0
|
100
|
+
end
|
101
|
+
|
102
|
+
say("\nrun command: #{yellow(command)}")
|
103
|
+
system(command)
|
104
|
+
end
|
105
|
+
|
106
|
+
threads.each { |t| t.join }
|
107
|
+
|
108
|
+
collector = ClientCollector.new(@runner.command, @suite, @runner)
|
109
|
+
collector.post
|
110
|
+
end
|
111
|
+
|
112
|
+
def check_configuration
|
113
|
+
unless @suites.suites["suites"].has_key?(@suite)
|
114
|
+
say("suite configure file: #{@suites.file} did not has key: #{@suite}")
|
115
|
+
exit(1)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def check_if_dirty_state
|
120
|
+
if dirty_state?
|
121
|
+
say("\n%s\n" % [`git status`])
|
122
|
+
say("Your current directory has some local modifications, " +
|
123
|
+
"please discard or commit them first.\n" +
|
124
|
+
"Or use #{yellow("--force")} to bypass git dirty check.")
|
125
|
+
exit(1)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def dirty_state?
|
130
|
+
`which git`
|
131
|
+
return false unless $? == 0
|
132
|
+
|
133
|
+
Dir.chdir(@suites.suites["suites"][@suite])
|
134
|
+
(File.directory?(".git") || File.directory?(File.join("..", ".git"))) \
|
135
|
+
&& `git status --porcelain | wc -l`.to_i > 0
|
136
|
+
end
|
137
|
+
|
138
|
+
def parse_options
|
139
|
+
@options = {}
|
140
|
+
opts = SUPPORT_OPTIONS.keys
|
141
|
+
@args.each do |arg|
|
142
|
+
if opts.index(arg)
|
143
|
+
@options[arg] = true
|
144
|
+
@args.delete(arg)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def parse_command
|
150
|
+
subcmd = ""
|
151
|
+
dependencies = []
|
152
|
+
if @args.empty?
|
153
|
+
subcmd = @suite_configs["commands"]["default"]["exec"]
|
154
|
+
dependencies = @suite_configs["commands"]["default"]["dependencies"]
|
155
|
+
elsif @suite_configs["commands"].has_key?(@args[0])
|
156
|
+
subcmd = @suite_configs["commands"][@args[0]]["exec"]
|
157
|
+
dependencies = @suite_configs["commands"][@args[0]]["dependencies"]
|
158
|
+
@args.delete(@args[0])
|
159
|
+
else
|
160
|
+
say("#{@args[0]} is not invalid sub-command, run as default command")
|
161
|
+
subcmd = @suite_configs["commands"]["default"]["exec"]
|
162
|
+
dependencies = @suite_configs["commands"]["default"]["dependencies"]
|
163
|
+
end
|
164
|
+
|
165
|
+
unless @args.empty?
|
166
|
+
subcmd = subcmd + " " + @args.join(" ")
|
167
|
+
end
|
168
|
+
|
169
|
+
[dependencies, subcmd]
|
170
|
+
end
|
171
|
+
|
172
|
+
def get_suite_configs
|
173
|
+
suite_configs_path = File.join(@suites.suites["suites"][@suite],
|
174
|
+
CTT::Cli::TEST_SUITE_CONFIG_FILE)
|
175
|
+
@suite_configs ||= YAML.load_file(suite_configs_path)
|
176
|
+
unless @suite_configs.is_a?(Hash)
|
177
|
+
say("invalid yaml format for file: #{suite_configs_path}", :red)
|
178
|
+
exit(1)
|
179
|
+
end
|
180
|
+
@suite_configs
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
data/lib/cli/configs.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
|
2
|
+
module CTT::Cli
|
3
|
+
class Configs
|
4
|
+
|
5
|
+
attr_accessor :configs, :suites
|
6
|
+
|
7
|
+
attr_reader :commands
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@suites = Suites.new.suites
|
11
|
+
load_commands
|
12
|
+
end
|
13
|
+
|
14
|
+
def load_commands
|
15
|
+
@commands = STATIC_COMMANDS.dup
|
16
|
+
commands = {}
|
17
|
+
@suites["suites"].each do |suite, _|
|
18
|
+
# for each suite, three commands should be added.
|
19
|
+
# - configure suite
|
20
|
+
# - suite [subcommand]
|
21
|
+
# - list suite
|
22
|
+
commands[suite] = {"usage" => "#{suite} [subcommand]",
|
23
|
+
"desc" => "run default test for test suite: #{suite}," +
|
24
|
+
" if no subcommand is specified"}
|
25
|
+
|
26
|
+
key = "list #{suite}"
|
27
|
+
commands[key] = {"usage" => key,
|
28
|
+
"desc" => "list all available subcommands for test suite: #{suite}"}
|
29
|
+
end
|
30
|
+
|
31
|
+
@commands.merge!(commands)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/cli/consts.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
|
2
|
+
module CTTExtensions
|
3
|
+
|
4
|
+
def say(message, color = nil, sep = "\n")
|
5
|
+
msg = message
|
6
|
+
puts Paint[msg, color]
|
7
|
+
end
|
8
|
+
|
9
|
+
def nl
|
10
|
+
puts ""
|
11
|
+
end
|
12
|
+
|
13
|
+
def err(message)
|
14
|
+
raise CTT::Cli::CliError, message
|
15
|
+
end
|
16
|
+
|
17
|
+
def red(message)
|
18
|
+
Paint[message, :red]
|
19
|
+
end
|
20
|
+
|
21
|
+
def yellow(message)
|
22
|
+
Paint[message, :yellow]
|
23
|
+
end
|
24
|
+
|
25
|
+
def green(message)
|
26
|
+
Paint[message, :green]
|
27
|
+
end
|
28
|
+
|
29
|
+
def cyan(message)
|
30
|
+
Paint[message, :cyan]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class Object
|
35
|
+
include CTTExtensions
|
36
|
+
end
|
data/lib/cli/errors.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
|
2
|
+
module CTT::Cli
|
3
|
+
class CliError < StandardError
|
4
|
+
attr_reader :exit_code
|
5
|
+
|
6
|
+
def initialize(*args)
|
7
|
+
@exit_code = 1
|
8
|
+
super(*args)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.error_code(code = nil)
|
12
|
+
define_method(:error_code) { code }
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.exit_code(code = nil)
|
16
|
+
define_method(:exit_code) { code }
|
17
|
+
end
|
18
|
+
|
19
|
+
error_code(42)
|
20
|
+
end
|
21
|
+
|
22
|
+
class UnknownCommand < CliError; error_code(100); end
|
23
|
+
|
24
|
+
end
|
data/lib/cli/report.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
|
2
|
+
module CTT::Cli
|
3
|
+
class TestReport
|
4
|
+
|
5
|
+
attr_reader :summary
|
6
|
+
|
7
|
+
def initialize(xml_file)
|
8
|
+
unless File.exists?(xml_file)
|
9
|
+
say("Test result file: #{xml_file} does not exist. abort!", :red)
|
10
|
+
exit(1)
|
11
|
+
end
|
12
|
+
|
13
|
+
begin
|
14
|
+
@doc = LibXML::XML::Document.file(xml_file)
|
15
|
+
rescue Exception => e
|
16
|
+
say("Test result file: #{xml_file} is not a valid xml document. abort!", :red)
|
17
|
+
exit(1)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def parse
|
22
|
+
@summary = {:total => 0,
|
23
|
+
:failed => 0,
|
24
|
+
:pending => 0,
|
25
|
+
:duration => 0.0,
|
26
|
+
:failed_cases => [],
|
27
|
+
:pending_cases => []}
|
28
|
+
|
29
|
+
cases = @doc.find("//case")
|
30
|
+
@summary[:total] = cases.size
|
31
|
+
get_duration
|
32
|
+
cases.each do |c|
|
33
|
+
get_failed_case(c)
|
34
|
+
get_pending_case(c)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def get_duration
|
39
|
+
# duration
|
40
|
+
duration = @doc.find_first("/result/duration")
|
41
|
+
@summary[:duration] += duration.content.to_f
|
42
|
+
end
|
43
|
+
|
44
|
+
def get_failed_case(case_node)
|
45
|
+
# failed
|
46
|
+
failed = case_node.find_first("errorDetails")
|
47
|
+
if failed
|
48
|
+
@summary[:failed] += 1
|
49
|
+
@summary[:failed_cases] << case_node.find_first("rerunCommand").content
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def get_pending_case(case_node)
|
54
|
+
# pending
|
55
|
+
pending = case_node.find_first("skipped")
|
56
|
+
if pending.content == "true"
|
57
|
+
@summary[:pending] += 1
|
58
|
+
@summary[:pending_cases] << case_node.find_first("testName").content
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/lib/cli/runner.rb
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
|
2
|
+
module CTT::Cli
|
3
|
+
|
4
|
+
class Runner
|
5
|
+
|
6
|
+
attr_reader :commands, :uuid, :command
|
7
|
+
attr_accessor :configs, :suites
|
8
|
+
|
9
|
+
|
10
|
+
# @param [Array] args
|
11
|
+
def self.run(args)
|
12
|
+
new(args).run
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(args)
|
16
|
+
@args = args
|
17
|
+
|
18
|
+
banner = "Usage: tac [<options>] <command> [<args>]"
|
19
|
+
@option_parser = OptionParser.new(banner)
|
20
|
+
|
21
|
+
@configs = Configs.new
|
22
|
+
@commands = @configs.commands
|
23
|
+
@suites = Suites.new
|
24
|
+
@uuid = UUIDTools::UUID.random_create
|
25
|
+
end
|
26
|
+
|
27
|
+
def run
|
28
|
+
#puts "the args: #{@args}"
|
29
|
+
parse_global_options
|
30
|
+
|
31
|
+
if @args.empty? && @options.empty?
|
32
|
+
say(usage)
|
33
|
+
exit(0)
|
34
|
+
end
|
35
|
+
|
36
|
+
if @options[:help]
|
37
|
+
Command::Help.new(@args, self).run
|
38
|
+
exit(0)
|
39
|
+
end
|
40
|
+
|
41
|
+
find, command, args = search_commands
|
42
|
+
unless find
|
43
|
+
say("invalid command. abort!", :red)
|
44
|
+
exit(1)
|
45
|
+
end
|
46
|
+
|
47
|
+
@command = command
|
48
|
+
execute(command, args)
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
def parse_global_options
|
53
|
+
@options = {}
|
54
|
+
opts = @option_parser
|
55
|
+
|
56
|
+
opts.on("-v", "--version", "Show version") do
|
57
|
+
@options[:version] = true
|
58
|
+
say("tac %s" % [CTT::Cli::VERSION])
|
59
|
+
exit(0)
|
60
|
+
end
|
61
|
+
|
62
|
+
opts.on("-h", "--help", "Show help message") do
|
63
|
+
@options[:help] = true
|
64
|
+
end
|
65
|
+
|
66
|
+
begin
|
67
|
+
@args = @option_parser.order!(@args)
|
68
|
+
rescue
|
69
|
+
say("invalid command. abort!", :red)
|
70
|
+
exit(1)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def usage
|
75
|
+
@option_parser.to_s
|
76
|
+
end
|
77
|
+
|
78
|
+
def search_commands
|
79
|
+
cmds = @commands.keys
|
80
|
+
|
81
|
+
find = nil
|
82
|
+
longest_cmd = ""
|
83
|
+
args = []
|
84
|
+
size = @args.size
|
85
|
+
size.times do |index|
|
86
|
+
|
87
|
+
longest_cmd = @args[0..(size - index - 1)].join(" ")
|
88
|
+
find = cmds.index(longest_cmd)
|
89
|
+
if find
|
90
|
+
args = @args[(size - index)..-1]
|
91
|
+
break
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
[find, longest_cmd, args]
|
96
|
+
end
|
97
|
+
|
98
|
+
def execute(command, args)
|
99
|
+
handler = get_command_handler(command, args)
|
100
|
+
handler.run
|
101
|
+
end
|
102
|
+
|
103
|
+
def get_command_handler(command, args)
|
104
|
+
# handle runtime commands
|
105
|
+
#
|
106
|
+
@configs.suites["suites"].keys.each do |s|
|
107
|
+
if command =~ /#{s}/
|
108
|
+
#pieces = command.split(" ")
|
109
|
+
#pieces.insert(0, "") if pieces.size == 1
|
110
|
+
#action, suite = pieces
|
111
|
+
return Command::TestSuite.new(command, args, self)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# handle user defined alias
|
116
|
+
#
|
117
|
+
# TODO
|
118
|
+
|
119
|
+
# handle static commands
|
120
|
+
#
|
121
|
+
handler =
|
122
|
+
case command
|
123
|
+
when "help"
|
124
|
+
Command::Help.new(args, self)
|
125
|
+
when "add suite", "delete suite", "suites"
|
126
|
+
Command::SuitesConfig.new(command, args, self)
|
127
|
+
when "tests", "rerun"
|
128
|
+
Command::MultipleTests.new(command, args, self)
|
129
|
+
else
|
130
|
+
nil
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
data/lib/cli/suites.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module CTT::Cli
|
4
|
+
class Suites
|
5
|
+
|
6
|
+
SUITES_CONFIG_FILE =
|
7
|
+
File.absolute_path(File.join(ENV["HOME"], ".tac/suites.yml"))
|
8
|
+
|
9
|
+
attr_accessor :suites
|
10
|
+
|
11
|
+
attr_reader :file
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
load
|
15
|
+
save
|
16
|
+
end
|
17
|
+
|
18
|
+
def load
|
19
|
+
@file = SUITES_CONFIG_FILE
|
20
|
+
unless Dir.exists?(File.dirname(@file))
|
21
|
+
Dir.mkdir(File.dirname(@file))
|
22
|
+
end
|
23
|
+
|
24
|
+
if File.exists?(@file)
|
25
|
+
@suites = YAML.load_file(@file)
|
26
|
+
else
|
27
|
+
@suites = {"suites" => {}}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def save
|
32
|
+
File.open(@file, "w") { |f| f.write YAML.dump(@suites) }
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
data/lib/cli/version.rb
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
|
2
|
+
require "rspec/core"
|
3
|
+
require "fileutils"
|
4
|
+
|
5
|
+
$:.unshift(File.expand_path("../../lib", __FILE__))
|
6
|
+
require "cli"
|
7
|
+
|
8
|
+
RSpec.configure do |c|
|
9
|
+
c.before(:each) do
|
10
|
+
config_path = File.expand_path("./config")
|
11
|
+
unless Dir.exists?(config_path)
|
12
|
+
FileUtils.mkdir(config_path)
|
13
|
+
end
|
14
|
+
src = File.join(config_path, "template/commands.yml")
|
15
|
+
dest = File.join(config_path, "commands.yml")
|
16
|
+
FileUtils.cp(src, dest)
|
17
|
+
end
|
18
|
+
|
19
|
+
c.color_enabled = true
|
20
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
|
2
|
+
require "spec_helper"
|
3
|
+
|
4
|
+
describe CTT::Cli::Command::Help do
|
5
|
+
|
6
|
+
it "list help command" do
|
7
|
+
args = "help abc def"
|
8
|
+
output = `./bin/tac #{args}`
|
9
|
+
keywords = ["help", "tests",
|
10
|
+
"add suite <Test Suite Path> [alias]"]
|
11
|
+
keywords.each do |w|
|
12
|
+
output.should include w
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
metadata
ADDED
@@ -0,0 +1,198 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tac-cli
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.3
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Pin Xie
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-12-03 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: json_pure
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.6.1
|
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: 1.6.1
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: progressbar
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 0.9.0
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 0.9.0
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: terminal-table
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 1.4.2
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.4.2
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: paint
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 0.8.5
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 0.8.5
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: interact
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ~>
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: 0.4.8
|
86
|
+
type: :runtime
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ~>
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: 0.4.8
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: libxml-ruby
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ~>
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: 2.3.3
|
102
|
+
type: :runtime
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ~>
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: 2.3.3
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: uuidtools
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ~>
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 2.1.3
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ~>
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: 2.1.3
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: rest-client
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ~>
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: 1.6.7
|
134
|
+
type: :runtime
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ~>
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: 1.6.7
|
142
|
+
description: Test automation client tool
|
143
|
+
email: pxie@vmware.com
|
144
|
+
executables:
|
145
|
+
- tac
|
146
|
+
extensions: []
|
147
|
+
extra_rdoc_files: []
|
148
|
+
files:
|
149
|
+
- bin/tac
|
150
|
+
- lib/cli.rb
|
151
|
+
- lib/cli/base.rb
|
152
|
+
- lib/cli/client_collector.rb
|
153
|
+
- lib/cli/cmd_consts.rb
|
154
|
+
- lib/cli/commands/help.rb
|
155
|
+
- lib/cli/commands/multiple_tests.rb
|
156
|
+
- lib/cli/commands/rerun.rb
|
157
|
+
- lib/cli/commands/suites_configure.rb
|
158
|
+
- lib/cli/commands/test_suite_management.rb
|
159
|
+
- lib/cli/configs.rb
|
160
|
+
- lib/cli/consts.rb
|
161
|
+
- lib/cli/ctt_extensions.rb
|
162
|
+
- lib/cli/errors.rb
|
163
|
+
- lib/cli/report.rb
|
164
|
+
- lib/cli/runner.rb
|
165
|
+
- lib/cli/suites.rb
|
166
|
+
- lib/cli/version.rb
|
167
|
+
- README.md
|
168
|
+
- Rakefile
|
169
|
+
- config/commands.yml
|
170
|
+
- spec/spec_helper.rb
|
171
|
+
- spec/unit/help_spec.rb
|
172
|
+
homepage: http://www.vmware.com
|
173
|
+
licenses: []
|
174
|
+
post_install_message:
|
175
|
+
rdoc_options: []
|
176
|
+
require_paths:
|
177
|
+
- lib
|
178
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
179
|
+
none: false
|
180
|
+
requirements:
|
181
|
+
- - ! '>='
|
182
|
+
- !ruby/object:Gem::Version
|
183
|
+
version: '0'
|
184
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
185
|
+
none: false
|
186
|
+
requirements:
|
187
|
+
- - ! '>='
|
188
|
+
- !ruby/object:Gem::Version
|
189
|
+
version: '0'
|
190
|
+
requirements: []
|
191
|
+
rubyforge_project:
|
192
|
+
rubygems_version: 1.8.19
|
193
|
+
signing_key:
|
194
|
+
specification_version: 3
|
195
|
+
summary: TAC CLI
|
196
|
+
test_files:
|
197
|
+
- spec/spec_helper.rb
|
198
|
+
- spec/unit/help_spec.rb
|