teapot 0.9.10 → 1.0.0.pre.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +6 -3
  3. data/README.md +3 -4
  4. data/Rakefile +3 -6
  5. data/bin/teapot +3 -7
  6. data/lib/teapot/build.rb +166 -18
  7. data/lib/teapot/configuration.rb +0 -1
  8. data/lib/teapot/context.rb +37 -21
  9. data/lib/teapot/controller/build.rb +24 -7
  10. data/lib/teapot/controller/fetch.rb +1 -1
  11. data/lib/teapot/controller.rb +1 -0
  12. data/lib/teapot/definition.rb +1 -1
  13. data/lib/teapot/dependency.rb +2 -6
  14. data/lib/teapot/environment/base.rb +7 -15
  15. data/lib/teapot/environment/constructor.rb +28 -0
  16. data/lib/teapot/environment/flatten.rb +42 -1
  17. data/lib/teapot/environment/system.rb +3 -3
  18. data/lib/teapot/extractors/linker_extractor.rb +2 -2
  19. data/lib/teapot/loader.rb +9 -11
  20. data/lib/teapot/name.rb +4 -0
  21. data/lib/teapot/package.rb +5 -4
  22. data/lib/teapot/repository.rb +29 -1
  23. data/lib/teapot/rule.rb +196 -0
  24. data/lib/teapot/rulebook.rb +91 -0
  25. data/lib/teapot/target.rb +15 -40
  26. data/lib/teapot/version.rb +1 -1
  27. data/{lib/teapot/build/targets/application.rb → spec/teapot/build_spec.rb} +26 -29
  28. data/{test/test_teapot.rb → spec/teapot/context_spec.rb} +13 -13
  29. data/spec/teapot/dependency_spec.rb +113 -0
  30. data/spec/teapot/environment_spec.rb +91 -0
  31. data/{lib/teapot/build/graph.rb → spec/teapot/name_spec.rb} +26 -26
  32. data/{test/test_substitutions.rb → spec/teapot/substitutions_spec.rb} +36 -36
  33. data/{test → spec/teapot}/teapot.rb +1 -1
  34. data/teapot.gemspec +16 -9
  35. metadata +98 -51
  36. data/lib/teapot/build/component.rb +0 -69
  37. data/lib/teapot/build/file_list.rb +0 -67
  38. data/lib/teapot/build/linker.rb +0 -49
  39. data/lib/teapot/build/target.rb +0 -76
  40. data/lib/teapot/build/targets/compiler.rb +0 -83
  41. data/lib/teapot/build/targets/directory.rb +0 -63
  42. data/lib/teapot/build/targets/executable.rb +0 -56
  43. data/lib/teapot/build/targets/external.rb +0 -91
  44. data/lib/teapot/build/targets/files.rb +0 -82
  45. data/lib/teapot/build/targets/library.rb +0 -117
  46. data/lib/teapot/commands.rb +0 -139
  47. data/lib/teapot/controller/run.rb +0 -43
  48. data/lib/teapot/graph.rb +0 -136
  49. data/test/test_dependency.rb +0 -112
  50. data/test/test_environment.rb +0 -102
@@ -36,15 +36,15 @@ module Teapot
36
36
  end
37
37
  end
38
38
 
