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.
@@ -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
+
@@ -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,8 @@
1
+ #=======
2
+ # Author: Alexey Lyanguzov (budabum@gmail.com)
3
+ #=======
4
+
5
+ module UICov
6
+ class ActionData < MemberData
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ #=======
2
+ # Author: Alexey Lyanguzov (budabum@gmail.com)
3
+ #=======
4
+
5
+ module UICov
6
+ class CheckData < MemberData
7
+ end
8
+ 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,8 @@
1
+ #=======
2
+ # Author: Alexey Lyanguzov (budabum@gmail.com)
3
+ #=======
4
+
5
+ module UICov
6
+ class ElementData < MemberData
7
+ end
8
+ end
@@ -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