Please review pull request #504: Ticket/2.7.x/12256 module tool core changes opened by (mmrobins)
Description:
These are the changes the puppet module tool will need that potentially affect other parts of Puppet.
- Opened: Wed Feb 15 19:38:33 UTC 2012
- Based on: puppetlabs:2.7.x (fef777b60ffca37cd3a41dd642c6b0838e32b492)
- Requested merge: mmrobins:ticket/2.7.x/12256-module_tool_core_changes (360d4b7751532aa92d18735a765a4d417a2a637a)
Diff follows:
diff --git a/lib/puppet/module.rb b/lib/puppet/module.rb
index 4282fc6..b68cd87 100644
--- a/lib/puppet/module.rb
+++ b/lib/puppet/module.rb
@@ -1,5 +1,6 @@
require 'puppet/util/logging'
require 'semver'
+require 'puppet/module_tool/applications'
# Support for modules
class Puppet::Module
@@ -31,7 +32,7 @@ def self.find(modname, environment = nil)
attr_reader :name, :environment
attr_writer :environment
- attr_accessor :dependencies
+ attr_accessor :dependencies, :forge_name
attr_accessor :source, :author, :version, :license, :puppetversion, :summary, :description, :project_page
def has_metadata?
@@ -40,6 +41,8 @@ def has_metadata?
return false unless FileTest.exist?(metadata_file)
metadata = PSON.parse File.read(metadata_file)
+
+
return metadata.is_a?(Hash) && !metadata.keys.empty?
end
@@ -108,6 +111,8 @@ def license_file
def load_metadata
data = "" File.read(metadata_file)
+ @forge_name = data['name'].gsub('-', '/') if data['name']
+
[:source, :author, :version, :license, :puppetversion, :dependencies].each do |attr|
unless value = data[attr.to_s]
unless attr == :puppetversion
@@ -155,6 +160,26 @@ def to_s
result
end
+ def dependencies_as_modules
+ dependent_modules = []
+ dependencies and dependencies.each do |dep|
+ author, dep_name = dep["name"].split('/')
+ found_module = environment.module(dep_name)
+ dependent_modules << found_module if found_module
+ end
+
+ dependent_modules
+ end
+
+ def required_by
+ environment.module_requirements[self.forge_name] || {}
+ end
+
+ def has_local_changes?
+ changes = Puppet::Module::Tool::Applications::Checksummer.run(path)
+ !changes.empty?
+ end
+
def unmet_dependencies
return [] unless dependencies
diff --git a/lib/puppet/module_tool/applications.rb b/lib/puppet/module_tool/applications.rb
index 24bcbe2..d5eb7f5 100644
--- a/lib/puppet/module_tool/applications.rb
+++ b/lib/puppet/module_tool/applications.rb
@@ -1,13 +1,17 @@
-module Puppet::Module::Tool
- module Applications
- require 'puppet/module_tool/applications/application'
- require 'puppet/module_tool/applications/builder'
- require 'puppet/module_tool/applications/checksummer'
- require 'puppet/module_tool/applications/cleaner'
- require 'puppet/module_tool/applications/generator'
- require 'puppet/module_tool/applications/installer'
- require 'puppet/module_tool/applications/searcher'
- require 'puppet/module_tool/applications/unpacker'
- require 'puppet/module_tool/applications/uninstaller'
+require 'puppet/module'
+
+class Puppet::Module
+ module Tool
+ module Applications
+ require 'puppet/module_tool/applications/application'
+ require 'puppet/module_tool/applications/builder'
+ require 'puppet/module_tool/applications/checksummer'
+ require 'puppet/module_tool/applications/cleaner'
+ require 'puppet/module_tool/applications/generator'
+ require 'puppet/module_tool/applications/installer'
+ require 'puppet/module_tool/applications/searcher'
+ require 'puppet/module_tool/applications/unpacker'
+ require 'puppet/module_tool/applications/uninstaller'
+ end
end
end
diff --git a/lib/puppet/module_tool/applications/application.rb b/lib/puppet/module_tool/applications/application.rb
index 43d5c04..fd398da 100644
--- a/lib/puppet/module_tool/applications/application.rb
+++ b/lib/puppet/module_tool/applications/application.rb
@@ -1,10 +1,11 @@
require 'net/http'
require 'semver'
+require 'puppet/module_tool/utils/interrogation'
module Puppet::Module::Tool
module Applications
class Application
- include Utils::Interrogation
+ include Puppet::Module::Tool::Utils::Interrogation
def self.run(*args)
new(*args).run
diff --git a/lib/puppet/node/environment.rb b/lib/puppet/node/environment.rb
index 06be16d..2a64f01 100644
--- a/lib/puppet/node/environment.rb
+++ b/lib/puppet/node/environment.rb
@@ -93,6 +93,14 @@ def module(name)
mod
end
+ def module_by_forge_name(forge_name)
+ author, modname = forge_name.split('/')
+ found_mod = self.module(modname)
+ found_mod and found_mod.forge_name == forge_name ?
+ found_mod :
+ nil
+ end
+
# Cache the modulepath, so that we aren't searching through
# all known directories all the time.
cached_attr(:modulepath, Puppet[:filetimeout]) do
@@ -128,6 +136,24 @@ def modules_by_path
modules_by_path
end
+ def module_requirements
+ deps = {}
+ modules.each do |mod|
+ next unless mod.forge_name
+ deps[mod.forge_name] ||= []
+ mod.dependencies and mod.dependencies.sort_by {|mod_dep| mod_dep['name']}.each do |mod_dep|
+ deps[mod_dep['name']] ||= []
+ dep_details = {
+ 'name' => mod.forge_name,
+ 'version' => mod.version,
+ 'version_requirement' => mod_dep['version_requirement']
+ }
+ deps[mod_dep['name']] << dep_details
+ end
+ end
+ deps
+ end
+
def to_s
name.to_s
end
diff --git a/lib/puppet/util/monkey_patches.rb b/lib/puppet/util/monkey_patches.rb
index f4dd0cf..14cb4a7 100644
--- a/lib/puppet/util/monkey_patches.rb
+++ b/lib/puppet/util/monkey_patches.rb
@@ -145,6 +145,24 @@ def self.binwrite(name, string, offset = 0)
end
+class Range
+ def intersection(other)
+ raise ArgumentError, 'value must be a Range' unless other.kind_of?(Range)
+ return unless other === self.first || self === other.first
+
+ start = [self.first, other.first].max
+ if self.exclude_end? && self.last <= other.last
+ start ... self.last
+ elsif other.exclude_end? && self.last >= other.last
+ start ... other.last
+ else
+ start .. [ self.last, other.last ].min
+ end
+ end
+
+ alias_method :&, :intersection
+end
+
# Ruby 1.8.5 doesn't have tap
module Kernel
def tap
diff --git a/lib/semver.rb b/lib/semver.rb
index ef9435a..5029d96 100644
--- a/lib/semver.rb
+++ b/lib/semver.rb
@@ -1,9 +1,14 @@
-class SemVer
- VERSION = /^v?(\d+)\.(\d+)\.(\d+)([A-Za-z][0-9A-Za-z-]*|)$/
- SIMPLE_RANGE = /^v?(\d+|[xX])(?:\.(\d+|[xX])(?:\.(\d+|[xX]))?)?$/
+require 'puppet/util/monkey_patches'
+# We need to subclass Numeric to force range comparisons not to try to iterate over SemVer
+# and instead use numeric comparisons (eg >, <, >=, <=)
+# Ruby 1.8 already did this for all ranges, but Ruby 1.9 changed range include behavior
+class SemVer < Numeric
include Comparable
+ VERSION = /^v?(\d+)\.(\d+)\.(\d+)(-[0-9A-Za-z-]*|)$/
+ SIMPLE_RANGE = /^v?(\d+|[xX])(?:\.(\d+|[xX])(?:\.(\d+|[xX]))?)?$/
+
def self.valid?(ver)
VERSION =~ ver
end
@@ -12,6 +17,45 @@ def self.find_matching(pattern, versions)
versions.select { |v| v.matched_by?("#{pattern}") }.sort.last
end
+ def self.[](range)
+ range.gsub(/([><=])\s+/, '\1').split(/\b\s+(?!-)/).map do |r|
+ case r
+ when SemVer::VERSION
+ SemVer.new(r + '-') .. SemVer.new(r)
+ when SemVer::SIMPLE_RANGE
+ r += ".0" unless SemVer.valid?(r.gsub(/x/i, '0'))
+ SemVer.new(r.gsub(/x/i, '0'))...SemVer.new(r.gsub(/(\d+)\.x/i) { "#{$1.to_i + 1}.0" } + '-')
+ when /\s+-\s+/
+ a, b = r.split(/\s+-\s+/)
+ SemVer.new(a + '-') .. SemVer.new(b)
+ when /^~/
+ ver = r.sub(/~/, '').split('.').map(&:to_i)
+ start = (ver + [0] * (3 - ver.length)).join('.')
+
+ ver.pop unless ver.length == 1
+ ver[-1] = ver.last + 1
+
+ finish = (ver + [0] * (3 - ver.length)).join('.')
+ SemVer.new(start + '-') ... SemVer.new(finish + '-')
+ when /^>=/
+ ver = r.sub(/^>=/, '')
+ SemVer.new(ver + '-') .. SemVer::MAX
+ when /^<=/
+ ver = r.sub(/^<=/, '')
+ SemVer::MIN .. SemVer.new(ver)
+ when /^>/
+ ver = r.sub(/^>/, '').split('.').map(&:to_i)
+ ver[2] = ver.last + 1
+ SemVer.new(ver.join('.') + '-') .. SemVer::MAX
+ when /^</
+ ver = r.sub(/^</, '')
+ SemVer::MIN ... SemVer.new(ver + '-')
+ else
+ (1..1)
+ end
+ end.inject { |a,e| a & e }
+ end
+
attr_reader :major, :minor, :tiny, :special
def initialize(ver)
@@ -59,7 +103,14 @@ def matched_by?(pattern)
end
def inspect
- "v#{@major}.#{@minor}.#{@tiny}#{@special}"
+ @vstring || "v#{@major}.#{@minor}.#{@tiny}#{@special}"
end
alias :to_s :inspect
+
+ MIN = SemVer.new('0.0.0-')
+ MIN.instance_variable_set(:@vstring, 'vMIN')
+
+ MAX = SemVer.new('8.0.0')
+ MAX.instance_variable_set(:@major, (1.0/0)) # => Infinity
+ MAX.instance_variable_set(:@vstring, 'vMAX')
end
diff --git a/spec/lib/puppet_spec/modules.rb b/spec/lib/puppet_spec/modules.rb
new file mode 100644
index 0000000..156e466
--- /dev/null
+++ b/spec/lib/puppet_spec/modules.rb
@@ -0,0 +1,26 @@
+module PuppetSpec::Modules
+ class << self
+ def create(name, dir, options = {})
+ module_dir = File.join(dir, name)
+ FileUtils.mkdir_p(module_dir)
+
+ environment = Puppet::Node::Environment.new(options[:environment])
+
+ if metadata = options[:metadata]
+ metadata[:source] ||= 'github'
+ metadata[:author] ||= 'puppetlabs'
+ metadata[:version] ||= '9.9.9'
+ metadata[:license] ||= 'to kill'
+ metadata[:dependencies] ||= []
+
+ metadata[:name] = "#{metadata[:author]}/#{name}"
+
+ File.open(File.join(module_dir, 'metadata.json'), 'w') do |f|
+ f.write(metadata.to_pson)
+ end
+ end
+
+ Puppet::Module.new(name, :environment => environment, :path => module_dir)
+ end
+ end
+end
diff --git a/spec/unit/module_spec.rb b/spec/unit/module_spec.rb
index 695f648..657ca40 100755
--- a/spec/unit/module_spec.rb
+++ b/spec/unit/module_spec.rb
@@ -1,6 +1,8 @@
#!/usr/bin/env rspec
require 'spec_helper'
require 'puppet_spec/files'
+require 'puppet_spec/modules'
+require 'puppet/module_tool/checksums'
describe Puppet::Module do
include PuppetSpec::Files
@@ -532,13 +534,14 @@
end
describe Puppet::Module do
+ include PuppetSpec::Files
before do
- Puppet::Module.any_instance.stubs(:path).returns "/my/mod/path"
- @module = Puppet::Module.new("foo")
+ @modpath = tmpdir('modpath')
+ @module = PuppetSpec::Modules.create('mymod', @modpath)
end
it "should use 'License' in its current path as its metadata file" do
- @module.license_file.should == "/my/mod/path/License"
+ @module.license_file.should == "#{@modpath}/mymod/License"
end
it "should return nil as its license file when the module has no path" do
@@ -547,13 +550,13 @@
end
it "should cache the license file" do
- Puppet::Module.any_instance.expects(:path).once.returns nil
- mod = Puppet::Module.new("foo")
- mod.license_file.should == mod.license_file
+ @module.expects(:path).once.returns nil
+ @module.license_file
+ @module.license_file
end
it "should use 'metadata.json' in its current path as its metadata file" do
- @module.metadata_file.should == "/my/mod/path/metadata.json"
+ @module.metadata_file.should == "#{@modpath}/mymod/metadata.json"
end
it "should return nil as its metadata file when the module has no path" do
@@ -657,4 +660,72 @@
it "should fail if the discovered name is different than the metadata name"
end
+
+ it "should be able to tell if there are local changes" do
+ modpath = tmpdir('modpath')
+ foo_checksum = 'acbd18db4cc2f85cedef654fccc4a4d8'
+ checksummed_module = PuppetSpec::Modules.create(
+ 'changed',
+ modpath,
+ :metadata => {
+ :checksums => {
+ "foo" => foo_checksum,
+ }
+ }
+ )
+
+ foo_path = Pathname.new(File.join(checksummed_module.path, 'foo'))
+
+ IO.binwrite(foo_path, 'notfoo')
+ Puppet::Module::Tool::Checksums.new(foo_path).checksum(foo_path).should_not == foo_checksum
+ checksummed_module.has_local_changes?.should be_true
+
+ IO.binwrite(foo_path, 'foo')
+
+ Puppet::Module::Tool::Checksums.new(foo_path).checksum(foo_path).should == foo_checksum
+ checksummed_module.has_local_changes?.should be_false
+ end
+
+ it "should know what other modules require it" do
+ Puppet.settings[:modulepath] = @modpath
+ dependable = PuppetSpec::Modules.create(
+ 'dependable',
+ @modpath,
+ :metadata => {:author => 'puppetlabs'}
+ )
+ PuppetSpec::Modules.create(
+ 'needy',
+ @modpath,
+ :metadata => {
+ :author => 'beggar',
+ :dependencies => [{
+ "version_requirement" => ">= 2.2.0",
+ "name" => "puppetlabs/dependable"
+ }]
+ }
+ )
+ PuppetSpec::Modules.create(
+ 'wantit',
+ @modpath,
+ :metadata => {
+ :author => 'spoiled',
+ :dependencies => [{
+ "version_requirement" => "< 5.0.0",
+ "name" => "puppetlabs/dependable"
+ }]
+ }
+ )
+ dependable.required_by.should =~ [
+ {
+ "name" => "beggar/needy",
+ "version" => "9.9.9",
+ "version_requirement" => ">= 2.2.0"
+ },
+ {
+ "name" => "spoiled/wantit",
+ "version" => "9.9.9",
+ "version_requirement" => "< 5.0.0"
+ }
+ ]
+ end
end
diff --git a/spec/unit/module_tool/application_spec.rb b/spec/unit/module_tool/application_spec.rb
index 22d3632..b86ec5c 100644
--- a/spec/unit/module_tool/application_spec.rb
+++ b/spec/unit/module_tool/application_spec.rb
@@ -4,9 +4,9 @@
describe Puppet::Module::Tool::Applications::Application do
describe 'app' do
- good_versions = %w{ 1.2.4 0.0.1 0.0.0 0.0.2git-8-g3d316d1 0.0.3b1 10.100.10000
- 0.1.2rc1 0.1.2dev-1 0.1.2svn12345 }
- bad_versions = %w{ 0.1.2-3 0.1 0 0.1.2.3 dev }
+ good_versions = %w{ 1.2.4 0.0.1 0.0.0 0.0.2-git-8-g3d316d1 0.0.3-b1 10.100.10000
+ 0.1.2-rc1 0.1.2-dev-1 0.1.2-svn12345 0.1.2-3 }
+ bad_versions = %w{ 0.1 0 0.1.2.3 dev 0.1.2beta }
before do
@app = Class.new(described_class).new
diff --git a/spec/unit/node/environment_spec.rb b/spec/unit/node/environment_spec.rb
index d5d3068..4adc5a6 100755
--- a/spec/unit/node/environment_spec.rb
+++ b/spec/unit/node/environment_spec.rb
@@ -5,6 +5,7 @@
require 'puppet/node/environment'
require 'puppet/util/execution'
+require 'puppet_spec/modules'
describe Puppet::Node::Environment do
let(:env) { Puppet::Node::Environment.new("testing") }
@@ -124,8 +125,10 @@
describe "when validating modulepath or manifestdir directories" do
before :each do
- @path_one = make_absolute('/one')
- @path_two = make_absolute('/two')
+ @path_one = tmpdir("path_one")
+ @path_two = tmpdir("path_one")
+ sep = File::PATH_SEPARATOR
+ Puppet[:modulepath] = "#{@path_one}#{sep}#{@path_two}"
end
it "should not return non-directories" do
@@ -167,15 +170,13 @@
end
it "should return nil if asked for a module that does not exist in its path" do
-
- mod = mock 'module'
- Puppet::Module.expects(:new).with("one", :environment => env).returns mod
- mod.expects(:exist?).returns false
+ modpath = tmpdir('modpath')
+ env.modulepath = [modpath]
env.module("one").should be_nil
end
- describe ".modules_by_path" do
+ describe "module data" do
before do
dir = tmpdir("deep_path")
@@ -187,75 +188,153 @@
FileUtils.mkdir_p(@second)
end
- it "should return an empty list if there are no modules" do
- env.modules_by_path.should == {
- @first => [],
- @second => []
- }
- end
-
- it "should include modules even if they exist in multiple dirs in the modulepath" do
- modpath1 = File.join(@first, "foo")
- FileUtils.mkdir_p(modpath1)
- modpath2 = File.join(@second, "foo")
- FileUtils.mkdir_p(modpath2)
-
- env.modules_by_path.should == {
- @first => [Puppet::Module.new('foo', :environment => env, :path => modpath1)],
- @second => [Puppet::Module.new('foo', :environment => env, :path => modpath2)]
- }
- end
- end
-
- describe ".modules" do
- it "should return an empty list if there are no modules" do
- env.modulepath = %w{/a /b}
- Dir.expects(:entries).with("/a").returns []
- Dir.expects(:entries).with("/b").returns []
-
- env.modules.should == []
- end
-
- it "should return a module named for every directory in each module path" do
- env.modulepath = %w{/a /b}
- Dir.expects(:entries).with("/a").returns %w{foo bar}
- Dir.expects(:entries).with("/b").returns %w{bee baz}
-
- env.modules.collect{|mod| mod.name}.sort.should == %w{foo bar bee baz}.sort
+ describe "#modules_by_path" do
+ it "should return an empty list if there are no modules" do
+ env.modules_by_path.should == {
+ @first => [],
+ @second => []
+ }
+ end
+
+ it "should include modules even if they exist in multiple dirs in the modulepath" do
+ modpath1 = File.join(@first, "foo")
+ FileUtils.mkdir_p(modpath1)
+ modpath2 = File.join(@second, "foo")
+ FileUtils.mkdir_p(modpath2)
+
+ env.modules_by_path.should == {
+ @first => [Puppet::Module.new('foo', :environment => env, :path => modpath1)],
+ @second => [Puppet::Module.new('foo', :environment => env, :path => modpath2)]
+ }
+ end
end
- it "should remove duplicates" do
- env.modulepath = %w{/a /b}
- Dir.expects(:entries).with("/a").returns %w{foo}
- Dir.expects(:entries).with("/b").returns %w{foo}
-
- env.modules.collect{|mod| mod.name}.sort.should == %w{foo}
+ describe "#module_requirements" do
+ it "should return a list of what modules depend on other modules" do
+ PuppetSpec::Modules.create(
+ 'foo',
+ @first,
+ :metadata => {
+ :author => 'puppetlabs',
+ :dependencies => [{ 'name' => 'puppetlabs/bar', "version_requirement" => ">= 1.0.0" }]
+ }
+ )
+ PuppetSpec::Modules.create(
+ 'bar',
+ @second,
+ :metadata => {
+ :author => 'puppetlabs',
+ :dependencies => [{ 'name' => 'puppetlabs/foo', "version_requirement" => "<= 2.0.0" }]
+ }
+ )
+ PuppetSpec::Modules.create(
+ 'baz',
+ @first,
+ :metadata => {
+ :author => 'puppetlabs',
+ :dependencies => [{ 'name' => 'puppetlabs/bar', "version_requirement" => "3.0.0" }]
+ }
+ )
+
+ env.module_requirements.should == {
+ 'puppetlabs/foo' => [
+ {
+ "name" => "puppetlabs/bar",
+ "version" => "9.9.9",
+ "version_requirement" => "<= 2.0.0"
+ }
+ ],
+ 'puppetlabs/bar' => [
+ {
+ "name" => "puppetlabs/baz",
+ "version" => "9.9.9",
+ "version_requirement" => "3.0.0"
+ },
+ {
+ "name" => "puppetlabs/foo",
+ "version" => "9.9.9",
+ "version_requirement" => ">= 1.0.0"
+ }
+ ],
+ 'puppetlabs/baz' => []
+ }
+ end
end
- it "should ignore invalid modules" do
- env.modulepath = %w{/a}
- Dir.expects(:entries).with("/a").returns %w{foo bar}
-
- Puppet::Module.expects(:new).with { |name, env| name == "foo" }.returns mock("foomod", :name => "foo")
- Puppet::Module.expects(:new).with { |name, env| name == "bar" }.raises( Puppet::Module::InvalidName, "name is invalid" )
-
- env.modules.collect{|mod| mod.name}.sort.should == %w{foo}
+ describe ".module_by_forge_name" do
+ it "should find modules by forge_name" do
+ mod = PuppetSpec::Modules.create(
+ 'baz',
+ @first,
+ :metadata => {:author => 'puppetlabs'},
+ :environment => env
+ )
+ env.module_by_forge_name('puppetlabs/baz').should == mod
+ end
+
+ it "should not find modules with same name by the wrong author" do
+ mod = PuppetSpec::Modules.create(
+ 'baz',
+ @first,
+ :metadata => {:author => 'sneakylabs'},
+ :environment => env
+ )
+ env.module_by_forge_name('puppetlabs/baz').should == nil
+ end
+
+ it "should return nil when the module can't be found" do
+ env.module_by_forge_name('ima/nothere').should be_nil
+ end
end
- it "should create modules with the correct environment" do
- env.modulepath = %w{/a}
- Dir.expects(:entries).with("/a").returns %w{foo}
+ describe ".modules" do
+ it "should return an empty list if there are no modules" do
+ env.modules.should == []
+ end
+
+ it "should return a module named for every directory in each module path" do
+ %w{foo bar}.each do |mod_name|
+ FileUtils.mkdir_p(File.join(@first, mod_name))
+ end
+ %w{bee baz}.each do |mod_name|
+ FileUtils.mkdir_p(File.join(@second, mod_name))
+ end
+ env.modules.collect{|mod| mod.name}.sort.should == %w{foo bar bee baz}.sort
+ end
+
+ it "should remove duplicates" do
+ FileUtils.mkdir_p(File.join(@first, 'foo'))
+ FileUtils.mkdir_p(File.join(@second, 'foo'))
+
+ env.modules.collect{|mod| mod.name}.sort.should == %w{foo}
+ end
+
+ it "should ignore modules with invalid names" do
+ FileUtils.mkdir_p(File.join(@first, 'foo'))
+ FileUtils.mkdir_p(File.join(@first, 'foo2'))
+ FileUtils.mkdir_p(File.join(@first, 'foo-bar'))
+ FileUtils.mkdir_p(File.join(@first, 'foo_bar'))
+ FileUtils.mkdir_p(File.join(@first, 'foo*bar'))
+ FileUtils.mkdir_p(File.join(@first, 'foo bar'))
+
+ env.modules.collect{|mod| mod.name}.sort.should == %w{foo foo-bar foo2 foo_bar}
+ end
+
+ it "should create modules with the correct environment" do
+ FileUtils.mkdir_p(File.join(@first, 'foo'))
+
+ env.modules.each {|mod| mod.environment.should == env }
+ end
- env.modules.each {|mod| mod.environment.should == env }
end
+ end
- it "should cache the module list" do
- env.modulepath = %w{/a}
- Dir.expects(:entries).once.with("/a").returns %w{foo}
+ it "should cache the module list" do
+ env.modulepath = %w{/a}
+ Dir.expects(:entries).once.with("/a").returns %w{foo}
- env.modules
- env.modules
- end
+ env.modules
+ env.modules
end
end
diff --git a/spec/unit/semver_spec.rb b/spec/unit/semver_spec.rb
index 0e0457b..87fc968 100644
--- a/spec/unit/semver_spec.rb
+++ b/spec/unit/semver_spec.rb
@@ -10,13 +10,13 @@
end
it 'should validate special version strings' do
- %w[ 0.0.0foo 999.999.999bar v0.0.0a v999.999.999beta ].each do |vstring|
+ %w[ 0.0.0-foo 999.999.999-bar v0.0.0-a v999.999.999-beta ].each do |vstring|
SemVer.valid?(vstring).should be_true
end
end
it 'should fail to validate invalid version strings' do
- %w[ nope 0.0foo 999.999 x0.0.0 z.z.z 1.2.3-beta 1.x.y ].each do |vstring|
+ %w[ nope 0.0foo 999.999 x0.0.0 z.z.z 1.2.3beta 1.x.y ].each do |vstring|
SemVer.valid?(vstring).should be_false
end
end
@@ -27,8 +27,8 @@
@versions = %w[
0.0.1
0.0.2
- 1.0.0rc1
- 1.0.0rc2
+ 1.0.0-rc1
+ 1.0.0-rc2
1.0.0
1.0.1
1.1.0
@@ -38,7 +38,7 @@
1.1.4
1.2.0
1.2.1
- 2.0.0rc1
+ 2.0.0-rc1
].map { |v| SemVer.new(v) }
end
@@ -49,7 +49,7 @@
end
it 'should return nil if no versions match' do
- %w[ 3.0.0 2.0.0rc2 1.0.0alpha ].each do |v|
+ %w[ 3.0.0 2.0.0-rc2 1.0.0-alpha ].each do |v|
SemVer.find_matching(v, @versions).should be_nil
end
end
@@ -58,7 +58,7 @@
SemVer.find_matching('1.0', @versions).should == 'v1.0.1'
SemVer.find_matching('1.1', @versions).should == 'v1.1.4'
SemVer.find_matching('1', @versions).should == 'v1.2.1'
- SemVer.find_matching('2', @versions).should == 'v2.0.0rc1'
+ SemVer.find_matching('2', @versions).should == 'v2.0.0-rc1'
SemVer.find_matching('2.1', @versions).should == nil
end
@@ -68,12 +68,105 @@
SemVer.find_matching('1.1.x', @versions).should == 'v1.1.4'
SemVer.find_matching('1.x', @versions).should == 'v1.2.1'
SemVer.find_matching('1.x.x', @versions).should == 'v1.2.1'
- SemVer.find_matching('2.x', @versions).should == 'v2.0.0rc1'
- SemVer.find_matching('2.x.x', @versions).should == 'v2.0.0rc1'
+ SemVer.find_matching('2.x', @versions).should == 'v2.0.0-rc1'
+ SemVer.find_matching('2.x.x', @versions).should == 'v2.0.0-rc1'
SemVer.find_matching('2.1.x', @versions).should == nil
end
end
+ describe '::[]' do
+ it "should produce expected ranges" do
+ tests = {
+ '1.2.3' => SemVer.new('v1.2.3-') .. SemVer.new('v1.2.3'),
+ '>1.2.3' => SemVer.new('v1.2.4-') .. SemVer::MAX,
+ '<1.2.3' => SemVer::MIN ... SemVer.new('v1.2.3-'),
+ '>=1.2.3' => SemVer.new('v1.2.3-') .. SemVer::MAX,
+ '<=1.2.3' => SemVer::MIN .. SemVer.new('v1.2.3'),
+ '>1.2.3 <1.2.5' => SemVer.new('v1.2.4-') ... SemVer.new('v1.2.5-'),
+ '>=1.2.3 <=1.2.5' => SemVer.new('v1.2.3-') .. SemVer.new('v1.2.5'),
+ '1.2.3 - 2.3.4' => SemVer.new('v1.2.3-') .. SemVer.new('v2.3.4'),
+ '~1.2.3' => SemVer.new('v1.2.3-') ... SemVer.new('v1.3.0-'),
+ '~1.2' => SemVer.new('v1.2.0-') ... SemVer.new('v2.0.0-'),
+ '~1' => SemVer.new('v1.0.0-') ... SemVer.new('v2.0.0-'),
+ '1.2.x' => SemVer.new('v1.2.0') ... SemVer.new('v1.3.0-'),
+ '1.x' => SemVer.new('v1.0.0') ... SemVer.new('v2.0.0-'),
+ }
+
+ tests.each do |vstring, expected|
+ SemVer[vstring].should == expected
+ end
+ end
+
+ it "should suit up" do
+ suitability = {
+ [ '1.2.3', 'v1.2.2' ] => false,
+ [ '>=1.2.3', 'v1.2.2' ] => false,
+ [ '<=1.2.3', 'v1.2.2' ] => true,
+ [ '>= 1.2.3', 'v1.2.2' ] => false,
+ [ '<= 1.2.3', 'v1.2.2' ] => true,
+ [ '1.2.3 - 1.2.4', 'v1.2.2' ] => false,
+ [ '~1.2.3', 'v1.2.2' ] => false,
+ [ '~1.2', 'v1.2.2' ] => true,
+ [ '~1', 'v1.2.2' ] => true,
+ [ '1.2.x', 'v1.2.2' ] => true,
+ [ '1.x', 'v1.2.2' ] => true,
+
+ [ '1.2.3', 'v1.2.3-alpha' ] => true,
+ [ '>=1.2.3', 'v1.2.3-alpha' ] => true,
+ [ '<=1.2.3', 'v1.2.3-alpha' ] => true,
+ [ '>= 1.2.3', 'v1.2.3-alpha' ] => true,
+ [ '<= 1.2.3', 'v1.2.3-alpha' ] => true,
+ [ '>1.2.3', 'v1.2.3-alpha' ] => false,
+ [ '<1.2.3', 'v1.2.3-alpha' ] => false,
+ [ '> 1.2.3', 'v1.2.3-alpha' ] => false,
+ [ '< 1.2.3', 'v1.2.3-alpha' ] => false,
+ [ '1.2.3 - 1.2.4', 'v1.2.3-alpha' ] => true,
+ [ '1.2.3 - 1.2.4', 'v1.2.4-alpha' ] => true,
+ [ '1.2.3 - 1.2.4', 'v1.2.5-alpha' ] => false,
+ [ '~1.2.3', 'v1.2.3-alpha' ] => true,
+ [ '~1.2.3', 'v1.3.0-alpha' ] => false,
+ [ '~1.2', 'v1.2.3-alpha' ] => true,
+ [ '~1.2', 'v2.0.0-alpha' ] => false,
+ [ '~1', 'v1.2.3-alpha' ] => true,
+ [ '~1', 'v2.0.0-alpha' ] => false,
+ [ '1.2.x', 'v1.2.3-alpha' ] => true,
+ [ '1.2.x', 'v1.3.0-alpha' ] => false,
+ [ '1.x', 'v1.2.3-alpha' ] => true,
+ [ '1.x', 'v2.0.0-alpha' ] => false,
+
+ [ '1.2.3', 'v1.2.3' ] => true,
+ [ '>=1.2.3', 'v1.2.3' ] => true,
+ [ '<=1.2.3', 'v1.2.3' ] => true,
+ [ '>= 1.2.3', 'v1.2.3' ] => true,
+ [ '<= 1.2.3', 'v1.2.3' ] => true,
+ [ '1.2.3 - 1.2.4', 'v1.2.3' ] => true,
+ [ '~1.2.3', 'v1.2.3' ] => true,
+ [ '~1.2', 'v1.2.3' ] => true,
+ [ '~1', 'v1.2.3' ] => true,
+ [ '1.2.x', 'v1.2.3' ] => true,
+ [ '1.x', 'v1.2.3' ] => true,
+
+ [ '1.2.3', 'v1.2.4' ] => false,
+ [ '>=1.2.3', 'v1.2.4' ] => true,
+ [ '<=1.2.3', 'v1.2.4' ] => false,
+ [ '>= 1.2.3', 'v1.2.4' ] => true,
+ [ '<= 1.2.3', 'v1.2.4' ] => false,
+ [ '1.2.3 - 1.2.4', 'v1.2.4' ] => true,
+ [ '~1.2.3', 'v1.2.4' ] => true,
+ [ '~1.2', 'v1.2.4' ] => true,
+ [ '~1', 'v1.2.4' ] => true,
+ [ '1.2.x', 'v1.2.4' ] => true,
+ [ '1.x', 'v1.2.4' ] => true,
+ }
+
+ suitability.each do |arguments, expected|
+ range, vstring = arguments
+ actual = SemVer[range] === SemVer.new(vstring)
+ actual.should == expected
+ end
+ end
+ end
+
describe 'instantiation' do
it 'should raise an exception when passed an invalid version string' do
expect { SemVer.new('invalidVersion') }.to raise_exception ArgumentError
@@ -88,25 +181,25 @@
end
it 'should populate the appropriate fields for a special version string' do
- version = SemVer.new('3.4.5beta6')
+ version = SemVer.new('3.4.5-beta6')
version.major.should == 3
version.minor.should == 4
version.tiny.should == 5
- version.special.should == 'beta6'
+ version.special.should == '-beta6'
end
end
describe '#matched_by?' do
- subject { SemVer.new('v1.2.3beta') }
+ subject { SemVer.new('v1.2.3-beta') }
describe 'should match against' do
describe 'literal version strings' do
- it { should be_matched_by('1.2.3beta') }
+ it { should be_matched_by('1.2.3-beta') }
- it { should_not be_matched_by('1.2.3alpha') }
- it { should_not be_matched_by('1.2.4beta') }
- it { should_not be_matched_by('1.3.3beta') }
- it { should_not be_matched_by('2.2.3beta') }
+ it { should_not be_matched_by('1.2.3-alpha') }
+ it { should_not be_matched_by('1.2.4-beta') }
+ it { should_not be_matched_by('1.3.3-beta') }
+ it { should_not be_matched_by('2.2.3-beta') }
end
describe 'partial version strings' do
@@ -155,14 +248,14 @@
it { should < SemVer.new('1.2.4') }
# Against special versions
- it { should > SemVer.new('1.2.3beta') }
- it { should < SemVer.new('1.2.4beta') }
+ it { should > SemVer.new('1.2.3-beta') }
+ it { should < SemVer.new('1.2.4-beta') }
end
- describe 'on a special version (v1.2.3beta)' do
- subject { SemVer.new('v1.2.3beta') }
+ describe 'on a special version (v1.2.3-beta)' do
+ subject { SemVer.new('v1.2.3-beta') }
- it { should == SemVer.new('1.2.3beta') }
+ it { should == SemVer.new('1.2.3-beta') }
# Same version, final release
it { should < SemVer.new('1.2.3') }
@@ -180,8 +273,8 @@
it { should < SemVer.new('1.2.4') }
# Against special versions
- it { should > SemVer.new('1.2.3alpha') }
- it { should < SemVer.new('1.2.3beta2') }
+ it { should > SemVer.new('1.2.3-alpha') }
+ it { should < SemVer.new('1.2.3-beta2') }
end
end
end
diff --git a/spec/unit/util/monkey_patches_spec.rb b/spec/unit/util/monkey_patches_spec.rb
index f3a94d1..5e4e073 100755
--- a/spec/unit/util/monkey_patches_spec.rb
+++ b/spec/unit/util/monkey_patches_spec.rb
@@ -95,3 +95,80 @@ class Puppet::TestYamlNonInitializeClass
end
end
end
+
+describe Range do
+ def do_test( range, other, expected )
+ result = range.intersection(other)
+ result.should == expected
+ end
+
+ it "should return expected ranges for iterable things" do
+ iterable_tests = {
+ 1 .. 4 => nil, # before
+ 11 .. 15 => nil, # after
+ 1 .. 6 => 5 .. 6, # overlap_begin
+ 9 .. 15 => 9 .. 10, # overlap_end
+ 1 .. 5 => 5 .. 5, # overlap_begin_edge
+ 10 .. 15 => 10 .. 10, # overlap_end_edge
+ 5 .. 10 => 5 .. 10, # overlap_all
+ 6 .. 9 => 6 .. 9, # overlap_inner
+
+ 1 ... 5 => nil, # before (exclusive range)
+ 1 ... 7 => 5 ... 7, # overlap_begin (exclusive range)
+ 1 ... 6 => 5 ... 6, # overlap_begin_edge (exclusive range)
+ 5 ... 11 => 5 .. 10, # overlap_all (exclusive range)
+ 6 ... 10 => 6 ... 10, # overlap_inner (exclusive range)
+ }
+
+ iterable_tests.each do |other, expected|
+ do_test( 5..10, other, expected )
+ do_test( other, 5..10, expected )
+ end
+ end
+
+ it "should return expected ranges for noniterable things" do
+ inclusive_base_case = {
+ 1.to_f .. 4.to_f => nil, # before
+ 11.to_f .. 15.to_f => nil, # after
+ 1.to_f .. 6.to_f => 5.to_f .. 6.to_f, # overlap_begin
+ 9.to_f .. 15.to_f => 9.to_f .. 10.to_f, # overlap_end
+ 1.to_f .. 5.to_f => 5.to_f .. 5.to_f, # overlap_begin_edge
+ 10.to_f .. 15.to_f => 10.to_f .. 10.to_f, # overlap_end_edge
+ 5.to_f .. 10.to_f => 5.to_f .. 10.to_f, # overlap_all
+ 6.to_f .. 9.to_f => 6.to_f .. 9.to_f, # overlap_inner
+
+ 1.to_f ... 5.to_f => nil, # before (exclusive range)
+ 1.to_f ... 7.to_f => 5.to_f ... 7.to_f, # overlap_begin (exclusive range)
+ 1.to_f ... 6.to_f => 5.to_f ... 6.to_f, # overlap_begin_edge (exclusive range)
+ 5.to_f ... 11.to_f => 5.to_f .. 10.to_f, # overlap_all (exclusive range)
+ 6.to_f ... 10.to_f => 6.to_f ... 10.to_f, # overlap_inner (exclusive range)
+ }
+
+ inclusive_base_case.each do |other, expected|
+ do_test( 5.to_f..10.to_f, other, expected )
+ do_test( other, 5.to_f..10.to_f, expected )
+ end
+
+ exclusive_base_case = {
+ 1.to_f .. 4.to_f => nil, # before
+ 11.to_f .. 15.to_f => nil, # after
+ 1.to_f .. 6.to_f => 5.to_f .. 6.to_f, # overlap_begin
+ 9.to_f .. 15.to_f => 9.to_f ... 10.to_f, # overlap_end
+ 1.to_f .. 5.to_f => 5.to_f .. 5.to_f, # overlap_begin_edge
+ 10.to_f .. 15.to_f => nil, # overlap_end_edge
+ 5.to_f .. 10.to_f => 5.to_f ... 10.to_f, # overlap_all
+ 6.to_f .. 9.to_f => 6.to_f .. 9.to_f, # overlap_inner
+
+ 1.to_f ... 5.to_f => nil, # before (exclusive range)
+ 1.to_f ... 7.to_f => 5.to_f ... 7.to_f, # overlap_begin (exclusive range)
+ 1.to_f ... 6.to_f => 5.to_f ... 6.to_f, # overlap_begin_edge (exclusive range)
+ 5.to_f ... 11.to_f => 5.to_f ... 10.to_f, # overlap_all (exclusive range)
+ 6.to_f ... 10.to_f => 6.to_f ... 10.to_f, # overlap_inner (exclusive range)
+ }
+
+ exclusive_base_case.each do |other, expected|
+ do_test( 5.to_f...10.to_f, other, expected )
+ do_test( other, 5.to_f...10.to_f, expected )
+ end
+ end
+end
-- You received this message because you are subscribed to the Google Groups "Puppet Developers" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to [email protected].
For more options, visit this group at http://groups.google.com/group/puppet-dev?hl=en.
