stepdown 0.3.3 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/Gemfile +4 -2
  2. data/Gemfile.lock +14 -10
  3. data/History.txt +30 -0
  4. data/README.rdoc +2 -2
  5. data/Rakefile +13 -0
  6. data/bin/stepdown +4 -4
  7. data/lib/stepdown.rb +6 -0
  8. data/lib/stepdown/analyzer.rb +67 -0
  9. data/lib/stepdown/feature_parser.rb +30 -0
  10. data/lib/stepdown/html_reporter.rb +41 -0
  11. data/lib/stepdown/options.rb +59 -0
  12. data/lib/stepdown/reporter.rb +106 -0
  13. data/lib/stepdown/scenario.rb +25 -0
  14. data/lib/stepdown/step.rb +22 -0
  15. data/lib/stepdown/step_collection.rb +36 -0
  16. data/lib/stepdown/step_group.rb +45 -0
  17. data/lib/stepdown/step_instance.rb +63 -0
  18. data/lib/stepdown/step_usage.rb +15 -0
  19. data/lib/stepdown/text_reporter.rb +38 -0
  20. data/spec/lib/{feature_parser_spec.rb → stepdown/feature_parser_spec.rb} +13 -12
  21. data/spec/lib/{options_spec.rb → stepdown/options_spec.rb} +24 -6
  22. data/spec/lib/stepdown/reporter_spec.rb +184 -0
  23. data/spec/lib/stepdown/scenario_spec.rb +40 -0
  24. data/spec/lib/stepdown/step_collection_spec.rb +78 -0
  25. data/spec/lib/stepdown/step_group_spec.rb +43 -0
  26. data/spec/lib/{step_instance_spec.rb → stepdown/step_instance_spec.rb} +13 -12
  27. data/spec/spec_helper.rb +4 -0
  28. data/stepdown.gemspec +7 -4
  29. data/templates/main.html.haml +3 -3
  30. data/templates/style.sass +5 -4
  31. metadata +64 -34
  32. data/lib/counting_step.rb +0 -14
  33. data/lib/feature_parser.rb +0 -32
  34. data/lib/html_reporter.rb +0 -37
  35. data/lib/options.rb +0 -69
  36. data/lib/reporter.rb +0 -62
  37. data/lib/scenario.rb +0 -19
  38. data/lib/step.rb +0 -10
  39. data/lib/step_down.rb +0 -112
  40. data/lib/step_group.rb +0 -52
  41. data/lib/step_instance.rb +0 -60
  42. data/lib/step_usage.rb +0 -13
  43. data/lib/text_reporter.rb +0 -36
  44. data/spec/lib/scenario_spec.rb +0 -42
  45. data/spec/lib/step_group_spec.rb +0 -119
