uicov 0.0.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.
- checksums.yaml +7 -0
- data/.gitignore +6 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +20 -0
- data/LICENSE +22 -0
- data/README.md +42 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/bin/uicov +6 -0
- data/lib/uicov.rb +33 -0
- data/lib/uicov/commands/command.rb +23 -0
- data/lib/uicov/commands/gather.rb +171 -0
- data/lib/uicov/commands/gentpl.rb +129 -0
- data/lib/uicov/commands/merge.rb +95 -0
- data/lib/uicov/commands/report.rb +119 -0
- data/lib/uicov/consts.rb +78 -0
- data/lib/uicov/coverage/action_data.rb +8 -0
- data/lib/uicov/coverage/check_data.rb +8 -0
- data/lib/uicov/coverage/data.rb +45 -0
- data/lib/uicov/coverage/element_data.rb +8 -0
- data/lib/uicov/coverage/member_data.rb +18 -0
- data/lib/uicov/coverage/screen_data.rb +109 -0
- data/lib/uicov/coverage/transition_data.rb +18 -0
- data/lib/uicov/coverage/types.rb +13 -0
- data/lib/uicov/coverage_data.rb +72 -0
- data/lib/uicov/coverage_info.rb +27 -0
- data/lib/uicov/main.rb +53 -0
- data/lib/uicov/opts.rb +28 -0
- data/lib/uicov/ruby_patches.rb +36 -0
- data/lib/uicov/screen_info.rb +7 -0
- data/lib/uicov/transition_info.rb +11 -0
- data/lib/uicov/ui_coverage.rb +91 -0
- data/lib/uicov/version.rb +7 -0
- data/rakefile +7 -0
- data/uicov.gemspec +32 -0
- metadata +110 -0
@@ -0,0 +1,95 @@
|
|
1
|
+
#=======
|
2
|
+
# Author: Alexey Lyanguzov (budabum@gmail.com)
|
3
|
+
#=======
|
4
|
+
|
5
|
+
module UICov
|
6
|
+
class Merge < Command
|
7
|
+
DEFAULT_FILENAME = 'merged.uic'
|
8
|
+
OPTIONS = {
|
9
|
+
'--merged-file=FILE' => "File to store merged coverage [default is '#{DEFAULT_FILENAME}']",
|
10
|
+
# '--no-transitions' => 'Do not merge transitions coverage',
|
11
|
+
# '--no-actions ' => 'Do not merge actions coverage',
|
12
|
+
# '--no-checks ' => 'Do not merge checks coverage',
|
13
|
+
# '--no-elements ' => 'Do not merge elements coverage'
|
14
|
+
}
|
15
|
+
USAGE_INFO = %Q^[options] template.uic file1.uic [file2.uic ... fileN.uic]
|
16
|
+
\n\rWhere options are:
|
17
|
+
#{OPTIONS.inject([]){|a, e| a << "\r\t#{e[0]}\t- #{e[1]}"; a}.join("\n")}
|
18
|
+
^
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
@merged_file = DEFAULT_FILENAME
|
22
|
+
end
|
23
|
+
|
24
|
+
def do_job(args)
|
25
|
+
usage 'Missed coverage file', USAGE_INFO if args.empty?
|
26
|
+
cov_files = process_args args
|
27
|
+
merge(cov_files)
|
28
|
+
@merged.save(@merged_file)
|
29
|
+
end
|
30
|
+
|
31
|
+
def merge(cov_files)
|
32
|
+
Log.warn 'Only one file is given. Nothing to merge.' if cov_files.size == 1
|
33
|
+
@merged = CovData.load cov_files[0]
|
34
|
+
cov_files[1..-1].each do |cov_file|
|
35
|
+
@cd = CovData.load cov_file
|
36
|
+
@cd.screens.each do |name, screen_data|
|
37
|
+
msd = @merged.screens[name]
|
38
|
+
if msd.nil?
|
39
|
+
@merged.screens[name] = screen_data.dup
|
40
|
+
else
|
41
|
+
merge_screen_data msd, screen_data
|
42
|
+
end
|
43
|
+
end
|
44
|
+
@merged.input_files.merge! @cd.input_files
|
45
|
+
end
|
46
|
+
return @merged
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
def process_args(args)
|
51
|
+
merged_file_option = args.grep(/--merged-file=.*/)[0]
|
52
|
+
if merged_file_option
|
53
|
+
@merged_file = File.expand_path merged_file_option.gsub(/.*=(.+)/, '\1')
|
54
|
+
args.delete_if { |e| e == merged_file_option }
|
55
|
+
end
|
56
|
+
return args
|
57
|
+
end
|
58
|
+
|
59
|
+
def merge_screen_data(msd, sd)
|
60
|
+
sd.elements.each do |name, sde|
|
61
|
+
me = msd.elements[name]
|
62
|
+
if me.nil?
|
63
|
+
msd.elements[name] = sde.dup
|
64
|
+
else
|
65
|
+
me.hit(sde.hits)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
sd.transitions.each do |name, sde|
|
69
|
+
me = msd.transitions[name]
|
70
|
+
if me.nil?
|
71
|
+
msd.transitions[name] = sde.dup
|
72
|
+
else
|
73
|
+
me.hit(sde.hits)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
sd.actions.each do |name, sde|
|
77
|
+
me = msd.actions[name]
|
78
|
+
if me.nil?
|
79
|
+
msd.actions[name] = sde.dup
|
80
|
+
else
|
81
|
+
me.hit(sde.hits)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
sd.checks.each do |name, sde|
|
85
|
+
me = msd.checks[name]
|
86
|
+
if me.nil?
|
87
|
+
msd.checks[name] = sde.dup
|
88
|
+
else
|
89
|
+
me.hit(sde.hits)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
@@ -0,0 +1,119 @@
|
|
1
|
+
#=======
|
2
|
+
# Author: Alexey Lyanguzov (budabum@gmail.com)
|
3
|
+
#=======
|
4
|
+
|
5
|
+
module UICov
|
6
|
+
class Report < Command
|
7
|
+
DEFAULT_FILENAME = 'uicov.report.html'
|
8
|
+
OPTIONS = {
|
9
|
+
'--report-file=FILE' => "File to store report [default is '#{DEFAULT_FILENAME}']",
|
10
|
+
# '--format=FORMAT ' => 'Report format. One of: html, puml [default is "html"]',
|
11
|
+
# '--no-transitions ' => 'Do not report transitions coverage',
|
12
|
+
# '--no-actions ' => 'Do not report actions coverage',
|
13
|
+
# '--no-checks ' => 'Do not report checks coverage',
|
14
|
+
# '--no-elements ' => 'Do not report elements coverage'
|
15
|
+
}
|
16
|
+
USAGE_INFO = %Q^[options] file1.uic [file2.uic ... fileN.uic]
|
17
|
+
\n\rWhere options are:
|
18
|
+
#{OPTIONS.inject([]){|a, e| a << "\r\t#{e[0]}\t- #{e[1]}"; a}.join("\n")}
|
19
|
+
^
|
20
|
+
|
21
|
+
def initialize
|
22
|
+
@report_file = DEFAULT_FILENAME
|
23
|
+
end
|
24
|
+
|
25
|
+
def do_job(args)
|
26
|
+
usage 'Missed coverage file', USAGE_INFO if args.empty?
|
27
|
+
cov_files = process_args args
|
28
|
+
@cd = merged_file(cov_files)
|
29
|
+
@html = ''
|
30
|
+
@html << add_header
|
31
|
+
@html << create_summary_report
|
32
|
+
@html << create_screens_summary_report
|
33
|
+
@html << create_per_screen_report
|
34
|
+
save @report_file
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
def process_args(args)
|
39
|
+
report_file_option = args.grep(/--report-file=.*/)[0]
|
40
|
+
if report_file_option
|
41
|
+
@report_file = File.expand_path report_file_option.gsub(/.*=(.+)/, '\1')
|
42
|
+
args.delete_if { |e| e == report_file_option }
|
43
|
+
end
|
44
|
+
return args
|
45
|
+
end
|
46
|
+
|
47
|
+
def merged_file(cov_files)
|
48
|
+
cov_files.size > 1 ? Merge.new.merge(cov_files) : CovData.load(cov_files.first)
|
49
|
+
end
|
50
|
+
|
51
|
+
def add_header
|
52
|
+
%Q^
|
53
|
+
<style>
|
54
|
+
.covtable{
|
55
|
+
border: thin solid black;
|
56
|
+
text-align: left;
|
57
|
+
}
|
58
|
+
BODY,TABLE {font-size: small}
|
59
|
+
H2{text-align:center;}
|
60
|
+
TH{border:thin solid black;text-align:center; background-color: #CCCCCC}
|
61
|
+
TD{border:thin solid black;text-align:right}
|
62
|
+
TD.namecol{border:thin solid black;text-align:left}
|
63
|
+
CAPTION{text-align: left; font-weight: bold}
|
64
|
+
</style>
|
65
|
+
^
|
66
|
+
end
|
67
|
+
|
68
|
+
def create_summary_report
|
69
|
+
%Q^
|
70
|
+
<h1>Summary Report</h1>
|
71
|
+
^
|
72
|
+
end
|
73
|
+
|
74
|
+
def create_screens_summary_report
|
75
|
+
# tr_line1 = "<tr><td><b>%s</b></td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>"
|
76
|
+
tr_line2 = "<tr>
|
77
|
+
<td class='namecol'><b>%s</b></td>
|
78
|
+
<td>%s</td><td>%s</td><td>%s</td>
|
79
|
+
<td>%s</td><td>%s</td><td>%s</td>
|
80
|
+
<td>%s</td><td>%s</td><td>%s</td>
|
81
|
+
<td>%s</td><td>%s</td><td>%s</td>
|
82
|
+
</tr>"
|
83
|
+
str_res = %Q^
|
84
|
+
<h1>Screens Summary Report</h1>
|
85
|
+
<table width='80%'>
|
86
|
+
<tr><th rowspan='2'>Screen</th><th colspan='3'>Elements</th><th colspan='3'>Transitions</th><th colspan='3'>Actions</th><th colspan='3'>Checks</th></tr>
|
87
|
+
<tr><th>Hit</th><th>All</th><th>%</th><th>Hit</th><th>All</th><th>%</th><th>Hit</th><th>All</th><th>%</th><th>Hit</th><th>All</th><th>%</th></tr>
|
88
|
+
^
|
89
|
+
total = @cd.screens.inject([0,0,0, 0,0,0, 0,0,0, 0,0,0]) do |arr, pair|
|
90
|
+
name, screen = pair
|
91
|
+
ec = [screen.get_count(:elements, true), screen.get_count(:elements), screen.get_coverage(:elements)]
|
92
|
+
tc = [screen.get_count(:transitions, true), screen.get_count(:transitions), screen.get_coverage(:transitions)]
|
93
|
+
ac = [screen.get_count(:actions, true), screen.get_count(:actions), screen.get_coverage(:actions)]
|
94
|
+
cc = [screen.get_count(:checks, true), screen.get_count(:checks), screen.get_coverage(:checks)]
|
95
|
+
str_res << tr_line2 % [name, *ec, *tc, *ac, *cc]
|
96
|
+
arr
|
97
|
+
end
|
98
|
+
str_res << %Q^
|
99
|
+
#{tr_line2 % ['Total', *total]}
|
100
|
+
</table>
|
101
|
+
^
|
102
|
+
end
|
103
|
+
#{@cd.screens.keys.map{|k| "#{tr_line % [k,'','','','']}".join("\n")}
|
104
|
+
|
105
|
+
def create_per_screen_report
|
106
|
+
%Q^
|
107
|
+
<h1>Detailed Report</h1>
|
108
|
+
#{@cd.screens.values.map{ |s| s.report }.join("\n")}
|
109
|
+
^
|
110
|
+
end
|
111
|
+
|
112
|
+
def save(filename)
|
113
|
+
report_file = File.expand_path filename
|
114
|
+
File.open(report_file, 'w'){|f| f.write(@html)}
|
115
|
+
Log.info "Result saved into file #{report_file}"
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
data/lib/uicov/consts.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
#=======
|
2
|
+
# Author: Alexey Lyanguzov (budabum@gmail.com)
|
3
|
+
#=======
|
4
|
+
|
5
|
+
module UICov
|
6
|
+
GEM_TESTS_DIR = "#{GEM_HOME}/test"
|
7
|
+
GEM_TESTS_DATA_DIR = "#{GEM_TESTS_DIR}/data"
|
8
|
+
GEM_LIB_DIR = "#{GEM_HOME}/lib/uicov"
|
9
|
+
|
10
|
+
require "#{GEM_LIB_DIR}/ruby_patches"
|
11
|
+
require "#{GEM_LIB_DIR}/version"
|
12
|
+
|
13
|
+
require "#{GEM_LIB_DIR}/commands/command"
|
14
|
+
require "#{GEM_LIB_DIR}/main"
|
15
|
+
|
16
|
+
require "#{GEM_LIB_DIR}/coverage/types"
|
17
|
+
require "#{GEM_LIB_DIR}/coverage/data"
|
18
|
+
require "#{GEM_LIB_DIR}/coverage/member_data"
|
19
|
+
require "#{GEM_LIB_DIR}/coverage/transition_data"
|
20
|
+
require "#{GEM_LIB_DIR}/coverage/action_data"
|
21
|
+
require "#{GEM_LIB_DIR}/coverage/check_data"
|
22
|
+
require "#{GEM_LIB_DIR}/coverage/element_data"
|
23
|
+
require "#{GEM_LIB_DIR}/coverage/screen_data"
|
24
|
+
|
25
|
+
require "#{GEM_LIB_DIR}/opts"
|
26
|
+
require "#{GEM_LIB_DIR}/coverage_info"
|
27
|
+
require "#{GEM_LIB_DIR}/screen_info"
|
28
|
+
require "#{GEM_LIB_DIR}/transition_info"
|
29
|
+
require "#{GEM_LIB_DIR}/coverage_data"
|
30
|
+
require "#{GEM_LIB_DIR}/ui_coverage"
|
31
|
+
|
32
|
+
Log = Logger.new STDOUT
|
33
|
+
Log.level = Logger::DEBUG
|
34
|
+
|
35
|
+
SKIN_PARAMS=%q^
|
36
|
+
skinparam state {
|
37
|
+
FontSize 10
|
38
|
+
AttributeFontSize 10
|
39
|
+
|
40
|
+
BackgroundColor #FCFCFC
|
41
|
+
BorderColor #C0C0C0
|
42
|
+
FontColor #808080
|
43
|
+
ArrowColor #C0C0C0
|
44
|
+
AttributeFontColor #808080
|
45
|
+
ArrowFontColor #808080
|
46
|
+
|
47
|
+
BackgroundColor<<Covered>> #CCFFCC
|
48
|
+
BorderColor<<Covered>> #008800
|
49
|
+
FontColor<<Covered>> #004400
|
50
|
+
AttributeFontColor<<Covered>> #004400
|
51
|
+
|
52
|
+
BackgroundColor<<Missed>> #FFEE88
|
53
|
+
BorderColor<<Missed>> #FF8800
|
54
|
+
FontColor<<Missed>> #886622
|
55
|
+
AttributeFontColor<<Missed>> #886622
|
56
|
+
}
|
57
|
+
^
|
58
|
+
|
59
|
+
LEGEND=%q^
|
60
|
+
state Legend {
|
61
|
+
state "Uncovered Screen from Model" as UncoveredScreen
|
62
|
+
UncoveredScreen -> CoveredScreen : uncovered_transition {0, 0}
|
63
|
+
state "Covered screen from Model" as CoveredScreen<<Covered>>
|
64
|
+
CoveredScreen -[#orange]-> MissedScreen : <font color=orange><b>UNKNOWN</b></font> {1, 1} - covered transition missed in Model
|
65
|
+
CoveredScreen : <b>covered_action_with_3_calls_from_2_tests</b> {3, 2}
|
66
|
+
CoveredScreen : <font color=orange><b>covered_action_missed_in_model</b></font> {1, 1}
|
67
|
+
state "Screen missed in Model" as MissedScreen<<Missed>>
|
68
|
+
CoveredScreen -[#green]-> CoveredScreen : <font color=green><b>covered_transition_to_self</b></font> {1, 1}
|
69
|
+
MissedScreen : uncovered_action {0, 0}
|
70
|
+
MissedScreen : <b>covered_action_missed_in_model</b> {1, 1}
|
71
|
+
state "Another Covered Screen from Model" as AnotherCoveredScreen<<Covered>>
|
72
|
+
MissedScreen -left[#blue]-> AnotherCoveredScreen : <font color=blue><b>DEEP_LINK</b></font> {1, 1}
|
73
|
+
AnotherCoveredScreen -[#green]-> CoveredScreen : <font color=green><b>covered_transition</b></font> {1, 1}
|
74
|
+
CoveredScreen --> AnotherCoveredScreen : <font color=red>NOT_SET</font> {0, 0} - missed transition name in Model
|
75
|
+
AnotherCoveredScreen : uncovered_action {0 ,0}
|
76
|
+
}
|
77
|
+
^
|
78
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
#=======
|
2
|
+
# Author: Alexey Lyanguzov (budabum@gmail.com)
|
3
|
+
#=======
|
4
|
+
|
5
|
+
module UICov
|
6
|
+
class CovData
|
7
|
+
attr_reader :screens, :input_files
|
8
|
+
attr_accessor :type
|
9
|
+
|
10
|
+
def self.load(filename)
|
11
|
+
YAML.load_file(filename)
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(cov_file=nil)
|
15
|
+
@type = CoverageDataType::UNKNOWN
|
16
|
+
@input_files = {}
|
17
|
+
@screens = {}
|
18
|
+
load(cov_file) unless cov_file.nil?
|
19
|
+
end
|
20
|
+
|
21
|
+
def set_processing_date(date=Time.now)
|
22
|
+
@data_gathered_at = date.strftime('%F %R:%S.%3N')
|
23
|
+
end
|
24
|
+
|
25
|
+
def add_screen(name)
|
26
|
+
@screens[name] ||= ScreenData.new name
|
27
|
+
end
|
28
|
+
|
29
|
+
def add_covered_screen(name)
|
30
|
+
scd = add_screen name
|
31
|
+
scd.hit
|
32
|
+
return scd
|
33
|
+
end
|
34
|
+
|
35
|
+
def add_input_file(filename, filedate)
|
36
|
+
@input_files[filename] = filedate
|
37
|
+
end
|
38
|
+
|
39
|
+
def save(filename)
|
40
|
+
File.open(filename, 'w') { |f| f.write YAML.dump(self) }
|
41
|
+
Log.info "Result saved to '#{File.expand_path(filename)}'"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
#=======
|
2
|
+
# Author: Alexey Lyanguzov (budabum@gmail.com)
|
3
|
+
#=======
|
4
|
+
|
5
|
+
module UICov
|
6
|
+
class MemberData
|
7
|
+
attr_reader :display_name, :hits
|
8
|
+
def initialize(name)
|
9
|
+
@display_name = name
|
10
|
+
@hits = 0
|
11
|
+
end
|
12
|
+
|
13
|
+
def hit(increment=1)
|
14
|
+
@hits += increment
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
@@ -0,0 +1,109 @@
|
|
1
|
+
#=======
|
2
|
+
# Author: Alexey Lyanguzov (budabum@gmail.com)
|
3
|
+
#=======
|
4
|
+
|
5
|
+
module UICov
|
6
|
+
class ScreenData
|
7
|
+
attr_reader :elements, :transitions, :actions, :checks
|
8
|
+
def initialize(name)
|
9
|
+
@name = name
|
10
|
+
@hits = 0
|
11
|
+
@elements = {}
|
12
|
+
@transitions = {}
|
13
|
+
@actions = {}
|
14
|
+
@checks = {}
|
15
|
+
end
|
16
|
+
|
17
|
+
def hit
|
18
|
+
@hits += 1
|
19
|
+
end
|
20
|
+
|
21
|
+
def add_transition(name, to)
|
22
|
+
tr_key = TransitionData.get_key(name, to)
|
23
|
+
transitions[tr_key] ||= TransitionData.new name, to
|
24
|
+
end
|
25
|
+
|
26
|
+
def add_covered_transition(name, to)
|
27
|
+
add_transition(name, to).hit
|
28
|
+
end
|
29
|
+
|
30
|
+
def add_action(name)
|
31
|
+
actions[name] ||= ActionData.new name
|
32
|
+
end
|
33
|
+
|
34
|
+
def add_covered_action(name)
|
35
|
+
add_action(name).hit
|
36
|
+
end
|
37
|
+
|
38
|
+
def add_check(name)
|
39
|
+
checks[name] ||= CheckData.new name
|
40
|
+
end
|
41
|
+
|
42
|
+
def add_covered_check(name)
|
43
|
+
add_check(name).hit
|
44
|
+
end
|
45
|
+
|
46
|
+
def add_element(name)
|
47
|
+
elements[name] ||= ElementData.new name
|
48
|
+
end
|
49
|
+
|
50
|
+
def add_covered_element(name)
|
51
|
+
add_element(name).hit
|
52
|
+
end
|
53
|
+
|
54
|
+
def report
|
55
|
+
%Q^
|
56
|
+
<h2>Screen: <span>#{@name}</span></h2>
|
57
|
+
<h3>Coverage summary</h3>
|
58
|
+
#{report_members_summary 'elements' unless elements.empty?}
|
59
|
+
#{report_members_summary 'transitions' unless transitions.empty?}
|
60
|
+
#{report_members_summary 'actions' unless actions.empty?}
|
61
|
+
#{report_members_summary 'checks' unless checks.empty?}
|
62
|
+
<h3>Coverage details</h3>
|
63
|
+
#{report_members 'elements' unless elements.empty?}
|
64
|
+
#{report_members 'transitions' unless transitions.empty?}
|
65
|
+
#{report_members 'actions' unless actions.empty?}
|
66
|
+
#{report_members 'checks' unless checks.empty?}
|
67
|
+
<hr/>
|
68
|
+
^
|
69
|
+
end
|
70
|
+
|
71
|
+
def get_coverage(members_name)
|
72
|
+
members = instance_variable_get("@#{members_name}")
|
73
|
+
uncovered = members.values.select{|e| e.hits == 0}
|
74
|
+
cov = ((members.size.to_f - uncovered.size) / members.size) * 100
|
75
|
+
return cov.nan? ? 100.0 : cov.round(2)
|
76
|
+
end
|
77
|
+
|
78
|
+
def get_count(members_name, hitted=false)
|
79
|
+
members = instance_variable_get("@#{members_name}")
|
80
|
+
if hitted
|
81
|
+
members.values.keep_if{ |e| 0 < e.hits}.size
|
82
|
+
else
|
83
|
+
members.size
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
def report_members(members_name)
|
89
|
+
members = instance_variable_get("@#{members_name}")
|
90
|
+
%Q^
|
91
|
+
<table class='covtable' width='25%'>
|
92
|
+
<thead><caption>#{members_name.capitalize}:</caption></thead>
|
93
|
+
<tbody>
|
94
|
+
<tr><th>Name</th><th>Hits</th></tr>
|
95
|
+
#{members.values.map{|e| "<tr><td class='namecol'>#{e.display_name}</td><td>#{e.hits}</td></tr>"}.join("\n") }
|
96
|
+
</tbody>
|
97
|
+
</table>
|
98
|
+
<br/>
|
99
|
+
^
|
100
|
+
end
|
101
|
+
|
102
|
+
def report_members_summary(members_name)
|
103
|
+
coverage = get_coverage members_name
|
104
|
+
%Q^
|
105
|
+
<div>#{members_name.capitalize}: #{coverage}%</div>
|
106
|
+
^
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|