cukedep 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +15 -0
  2. data/.rspec +1 -0
  3. data/.ruby-gemset +1 -0
  4. data/.ruby-version +1 -0
  5. data/.simplecov +7 -0
  6. data/.travis.yml +15 -0
  7. data/.yardopts +6 -0
  8. data/CHANGELOG.md +3 -0
  9. data/Gemfile +12 -0
  10. data/LICENSE.txt +19 -0
  11. data/README.md +64 -0
  12. data/Rakefile +32 -0
  13. data/bin/cukedep +14 -0
  14. data/cucumber.yml +10 -0
  15. data/lib/cukedep.rb +9 -0
  16. data/lib/cukedep/application.rb +112 -0
  17. data/lib/cukedep/cli/application.rb +0 -0
  18. data/lib/cukedep/cli/cmd-line.rb +115 -0
  19. data/lib/cukedep/config.rb +31 -0
  20. data/lib/cukedep/constants.rb +29 -0
  21. data/lib/cukedep/feature-model.rb +285 -0
  22. data/lib/cukedep/feature-rep.rb +49 -0
  23. data/lib/cukedep/gherkin-listener.rb +98 -0
  24. data/lib/macros4cuke.rb +8 -0
  25. data/sample/features/step_definitions/steps.rb +105 -0
  26. data/sample/features/support/env.rb +12 -0
  27. data/sample/model/model.rb +216 -0
  28. data/spec/cukedep/application_spec.rb +81 -0
  29. data/spec/cukedep/cli/cmd-line_spec.rb +91 -0
  30. data/spec/cukedep/feature-model_spec.rb +103 -0
  31. data/spec/cukedep/feature-rep_spec.rb +53 -0
  32. data/spec/cukedep/file-parsing.rb +40 -0
  33. data/spec/cukedep/gherkin-listener_spec.rb +59 -0
  34. data/spec/cukedep/sample_features/a_few_tests.feature +24 -0
  35. data/spec/cukedep/sample_features/more_tests.feature +24 -0
  36. data/spec/cukedep/sample_features/other_tests.feature +15 -0
  37. data/spec/cukedep/sample_features/some_tests.feature +13 -0
  38. data/spec/cukedep/sample_features/standalone.feature +19 -0
  39. data/spec/cukedep/sample_features/still_other_tests.feature +24 -0
  40. data/spec/cukedep/sample_features/yet_other_tests.feature +19 -0
  41. data/spec/spec_helper.rb +18 -0
  42. data/templates/rake.erb +163 -0
  43. metadata +165 -0