39
- def self.convert_to_shell(values)
40
- Hash[values.map{|key, value| [
39
+ def self.convert_to_shell(environment)
40
+ Hash[environment.values.map{|key, value| [
41
41
  key.to_s.upcase,
42
42
  shell_escape(value)
43
43
  ]}]
44
44
  end
45
45
 
46
46
  def self.dump(environment, io = STDOUT)
47
- environment.to_hash.each do |key, value|
47
+ environment.values.each do |key, value|
48
48
  io.puts "#{key}:".rjust(20).color(:magenta) + " #{value.inspect}"
49
49
  end
50
50
  end
@@ -28,7 +28,7 @@ module Teapot
28
28
  roots = []
29
29
  libraries = []
30
30
  paths = []
31
-
31
+
32
32
  # Extract include directories:
33
33
  flags.each do |option|
34
34
  if option.to_s =~ /^-L(.+)/
@@ -37,7 +37,7 @@ module Teapot
37
37
  libraries << Pathname($1)
38
38
  end
39
39
  end
40
-
40
+
41
41
  libraries.each do |name|
42
42
  archive_name = "lib#{name}.a"
43
43
 
data/lib/teapot/loader.rb CHANGED
@@ -22,13 +22,15 @@ require 'teapot/project'
22
22
  require 'teapot/target'
23
23
  require 'teapot/generator'
24
24
  require 'teapot/configuration'
25
+ require 'teapot/rule'
25
26
 
26
27
  require 'teapot/name'
27
- require 'teapot/build'
28
+
29
+ require 'build/files'
28
30
 
29
31
  module Teapot
30
- LOADER_VERSION = "0.9"
31
- MINIMUM_LOADER_VERSION = "0.8"
32
+ LOADER_VERSION = "1.0.0"
33
+ MINIMUM_LOADER_VERSION = "1.0"
32
34
 
33
35
  class IncompatibleTeapotError < StandardError
34
36
  def initialize(package, version)
@@ -47,18 +49,14 @@ module Teapot
47
49
  end
48
50
 
49
51
  class Loader
52
+ Files = ::Build::Files
53
+
50
54
  class Definitions < Array
51
55
  def default_configuration
52
56
  find{|definition| Configuration === definition}
53
57
  end
54
58
  end
55
59
 
56
- # Provides build_directory and build_external methods
57
- include Build::Helpers
58
-
59
- # Provides run_executable and other related methods.
60
- include Commands::Helpers
61
-
62
60
  def initialize(context, package)
63
61
  @context = context
64
62
  @package = package
@@ -134,9 +132,9 @@ module Teapot
134
132
  # Load a teapot.rb file relative to the root of the @package.
135
133
  def load(path)
136
134
  absolute_path = @package.path + path
137
-
135
+
138
136
  raise NonexistantTeapotError.new(absolute_path) unless File.exist?(absolute_path)
139
-
137
+
140
138
  self.instance_eval(absolute_path.read, absolute_path.to_s)
141
139
 
142
140
  if @version == nil
data/lib/teapot/name.rb CHANGED
@@ -24,6 +24,10 @@ module Teapot
24
24
  @text = text
25
25
  end
26
26
 
27
+ def self.from_target(string)
28
+ self.new(string.gsub(/(^|[ \-_])(.)/){" " + $2.upcase}.strip)
29
+ end
30
+
27
31
  attr :text
28
32
 
29
33
  def identifier
@@ -18,19 +18,20 @@
18
18
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
19
  # THE SOFTWARE.
20
20
 
21
- require 'pathname'
21
+ require 'build/files'
22
22
 
23
23
  require 'teapot/context'
24
24
  require 'teapot/environment'
25
- require 'teapot/commands'
26
25
 
27
26
  require 'teapot/definition'
28
27
 
29
28
  module Teapot
29
+ Path = Build::Files::Path
30
+
30
31
  class Package
31
32
  def initialize(path, name, options = {})
32
33
  # The path where the package is (or will be) located:
33
- @path = path
34
+ @path = Path[path]
34
35
 
35
36
  # Get the name of the package from the options, if provided:
36
37
  if options[:name]
@@ -49,7 +50,7 @@ module Teapot
49
50
 
50
51
  # Copy the options provided:
51
52
  @options = options
52
- end
53
+ end
53
54
 
54
55
  attr :name
55
56
  attr :path
@@ -18,10 +18,38 @@
18
18
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
19
  # THE SOFTWARE.
20
20
 
21
- require 'teapot/commands'
21
+ require 'rexec'
22
22
 
23
23
  module Teapot
24
24
  module Git
25
+ module Commands
26
+ class CommandError < StandardError
27
+ end
28
+
29
+ def self.run(*args, &block)
30
+ options = Hash === args.last ? args.pop : {}
31
+ options[:passthrough] ||= :all
32
+
33
+ args = args.flatten.collect &:to_s
34
+
35
+ puts args.join(' ').color(:blue) + " in #{options[:chdir] || Dir.getwd}"
36
+
37
+ task = RExec::Task.open(args, options, &block)
38
+
39
+ if task.wait == 0
40
+ true
41
+ else
42
+ raise CommandError.new("Non-zero exit status: #{args.join(' ')}!")
43
+ end
44
+ end
45
+
46
+ def self.run!(*args, &block)
47
+ run(*args, &block)
48
+ rescue CommandError
49
+ false
50
+ end
51
+ end
52
+
25
53
  class Repository
26
54
  def initialize(root, options = {})
27
55
  @root = root
@@ -0,0 +1,196 @@
1
+ # Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ module Teapot
22
+ # A rule is a function with a specific set of input and output parameters, which can match against a given set of specific inputs and outputs. For example, there might be several rules for compiling, but the specific rules depend on the language being compiled.
23
+ class Rule
24
+ class Parameter
25
+ def initialize(direction, name, options = {}, &block)
26
+ @direction = direction
27
+ @name = name
28
+
29
+ @options = options
30
+
31
+ @dynamic = block_given? ? Proc.new(&block) : nil
32
+ end
33
+
34
+ attr :direction
35
+ attr :name
36
+
37
+ attr :options
38
+
39
+ def dynamic?
40
+ @dynamic != nil
41
+ end
42
+
43
+ def implicit?
44
+ dynamic? and @options[:implicit]
45
+ end
46
+
47
+ def typed?
48
+ @options[:typed]
49
+ end
50
+
51
+ def applicable? arguments
52
+ # The parameter is either optional, or is included in the argument list, otherwise we fail.
53
+ unless @options[:optional] or arguments.include?(@name)
54
+ return false
55
+ end
56
+
57
+ value = arguments[@name]
58
+
59
+ # If the parameter is optional, and wasn't provided, we are okay.
60
+ if @options[:optional]
61
+ return true if value == nil
62
+ end
63
+
64
+ # If the parameter is typed, and we don't match the expected type, we fail.
65
+ if type = @options[:typed]
66
+ return false unless type === value
67
+ end
68
+
69
+ # If a pattern is provided, we must match it.
70
+ if pattern = @options[:pattern]
71
+ return Array(value).all? {|item| pattern.match(item)}
72
+ end
73
+
74
+ return true
75
+ end
76
+
77
+ def compute(arguments)
78
+ if implicit?
79
+ @dynamic.call(arguments)
80
+ elsif dynamic?
81
+ @dynamic.call(arguments[@name], arguments)
82
+ else
83
+ arguments[@name]
84
+ end
85
+ end
86
+
87
+ def inspect
88
+ "#{direction}:#{@name} (#{options.inspect})"
89
+ end
90
+ end
91
+
92
+ def initialize(process_name, type)
93
+ @name = process_name + "." + type
94
+ @full_name = @name.gsub(/[^\w]/, '_')
95
+
96
+ @process_name = process_name.gsub('-', '_').to_sym
97
+ @type = type
98
+
99
+ @apply = nil
100
+
101
+ @parameters = []
102
+ end
103
+
104
+ # compile.cpp
105
+ attr :name
106
+
107
+ # compile
108
+ attr :process_name
109
+
110
+ # compile_cpp
111
+ attr :full_name
112
+
113
+ attr :primary_output
114
+
115
+ def input(name, options = {}, &block)
116
+ @parameters << Parameter.new(:input, name, options, &block)
117
+ end
118
+
119
+ def parameter(name, options = {}, &block)
120
+ @parameters << Parameter.new(:argument, name, options, &block)
121
+ end
122
+
123
+ def output(name, options = {}, &block)
124
+ @parameters << Parameter.new(:output, name, options, &block)
125
+
126
+ @primary_output ||= @parameters.last
127
+ end
128
+
129
+ # Check if this rule can process these parameters
130
+ def applicable?(arguments)
131
+ @parameters.each do |parameter|
132
+ next if parameter.implicit?
133
+
134
+ return false unless parameter.applicable?(arguments)
135
+ end
136
+
137
+ return true
138
+ end
139
+
140
+ def normalize(arguments)
141
+ Hash[
142
+ @parameters.collect do |parameter|
143
+ [parameter.name, parameter.compute(arguments)]
144
+ end
145
+ ]
146
+ end
147
+
148
+ def files(arguments)
149
+ input_files = []
150
+ output_files = []
151
+
152
+ @parameters.each do |parameter|
153
+ # This could probably be improved a bit, we are assuming all parameters are file based:
154
+ value = arguments[parameter.name]
155
+
156
+ next unless value
157
+
158
+ case parameter.direction
159
+ when :input
160
+ input_files << value
161
+ when :output
162
+ output_files << value
163
+ end
164
+ end
165
+
166
+ return Build::Files::Composite.new(input_files), Build::Files::Composite.new(output_files)
167
+ end
168
+
169
+ def apply(&block)
170
+ @apply = Proc.new(&block)
171
+ end
172
+
173
+ def apply!(scope, arguments)
174
+ scope.instance_exec(arguments, &@apply) if @apply
175
+ end
176
+
177
+ def result(arguments)
178
+ if @primary_output
179
+ arguments[@primary_output.name]
180
+ end
181
+ end
182
+
183
+ def to_s
184
+ "<#{self.class.name} #{@name.dump}>"
185
+ end
186
+ end
187
+
188
+ class NoApplicableRule < StandardError
189
+ def initialize(name, arguments)
190
+ super "No applicable rule with name #{name}.* for parameters: #{arguments.inspect}"
191
+
192
+ @name = name
193
+ @arguments = arguments
194
+ end
195
+ end
196
+ end
@@ -0,0 +1,91 @@
1
+ # Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require 'teapot/rule'
22
+
23
+ module Teapot
24
+ class Rulebook
25
+ def initialize
26
+ @rules = {}
27
+ @processes = {}
28
+ end
29
+
30
+ attr :rules
31
+
32
+ def << rule
33
+ @rules[rule.name] = rule
34
+
35
+ # A cache for fast process/file-type lookup:
36
+ processes = @processes[rule.process_name] ||= []
37
+ processes << rule
38
+ end
39
+
40
+ def [] name
41
+ @rules[name]
42
+ end
43
+
44
+ def with(superclass, state = {})
45
+ task_class = Class.new(superclass)
46
+
47
+ # Define methods for all processes, e.g. task_class#compile
48
+ @processes.each do |key, rules|
49
+ # Define general rules, which use rule applicability for disambiguation:
50
+ task_class.send(:define_method, key) do |arguments, &block|
51
+ rule = rules.find{|rule| rule.applicable? arguments }
52
+
53
+ if rule
54
+ update(rule, arguments, &block)
55
+ else
56
+ raise NoApplicableRule.new(key, arguments)
57
+ end
58
+ end
59
+ end
60
+
61
+ # Define methods for all rules, e.g. task_class#compile_cpp
62
+ @rules.each do |key, rule|
63
+ task_class.send(:define_method, rule.full_name) do |arguments, &block|
64
+ update(rule, arguments, &block)
65
+ end
66
+ end
67
+
68
+ state.each do |key, value|
69
+ task_class.send(:define_method, key) do
70
+ value
71
+ end
72
+ end
73
+
74
+ return task_class
75
+ end
76
+
77
+ def self.for(environment)
78
+ rulebook = self.new
79
+
80
+ environment.defined.each do |name, define|
81
+ object = define.klass.new(*name.split('.', 2))
82
+
83
+ object.instance_eval(&define.block)
84
+
85
+ rulebook << object
86
+ end
87
+
88
+ return rulebook
89
+ end
90
+ end
91
+ end
data/lib/teapot/target.rb CHANGED
@@ -19,10 +19,11 @@
19
19
  # THE SOFTWARE.
20
20
 
21
21
  require 'pathname'
22
- require 'teapot/build'
23
22
  require 'teapot/dependency'
24
23
  require 'teapot/definition'
25
24
 
25
+ require 'teapot/rulebook'
26
+
26
27
  module Teapot
27
28
  class BuildError < StandardError
28
29
  end
@@ -32,19 +33,23 @@ module Teapot
32
33
 
33
34
  def initialize(context, package, name)
34
35
  super context, package, name
35
-
36
+
36
37
  @build = nil
38
+
39
+ @rulebook = Rulebook.new
37
40
  end
38
-
39
- def builder
40
- Build.top(@path)
41
- end
42
-
43
- def environment_for_configuration(configuration)
41
+
42
+ attr :rulebook
43
+
44
+ def provision_chain(configuration)
44
45
  # Reduce the number of keystrokes for good health:
45
46
  context = configuration.context
46
47
 
47
48
  chain = Dependency::chain(context.selection, dependencies, context.targets.values)
49
+ end
50
+
51
+ def environment_for_configuration(configuration)
52
+ chain = provision_chain(configuration)
48
53
 
49
54
  environments = []
50
55
 
@@ -63,45 +68,15 @@ module Teapot
63
68
  default platforms_path configuration.platforms_path
64
69
  default build_prefix {platforms_path + "cache/#{platform_name}-#{variant}"}
65
70
  default install_prefix {platforms_path + "#{platform_name}-#{variant}"}
66
-
67
- append buildflags {"-I#{install_prefix + "include"}"}
68
- append linkflags {"-L#{install_prefix + "lib"}"}
69
71
  end
70
72
  end
71
-
73
+
72
74
  def build(&block)
73
75
  if block_given?
74
- @build = Proc.new(&block)
76
+ @build = block
75
77
  end
76
78
 
77
79
  return @build
78
80
  end
79
-
80
- def build!(configuration)
81
- return unless @build
82
-
83
- local_environment = environment_for_configuration(configuration)
84
-
85
- @build.call(local_environment)
86
- end
87
-
88
- # Specify a default block which is used to run the configuration.
89
- def run(&block)
90
- if block_given?
91
- @run = block
92
- end
93
-
94
- return @run
95
- end
96
-
97
- def run!(configuration)
98
- return unless @run
99
-
100
- local_environment = environment_for_configuration(configuration)
101
-
102
- if @run
103
- @run.call(local_environment)
104
- end
105
- end
106
81
  end
107
82
  end
@@ -19,5 +19,5 @@
19
19
  # THE SOFTWARE.
20
20
 
21
21
  module Teapot
22
- VERSION = "0.9.10"
22
+ VERSION = "1.0.0-rc1"
23
23
  end
@@ -1,4 +1,4 @@
1
- # Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
1
+ # Copyright, 2014, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
2
  #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  # of this software and associated documentation files (the "Software"), to deal
@@ -18,38 +18,35 @@
18
18
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
19
  # THE SOFTWARE.
20
20
 
21
- require 'teapot/build/targets/library'
22
- require 'teapot/build/targets/directory'
21
+ require 'teapot/build'
22
+ require 'teapot/environment'
23
23
 
24
- module Teapot
25
- module Build
26
- module Targets
27
- class Application < Directory
28
- def initialize(parent, name, options = {})
29
- super parent
30
-
31
- @name = name
32
- @options = options
33
- end
34
-
35
- def subdirectory
36
- "Applications/#{@name}"
37
- end
38
-
39
- def << target
40
- if target.respond_to? :subdirectory
41
- target.options[:subdirectory] = subdirectory
42
- end
43
-
44
- super
45
- end
24
+ module Teapot::BuildSpec
25
+ class DummyTarget
26
+ def name
27
+ "dummy-target"
28
+ end
29
+
30
+ def build
31
+ lambda do
32
+ # This is technically incorrect, because this Top graph node specifies no outputs. But, for testing, it's fine.
33
+ fs.touch "bob"
46
34
  end
35
+ end
36
+ end
37
+
38
+ describe Teapot::Build do
39
+ let(:environment) {Teapot::Environment.hash(foo: 'bar')}
40
+
41
+ it "should create a simple build graph" do
42
+ expect(FileUtils::NoWrite).to receive(:touch).with("bob").once
43
+ expect(FileUtils::Verbose).to receive(:touch).with("bob").once
47
44
 
48
- class Directory
49
- def add_application(*args, &block)
50
- self << Application.target(self, *args, &block)
51
- end
45
+ controller = Teapot::Build::Controller.new do |controller|
46
+ controller.add_target(DummyTarget.new, environment)
52
47
  end
48
+
49
+ controller.update!
53
50
  end
54
51
  end
55
52
  end
@@ -18,25 +18,25 @@
18
18
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
19
  # THE SOFTWARE.
20
20
 
21
- require "minitest/autorun"
22
-
23
21
  require 'teapot/context'
24
22
 
25
- class TestConfig < MiniTest::Test
26
- ROOT = Pathname.new(__FILE__).dirname
23
+ module Teapot::ContextSpec
24
+ ROOT = Build::Files::Path.new(__dir__)
27
25
 
28
- def test_context
29
- context = Teapot::Context.new(ROOT)
26
+ describe Teapot::Context do
27
+ it "should load teapot.rb file" do
28
+ context = Teapot::Context.new(ROOT)
30
29
 
31
- # There is one configuration:
32
- assert_equal 1, context.configurations.count
30
+ # There is one configuration:
31
+ expect(context.configurations.count).to be == 1
33
32
 
34
- # No targets were defined:
35
- assert_equal 0, context.targets.count
33
+ # No targets were defined:
34
+ expect(context.targets.count).to be == 0
36
35
 
37
- default_configuration = context.configuration
36
+ default_configuration = context.configuration
38
37
 
39
- # 13 defined packages + 1 implicit package.
40
- assert_equal 14, default_configuration.packages.count
38
+ # 13 defined packages + 1 implicit package.
39
+ expect(default_configuration.packages.count).to be == 14
40
+ end
41
41
  end
42
42
  end