@@ -0,0 +1,22 @@
1
+ module Stepdown
2
+ class Step
3
+
4
+ attr_accessor :count
5
+ attr_reader :id, :regex
6
+
7
+ def initialize(id, regex)
8
+ @id = id
9
+ @regex = regex
10
+ @count = 0
11
+ end
12
+
13
+ def <=>(other)
14
+ other.count <=> self.count
15
+ end
16
+
17
+ def ==(other)
18
+ self.id == other.id
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,36 @@
1
+ require 'stepdown/step'
2
+
3
+ module Stepdown
4
+ class StepCollection
5
+ include Enumerable
6
+
7
+ def initialize
8
+ @steps = {}
9
+ end
10
+
11
+ def add_step(id, regex)
12
+ if @steps[id]
13
+ @steps[id].count += 1
14
+ else
15
+ @steps[id] = Stepdown::Step.new(id, regex)
16
+ @steps[id].count = 1
17
+ end
18
+ end
19
+
20
+ def steps
21
+ @steps.collect{|id,step| step }
22
+ end
23
+
24
+ def each
25
+ @steps.each{|id, step| yield step }
26
+ end
27
+
28
+ def [](id)
29
+ @steps[id]
30
+ end
31
+
32
+ def length
33
+ @steps.length
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,45 @@
1
+ require 'cgi'
2
+ require 'stepdown/step_collection'
3
+
4
+ module Stepdown
5
+ class StepGroup
6
+ attr_reader :id, :regex, :use_count
7
+
8
+ def initialize(step)
9
+ @id = step.id
10
+ @regex = step.regex
11
+ @use_count = 0
12
+ @step_collection = Stepdown::StepCollection.new
13
+ end
14
+
15
+ def step_collection
16
+ @step_collection.sort
17
+ end
18
+
19
+ def add_step(step)
20
+ @step_collection.add_step(step.id, step.regex)
21
+ end
22
+
23
+ def add_steps(step_set)
24
+ step_set.each{|step| add_step(step)}
25
+ end
26
+
27
+ def update_use_count(num_steps)
28
+ @use_count += num_steps
29
+ end
30
+
31
+ def group_graph
32
+ base = "https://chart.googleapis.com/chart?cht=gv:dot&chl=graph{"
33
+ base += "a [label=\"#{CGI.escape(CGI.escapeHTML(@regex.inspect.to_s))}\"];"
34
+
35
+ step_collection[0..10].each do |step|
36
+
37
+ next if step.regex.nil?
38
+ base += "a--\"#{CGI.escape(CGI.escapeHTML(step.regex.inspect.to_s))}\" [weight=#{step.count}];"
39
+ #a [label=\"#{grouping.in_steps[0][:step].regex.inspect}\"]; a--b [penwidth=3,weight=2];b--d}"
40
+ end
41
+ base += "}"
42
+ base
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,63 @@
1
+ require 'stepdown/step_collection'
2
+
3
+ module Stepdown
4
+ class StepInstance
5
+ def initialize
6
+ @steps = []
7
+ end
8
+
9
+ def Given(regex,&block)
10
+ define_step(regex,&block)
11
+ end
12
+
13
+ def When(regex,&block)
14
+ define_step(regex,&block)
15
+ end
16
+
17
+ def Then(regex,&block)
18
+ define_step(regex,&block)
19
+ end
20
+
21
+ def define_step(regex,&block)
22
+ @steps << regex
23
+ end
24
+
25
+ def self.method_missing(*args)
26
+ #nothing
27
+ end
28
+
29
+ def method_missing(*args)
30
+ #nothing
31
+ end
32
+
33
+ def self.const_missing(*args)
34
+ self
35
+ end
36
+
37
+ def require(*args)
38
+ # do nothing
39
+ end
40
+
41
+ def line_matches(line)
42
+ stripped_line = line.strip.gsub(/^(And|Given|When|Then) (.*)$/,'\2')
43
+
44
+ @steps.each_with_index do |regex,i|
45
+ match = regex.match(stripped_line)
46
+ if match
47
+ return step_collection.detect{|step| i == step.id}
48
+ end
49
+ end
50
+
51
+ return nil
52
+ end
53
+
54
+ def step_collection
55
+ return @step_collection if @step_collection
56
+ @step_collection = StepCollection.new
57
+ @steps.each_with_index do |regex, i|
58
+ @step_collection.add_step(i, regex)
59
+ end
60
+ @step_collection
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,15 @@
1
+
2
+ module Stepdown
3
+ class StepUsage
4
+
5
+ attr_accessor :total_usage, :number_scenarios, :use_scenario
6
+ attr_reader :step
7
+
8
+ def initialize(step)
9
+ @step = step
10
+ @total_usage = 0
11
+ @number_scenarios = 0
12
+ @use_scenario = 0.0
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,38 @@
1
+ require 'stepdown/reporter'
2
+
3
+ module Stepdown
4
+ class TextReporter < Reporter
5
+
6
+ def output_overview
7
+ puts "Generating report..."
8
+ output = File.new(Reporter::OUTPUT_DIR + '/analysis.txt', "w+")
9
+
10
+ output.puts "Total number of scenarios: #{total_scenarios}"
11
+ output.puts "Total numer of steps: #{total_steps}"
12
+ output.puts "Steps per scenario: #{steps_per_scenario}"
13
+ output.puts "Unique steps per scenario: #{unique_steps}"
14
+
15
+ output.puts "Step usages"
16
+ output.puts "Step|Total usage|Scenarios|Use per scenario"
17
+ usages.each{|use| output.puts used_step_line(use) }
18
+
19
+ output.puts "Unused steps"
20
+ unused_steps.each{|use| output.puts unused_step_line(use) }
21
+
22
+ output.close
23
+
24
+ puts "Report output to #{Reporter::OUTPUT_DIR}/analysis.txt"
25
+
26
+ end
27
+
28
+ def used_step_line(use)
29
+ line = [use.step.regex.inspect, use.total_usage, use.number_scenarios, use.use_scenario]
30
+ line.join("|")
31
+ end
32
+
33
+ def unused_step_line(use)
34
+ use.step.regex.inspect
35
+ end
36
+
37
+ end
38
+ end
@@ -1,7 +1,8 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../../lib/scenario')
2
- require File.expand_path(File.dirname(__FILE__) + '/../../lib/feature_parser')
1
+ require 'spec_helper'
2
+ require 'scenario'
3
+ require 'feature_parser'
3
4
 