@@ -0,0 +1,8 @@
1
+ # File: macros4cuke.rb
2
+ # This file acts as a jumping-off point for loading dependencies expected
3
+ # for a Macros4Cuke user.
4
+
5
+ require_relative './macros4cuke/constants'
6
+ require_relative './macros4cuke/macro-step-support'
7
+
8
+ # End of file
@@ -0,0 +1,105 @@
1
+ # File: steps.rb
2
+ # Step definitions for a sample Cucumber application
3
+
4
+ Given(/^the catalogue is empty$/) do
5
+ $store.zap_catalogue!()
6
+ end
7
+
8
+ Given(/^I add the video "(.*?)" to the catalogue$/) do |a_title|
9
+ $store.add_video(a_title)
10
+ end
11
+
12
+ Then(/^I should see the video "(.*?)" as unknown$/) do |a_title|
13
+ $store.search_video(a_title).should be_nil
14
+ end
15
+
16
+ Then(/^I should see the video "(.*?)" as (available)$/) do |a_title, a_state|
17
+ found_video = $store.search_video(a_title)
18
+ found_video.state.should == a_state.to_sym
19
+ end
20
+
21
+ When(/^I remove the video "(.*?)"$/) do |a_title|
22
+ found_video = $store.search_video(a_title)
23
+ found_video.should_not be_nil
24
+ found_video.state.should == :available
25
+ $store.remove_video(found_video)
26
+ end
27
+
28
+ Given(/^there is no member yet$/) do
29
+ $store.send(:zap_members!) # Why is this method seen as private?
30
+ end
31
+
32
+ Then(/^I should see member "(.*?)" as unknown$/) do |member_name|
33
+ $store.search_member(member_name).should be_nil
34
+ end
35
+
36
+ Then(/^I should see member "(.*?)" as registered$/) do |member_name|
37
+ $store.search_member(member_name).should_not be_nil
38
+ puts "Member #{member_name} is registered."
39
+ end
40
+
41
+ Given(/^I subscribe "(.*?)"$/) do |member_name|
42
+ $store.add_member(member_name)
43
+ end
44
+
45
+
46
+ Given(/^there is no registered user$/) do
47
+ $store.zap_users!
48
+ end
49
+
50
+ When(/^I enter the credentials "(.*?)"$/) do |credential|
51
+ @entered_credential = credential
52
+ end
53
+
54
+ Then(/^I should not be authorized$/) do
55
+ $store.search_user(@entered_credential).should be_nil
56
+ puts "Invalid user credential"
57
+ end
58
+
59
+ When(/^I register my credentials "(.*?)"$/) do |credential|
60
+ $store.add_user(credential)
61
+ end
62
+
63
+ Then(/^I should see a welcome message$/) do
64
+ $store.search_user(@entered_credential).should_not be_nil
65
+ puts "Welcome to the rental application."
66
+ end
67
+
68
+
69
+ When(/^I register the rental of "(.*?)" for "(.*?)"$/) do |a_title, member_name|
70
+ found_video = $store.search_video(a_title)
71
+ found_video.should_not be_nil
72
+ found_video.state.should == :available
73
+
74
+ member = $store.search_member(member_name)
75
+ member.should_not be_nil
76
+ $store.add_rental(found_video, member)
77
+ @confirm_rental = true
78
+ end
79
+
80
+ Then(/^I should see the rental confirmed$/) do
81
+ puts "Rental registered." if @confirm_rental
82
+ @confirm_rental = nil
83
+ end
84
+
85
+ Then(/^I should see the rental refused$/) do
86
+ puts "Rental refused." unless @confirm_rental
87
+ end
88
+
89
+
90
+ When(/^I register the return of "(.*?)" from "(.*?)"$/) do |a_title, member_name|
91
+ rental = $store.search_rental(a_title)
92
+ rental.should_not be_nil
93
+ rental.member.should == member_name
94
+ $store.close_rental(rental)
95
+ @confirm_return = true
96
+ end
97
+
98
+ Then(/^I should see the return confirmed$/) do
99
+ puts "Return registered." if @confirm_return
100
+ @confirm_return = nil
101
+ end
102
+
103
+
104
+
105
+ # End of file
@@ -0,0 +1,12 @@
1
+ # File: env.rb
2
+
3
+ require 'rspec/expectations'
4
+ require_relative '../../model/model'
5
+
6
+ AfterConfiguration do |_|
7
+ # Quick and dirty implementation: use a global variable
8
+ # as an entry point to the domain model.
9
+ $store = Sample::RentalStore.new
10
+ end
11
+
12
+ # End of file
@@ -0,0 +1,216 @@
1
+ # File: model.rb
2
+
3
+ require 'pp'
4
+ require 'yaml' # Rely on YAML for object persistence
5
+
6
+
7
+ # Module used as a namespace
8
+ module Sample
9
+
10
+ # Assumption: the store has one examplar only of a video title
11
+ Video = Struct.new(
12
+ :title, # Title of the video (as an identifier)
13
+ :state # Current state of the video
14
+ )
15
+
16
+ # A member of the video store, i.e. a person that is allowed
17
+ # to rent a video from the store.
18
+ Member = Struct.new(
19
+ :name # As an identifier
20
+ )
21
+
22
+ # Association object between a Video and a Member.
23
+ # In our sample model, no rental history is performed
24
+ Rental = Struct.new(
25
+ :video, # Use the title of the rented video as key
26
+ :member # Use the name of the Member as key
27
+ )
28
+
29
+ # The identification of a store rental employee
30
+ # that is authorized to use the rental software.
31
+ User = Struct.new(
32
+ :credential # user credential
33
+ )
34
+
35
+ # Simplistic domain model of a video rental store.
36
+ class RentalStore
37
+ MyDir = File.dirname(__FILE__) + '/'
38
+ CatalogueFile = 'catalogue.yml'
39
+ MembersFile = 'members.yml'
40
+ RentalsFile = 'rentals.yml'
41
+ UsersFile = 'users.yml'
42
+
43
+
44
+ # The list of all videos owned by the store.
45
+ attr_reader(:catalogue)
46
+
47
+ # The list of all Members
48
+ attr_reader(:members)
49
+
50
+ # The list of all open-standing rentals
51
+ attr_reader(:rentals)
52
+
53
+ # The list of application user (credentials)
54
+ attr_reader(:users)
55
+
56
+ def initialize()
57
+ init_catalogue()
58
+ init_members()
59
+ init_rentals()
60
+ init_users()
61
+ end
62
+
63
+
64
+ # Clear the catalogue (= make it empty).
65
+ def zap_catalogue!()
66
+ catalogue.clear()
67
+ save_catalogue()
68
+ zap_rentals!()
69
+ end
70
+
71
+ def zap_members!()
72
+ members.clear()
73
+ save_members()
74
+ zap_rentals!()
75
+ end
76
+
77
+ def zap_users!()
78
+ users.clear()
79
+ save_users()
80
+ end
81
+
82
+ # Search a Video object having the given title
83
+ def search_video(aTitle)
84
+ result = catalogue.find {|video| video.title.strip == aTitle.strip}
85
+ if result.nil?
86
+ msg = "Video with title '#{aTitle}' isn't in the catalogue."
87
+ $stderr.puts msg
88
+ end
89
+
90
+ return result
91
+ end
92
+
93
+ def remove_video(aVideo)
94
+ catalogue.delete(aVideo)
95
+ save_catalogue()
96
+ end
97
+
98
+ # Add a new video to the catalogue
99
+ def add_video(aTitle)
100
+ # Simplification: no check for title collision
101
+ catalogue << Video.new(aTitle, :available)
102
+ save_catalogue()
103
+ end
104
+
105
+ def search_member(aName)
106
+ mb = members.find {|person| person.name.strip == aName.strip}
107
+ puts "No member with name #{aName}." if mb.nil?
108
+
109
+ return mb
110
+ end
111
+
112
+ def add_member(aName)
113
+ # Simplification: no check for name collision
114
+ members << Member.new(aName)
115
+ save_members()
116
+ end
117
+
118
+ def search_user(aCredential)
119
+ users.find {|user| user.credential.strip == aCredential.strip}
120
+ end
121
+
122
+ def add_user(aCredential)
123
+ # Simplification: no check for credential collision
124
+ users << User.new(aCredential)
125
+ save_users()
126
+ end
127
+
128
+ def search_rental(aTitle)
129
+ rentals.find {|r| r.video.strip == aTitle.strip}
130
+ end
131
+
132
+ def add_rental(aVideo, aMember)
133
+ rentals << Rental.new(aVideo.title, aMember.name)
134
+ aVideo.state = :rented
135
+ save_rentals
136
+ end
137
+
138
+ def close_rental(aRental)
139
+ rentals.delete(aRental)
140
+ save_rentals
141
+ returned_video = search_video(aRental.video)
142
+ returned_video.state = :available
143
+ end
144
+
145
+ private
146
+ def init_catalogue()
147
+ filepath = MyDir + CatalogueFile
148
+ if File.exist?(filepath)
149
+ @catalogue = YAML.load_file(filepath)
150
+ else
151
+ @catalogue = []
152
+ end
153
+ end
154
+
155
+ def init_members()
156
+ filepath = MyDir + MembersFile
157
+ if File.exist?(filepath)
158
+ @members = YAML.load_file(filepath)
159
+ else
160
+ @members = []
161
+ end
162
+ end
163
+
164
+ def init_users()
165
+ filepath = MyDir + UsersFile
166
+ if File.exist?(filepath)
167
+ @users = YAML.load_file(filepath)
168
+ else
169
+ @users = []
170
+ end
171
+ end
172
+
173
+ def init_rentals()
174
+ filepath = MyDir + RentalsFile
175
+ if File.exist?(filepath)
176
+ @rentals = YAML.load_file(filepath)
177
+ else
178
+ @rentals = []
179
+ end
180
+ end
181
+
182
+ def zap_rentals!()
183
+ rentals.clear
184
+ save_rentals()
185
+ end
186
+
187
+ def zap_members!()
188
+ members.clear
189
+ save_members()
190
+ end
191
+
192
+ def save_catalogue()
193
+ filepath = MyDir + CatalogueFile
194
+ File.open(filepath, 'w') {|f| YAML.dump(catalogue, f)}
195
+ end
196
+
197
+ def save_members()
198
+ filepath = MyDir + MembersFile
199
+ File.open(filepath, 'w') {|f| YAML.dump(members, f)}
200
+ end
201
+
202
+ def save_users()
203
+ filepath = MyDir + UsersFile
204
+ File.open(filepath, 'w') {|f| YAML.dump(users, f)}
205
+ end
206
+
207
+ def save_rentals()
208
+ filepath = MyDir + RentalsFile
209
+ File.open(filepath, 'w') {|f| YAML.dump(rentals, f)}
210
+ end
211
+
212
+ end # class
213
+
214
+ end # module
215
+
216
+ # End of file
@@ -0,0 +1,81 @@
1
+ # File: cmd-line_spec.rb
2
+
3
+ require 'stringio'
4
+ require_relative '../spec_helper'
5
+
6
+ # Load the class under testing
7
+ require_relative '../../lib/cukedep/application'
8
+
9
+ module Cukedep # Open module to get rid of long qualified names
10
+
11
+ describe Application do
12
+
13
+ context 'Creation & initialization:' do
14
+ it 'should be created without argument' do
15
+ expect { Application.new }.not_to raise_error
16
+ end
17
+ end # context
18
+
19
+ context 'Provided services:' do
20
+ subject { Application.new }
21
+
22
+ it 'should read its command-line' do
23
+ options = subject.send(:options_from, %w[])
24
+ expect(options).to be_empty
25
+ end
26
+
27
+ it 'should generate a user settings file' do
28
+ # Start state: no config
29
+ File.delete(Cukedep::YMLFilename) if File.exist?(Cukedep::YMLFilename)
30
+
31
+ # --setup option creates the config file then stops the application
32
+ expect { subject.start!(['--setup'])}.to raise_error(SystemExit)
33
+
34
+ # Check that the config file was effectively created.
35
+ expect { File.exist?(Cukedep::YMLFilename) }.to be_true
36
+ created_config = subject.send(:load_cfg)
37
+ expect(created_config).to eq(Config.default)
38
+
39
+ # Re-run again with --setup option.
40
+ # It should ask permission for overwriting
41
+ # Capture console IO
42
+ old_stdout = $>
43
+ ostream = StringIO.new('rw')
44
+ $> = ostream
45
+ old_stdin = $stdin
46
+ $stdin = StringIO.new("n\n", 'r')
47
+ expect { subject.start!(['--setup'])}.to raise_error(SystemExit)
48
+ $> = old_stdout
49
+ $sdtin = old_stdin
50
+ end
51
+
52
+ it 'should complain in absence of project dir' do
53
+ # Start state: no config
54
+ File.delete(Cukedep::YMLFilename) if File.exist?(Cukedep::YMLFilename)
55
+ expect(File.exist?(Cukedep::YMLFilename)).to be_false
56
+
57
+ # Create default config
58
+ expect { subject.start!(['--setup'])}.to raise_error(SystemExit)
59
+
60
+ err = StandardError
61
+ err_msg = "No project dir specified via 'Cukedep::YMLFilename' nor via --project option."
62
+ expect {subject.start!([])}.to raise_error(err, err_msg)
63
+ end
64
+
65
+ it 'should parse the feature files' do
66
+ curr_dir = Dir.getwd()
67
+ begin
68
+ file_dir = File.dirname(__FILE__)
69
+ Dir.chdir(file_dir + '/sample_features')
70
+ unless File.exist?(Cukedep::YMLFilename)
71
+ expect { subject.start!(['--setup'])}.to raise_error(SystemExit)
72
+ end
73
+ subject.start!(['--project', '../../../sample'])
74
+ ensure
75
+ Dir.chdir(curr_dir)
76
+ end
77
+ end
78
+ end # context
79
+ end # describe
80
+
81
+ end # module
@@ -0,0 +1,91 @@
1
+ # File: cmd-line_spec.rb
2
+
3
+ require 'stringio'
4
+ require_relative '../../spec_helper'
5
+
6
+ # Load the class under testing
7
+ require_relative '../../../lib/cukedep/cli/cmd-line'
8
+
9
+ module Cukedep # Open module to get rid of long qualified names
10
+
11
+ describe CLI::CmdLine do
12
+
13
+ context 'Creation & initialization:' do
14
+ subject { CLI::CmdLine.new }
15
+
16
+ it 'should be created without argument' do
17
+ expect { CLI::CmdLine.new }.not_to raise_error
18
+ end
19
+ end # context
20
+
21
+ context 'Provided services:' do
22
+ def capture_output()
23
+ @output = $>
24
+ ostream = StringIO.new('rw')
25
+ $> = ostream
26
+ end
27
+
28
+ def release_output()
29
+ $> = @output
30
+ end
31
+
32
+
33
+ it 'should accept an empty command-line' do
34
+ expect { subject.parse!([]) }.not_to raise_error
35
+ expect(subject.options).to be_empty
36
+ end
37
+
38
+ it 'should accept the dry-run option' do
39
+ expect { subject.parse!(['--dry-run']) }.not_to raise_error
40
+ expect(subject.options).to eq({ :"dry-run" => true })
41
+ end
42
+
43
+ it 'should accept the setup option' do
44
+ expect { subject.parse!(['--setup']) }.not_to raise_error
45
+ expect(subject.options).to eq({ :setup => true })
46
+ end
47
+
48
+ it 'should validate the project option argument' do
49
+ # Case 1: missing project dir argument
50
+ cmd_opts = ['--project']
51
+ err_type = StandardError
52
+ err_msg = <<-MSG_END
53
+ No argument provided with command line option: --project
54
+ To see the command-line syntax, do:
55
+ cukedep --help
56
+ MSG_END
57
+ expect { subject.parse!(cmd_opts) }.to raise_error(err_type)
58
+
59
+ # Case 2: non existing project dir
60
+ cmd_opts = ['--project', 'nowhere']
61
+ err_msg = "Cannot find the directory 'nowhere'."
62
+ expect { subject.parse!(cmd_opts) }.to raise_error(err_type, err_msg)
63
+
64
+ # Case 3: project dir exists
65
+ #cmd_opts = ['--project', '../../../sample']
66
+ #expect { subject.parse!(cmd_opts) }.not_to raise_error
67
+ #expect(subject.options).to eq({ :project => '../../../sample' })
68
+ end
69
+
70
+ it 'should handle the version option' do
71
+ capture_output
72
+ cmd_opts = ['--version']
73
+ expect { subject.parse!(cmd_opts) }.to raise_error(SystemExit)
74
+ expect($>.string).to eq(Cukedep::Version + "\n")
75
+ release_output
76
+ end
77
+
78
+ it 'should handle the help option' do
79
+ capture_output
80
+ cmd_opts = ['--help']
81
+ expect { subject.parse!(cmd_opts) }.to raise_error(SystemExit)
82
+ expect($>.string).to eq(subject.parser.to_s)
83
+ release_output
84
+ end
85
+ end # context
86
+
87
+
88
+
89
+ end # describe
90
+
91
+ end # module