cukedep 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.
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