4
- describe FeatureParser do
5
+ describe Stepdown::FeatureParser do
5
6
  def stub_line_match_with(instance, line, value)
6
7
  instance.stub!(:line_matches).with(line).and_return(value)
7
8
  end
@@ -15,10 +16,10 @@ describe FeatureParser do
15
16
  instance = mock("instance")
16
17
  file_lines = ["Scenario: My testing scenario"]
17
18
 
18
- @parser = FeatureParser.new
19
+ @parser = Stepdown::FeatureParser.new
19
20
  @parser.should_receive(:read_feature_file).with(file).and_return(file_lines)
20
21
  scenario = mock("scenario")
21
- Scenario.should_receive(:new).and_return(scenario)
22
+ Stepdown::Scenario.should_receive(:new).and_return(scenario)
22
23
 
23
24
  @parser.process_feature(file, instance).should =~ [scenario]
24
25
  end
@@ -28,10 +29,10 @@ describe FeatureParser do
28
29
  instance = mock("instance")
29
30
  file_lines = ["Background: My testing scenario"]
30
31
 
31
- @parser = FeatureParser.new
32
+ @parser = Stepdown::FeatureParser.new
32
33
  @parser.should_receive(:read_feature_file).with(file).and_return(file_lines)
33
34
  scenario = mock("scenario")
34
- Scenario.should_receive(:new).and_return(scenario)
35
+ Stepdown::Scenario.should_receive(:new).and_return(scenario)
35
36
 
36
37
  @parser.process_feature(file, instance).should =~ [scenario]
37
38
  end
@@ -40,7 +41,7 @@ describe FeatureParser do
40
41
 
41
42
  describe "parsing step lines" do
42
43
  before :each do
43
- @parser = FeatureParser.new
44
+ @parser = Stepdown::FeatureParser.new
44
45
  @step_instance = mock("step_instance")
45
46
 
46
47
  end
@@ -50,7 +51,7 @@ describe FeatureParser do
50
51
  unmatched_lines = ["not matched", "not matched 2"]
51
52
  steps = []
52
53
  lines.each_with_index do |line, i|
53
- step = Step.new(i, line)
54
+ step = Stepdown::Step.new(i, line)
54
55
  stub_line_match_with(@step_instance, line, step)
55
56
  steps << step
56
57
  end
@@ -63,21 +64,21 @@ describe FeatureParser do
63
64
  @parser.should_receive(:read_feature_file).and_return(all_lines)
64
65
 
65
66
  scenarios = @parser.process_feature(mock('file'), @step_instance)
66
- scenarios.first.steps.should =~ steps[1..2]
67
+ scenarios.first.steps.collect{|s| s.regex }.should =~ ["matched", "match 2"]
67
68
  end
68
69
 
69
70
  it "should add matched steps" do
70
71
  lines = ["Scenario", "matched", "match 2"]
71
72
  steps = []
72
73
  lines.each_with_index do |line, i|
73
- step = Step.new(i, line)
74
+ step = Stepdown::Step.new(i, line)
74
75
  stub_line_match_with(@step_instance, line, step)
75
76
  steps << step
76
77
  end
77
78
  @parser.should_receive(:read_feature_file).and_return(lines)
78
79
 
79
80
  scenarios = @parser.process_feature(mock('file'), @step_instance)
80
- scenarios.first.steps.should =~ steps[1..2]
81
+ scenarios.first.steps.collect{|s| s.regex }.should =~ ["matched", "match 2"]
81
82
 
82
83
  end
83
84
 
@@ -1,10 +1,10 @@
1
- require 'rspec'
2
- require File.expand_path(File.dirname(__FILE__) + '/../../lib/options')
1
+ require 'spec_helper'
2
+ require 'options'
3
3
 
4
- describe @options do
4
+ describe Stepdown::Options do
5
5
 
6
6
  before :each do
7
- @options = Options.new
7
+ @options = Stepdown::Options.new
8
8
  end
9
9
 
10
10
  describe "setting input directories" do
@@ -66,9 +66,27 @@ describe @options do
66
66
  end
67
67
 
68
68
  describe "validating options" do
69
- it "should report an invalid steps directory"
70
- it "should report an invalid features directory"
69
+ require 'stringio'
70
+ before :each do
71
+ Dir.stub!(:pwd).and_return("")
72
+ @io = StringIO.new
73
+ end
74
+
75
+ it "should report an invalid steps directory" do
76
+ @options.parse(["--steps=steps_dir"])
77
+ lambda do
78
+ @options.validate(@io)
79
+ @io.string.should == "Directory steps_dir does not exist"
80
+ end.should raise_error(SystemExit)
81
+ end
71
82
 
