uicov 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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