83
+ it "should report an invalid features directory" do
84
+ @options.parse(["--features=features_dir"])
85
+ lambda do
86
+ @options.validate(@io)
87
+ @io.string.should == "Directory features_dir does not exist"
88
+ end.should raise_error(SystemExit)
89
+ end
72
90
  end
73
91
 
74
92
  end
@@ -0,0 +1,184 @@
1
+ require 'stepdown/step_group'
2
+ require 'stepdown/step_usage'
3
+ require 'stepdown/reporter'
4
+ require 'stepdown/scenario'
5
+
6
+ describe Stepdown::Reporter do
7
+
8
+ describe "returning overall statistics" do
9
+ it "should return the total number of steps" do
10
+ steps = [mock('step_1'), mock('step_2')]
11
+ reporter = Stepdown::Reporter.new([], steps)
12
+ reporter.total_steps.should == 2
13
+ end
14
+
15
+ it "should return the total number of scenarios" do
16
+ scenarios = [mock('scenario_1'), mock('scenario_2')]
17
+ reporter = Stepdown::Reporter.new(scenarios, [])
18
+ reporter.total_scenarios.should == 2
19
+ end
20
+
21
+ it "should return the number of steps per scenario" do
22
+ steps = [mock('step_1'), mock('step_2'), mock('step_3')]
23
+ scenario1 = mock("scenario1", :steps => steps, :step_count => steps.length)
24
+ scenario2 = mock("scenario2", :steps => [], :step_count => 0)
25
+
26
+ reporter = Stepdown::Reporter.new([scenario1, scenario2], [])
27
+ reporter.steps_per_scenario.should == "1.50"
28
+ end
29
+
30
+ it "should return the number of unique steps per scenario" do
31
+ steps = [mock('step_1'), mock('step_2'), mock('step_3')]
32
+ scenario1 = mock("scenario1", :steps => steps, :unique_step_count => 2, :step_count => 3)
33
+ scenario2 = mock("scenario2", :steps => steps[0...1], :unique_step_count => 1, :step_count => 1)
34
+
35
+ reporter = Stepdown::Reporter.new([scenario1, scenario2], [])
36
+ reporter.unique_steps.should == "1.33"
37
+ end
38
+
39
+ end
40
+
41
+ #this whole grouping thing needs to be refactored. Nasty.
42
+ describe "creating step groupings" do
43
+ before :each do
44
+ @scen_1 = Stepdown::Scenario.new
45
+ @scen_2 = Stepdown::Scenario.new
46
+ @scenarios = [@scen_1, @scen_2]
47
+
48
+ @collection = Stepdown::StepCollection.new
49
+ @s1 = Stepdown::Step.new(1, /step 1/)
50
+ @s2 = Stepdown::Step.new(2, /step 2/)
51
+ @s3 = Stepdown::Step.new(3, /step 3/)
52
+ @s4 = Stepdown::Step.new(4, /step 4/)
53
+ @s5 = Stepdown::Step.new(5, /step 5/)
54
+
55
+ @collection.add_step(@s1.id, @s1.regex)
56
+ @collection.add_step(@s2.id, @s2.regex)
57
+ @collection.add_step(@s3.id, @s3.regex)
58
+ @collection.add_step(@s4.id, @s4.regex)
59
+ @collection.add_step(@s5.id, @s5.regex)
60
+
61
+ @scen_1.add_step(@s1)
62
+ @scen_1.add_step(@s2)
63
+ @scen_1.add_step(@s3)
64
+ @scen_1.add_step(@s4)
65
+
66
+ @scen_2.add_step(@s1)
67
+ @scen_2.add_step(@s2)
68
+ @scen_2.add_step(@s1)
69
+ @scen_2.add_step(@s5)
70
+
71
+ end
72
+
73
+ it "should return the correct step grouping" do
74
+ reporter = Stepdown::Reporter.new([@scen_1, @scen_2], @collection)
75
+
76
+ reporter.groupings[0].step_collection.should =~ [@s1,@s2,@s3,@s4,@s5]
77
+ reporter.groupings[1].step_collection.should =~ [@s1,@s2,@s3,@s4,@s5]
78
+ reporter.groupings[2].step_collection.should =~ [@s1,@s2,@s5]
79
+ reporter.groupings[3].step_collection.should =~ [@s1,@s2,@s3,@s4]
80
+ reporter.groupings[4].step_collection.should =~ [@s1,@s2,@s3,@s4]
81
+
82
+ end
83
+
84
+ it "should return usage for steps across scenarios" do
85
+ reporter = Stepdown::Reporter.new([@scen_1, @scen_2], @collection)
86
+
87
+ group_1 = reporter.groupings.detect{|g| g.id == 1}
88
+ group_1.use_count.should == 8
89
+ end
90
+
91
+ it "should return usage for steps in scenarios with duplicated steps" do
92
+ reporter = Stepdown::Reporter.new([@scen_1, @scen_2], @collection)
93
+
94
+ group_5 = reporter.groupings.detect{|g| g.id == 5}
95
+ group_5.use_count.should == 4
96
+ end
97
+
98
+ end
99
+
100
+ #this usage thing needs to be refactored as well
101
+ describe "creating step usages" do
102
+ before :each do
103
+ @scen_1 = Stepdown::Scenario.new
104
+ @scen_2 = Stepdown::Scenario.new
105
+ @scenarios = [@scen_1, @scen_2]
106
+
107
+ @collection = Stepdown::StepCollection.new
108
+ @s1 = Stepdown::Step.new(1, /step 1/)
109
+ @s2 = Stepdown::Step.new(2, /step 2/)
110
+ @s3 = Stepdown::Step.new(3, /step 3/)
111
+ @s4 = Stepdown::Step.new(4, /step 4/)
112
+ @s5 = Stepdown::Step.new(5, /step 5/)
113
+
114
+ @collection.add_step(@s1.id, @s1.regex)
115
+ @collection.add_step(@s2.id, @s2.regex)
116
+ @collection.add_step(@s3.id, @s3.regex)
117
+ @collection.add_step(@s4.id, @s4.regex)
118
+ @collection.add_step(@s5.id, @s5.regex)
119
+
120
+ @scen_1.add_step(@s1)
121
+ @scen_1.add_step(@s2)
122
+ @scen_1.add_step(@s3)
123
+ @scen_1.add_step(@s4)
124
+
125
+ @scen_2.add_step(@s1)
126
+ @scen_2.add_step(@s2)
127
+ @scen_2.add_step(@s1)
128
+ @scen_2.add_step(@s5)
129
+ @scen_2.add_step(@s5)
130
+ end
131
+
132
+ it "should return the usage of across scenarios" do
133
+ reporter = Stepdown::Reporter.new([@scen_2, @scen_1], @collection)
134
+
135
+ usage = reporter.usages.detect{|use| use.step.id == 1}
136
+ usage.total_usage.should == 3
137
+ usage.number_scenarios.should == 2
138
+ usage.use_scenario.should == "1.50"
139
+ end
140
+
141
+ it "should return duplicate usage of a step in a scenario" do
142
+ reporter = Stepdown::Reporter.new([@scen_2, @scen_1], @collection)
143
+
144
+ usage = reporter.usages.detect{|use| use.step.id == 5}
145
+ usage.total_usage.should == 2
146
+ usage.number_scenarios.should == 1
147
+ usage.use_scenario.should == "2.00"
148
+ end
149
+
150
+ it "should return usage of a step in a scenario" do
151
+ reporter = Stepdown::Reporter.new([@scen_2, @scen_1], @collection)
152
+
153
+ usage = reporter.usages.detect{|use| use.step.id == 3}
154
+ usage.total_usage.should == 1
155
+ usage.number_scenarios.should == 1
156
+ usage.use_scenario.should == "1.00"
157
+ end
158
+
159
+ end
160
+
161
+ describe "returing step usage" do
162
+ before :each do
163
+ @reporter = Stepdown::Reporter.new([], mock('step_colllection'))
164
+
165
+ @use_1 = Stepdown::StepUsage.new(Stepdown::Step.new(1,/regex/))
166
+ @use_2 = Stepdown::StepUsage.new(Stepdown::Step.new(2,/regex/))
167
+ @use_3 = Stepdown::StepUsage.new(Stepdown::Step.new(3,/regex/))
168
+
169
+ @use_1.total_usage += 1
170
+ @use_2.total_usage += 1
171
+
172
+ @reporter.stub!(:step_usages).and_return([@use_1, @use_2, @use_3])
173
+ end
174
+
175
+ it "should return unused steps" do
176
+ @reporter.usages.should =~ [@use_1, @use_2]
177
+ end
178
+
179
+ it "should return used steps" do
180
+ @reporter.unused_steps.should =~ [@use_3]
181
+ end
182
+ end
183
+
184
+ end