diff --git a/ruby-gem/calabash-android.gemspec b/ruby-gem/calabash-android.gemspec index 31d65f78f..9256036ea 100644 --- a/ruby-gem/calabash-android.gemspec +++ b/ruby-gem/calabash-android.gemspec @@ -4,7 +4,8 @@ Gem::Specification.new do |s| s.name = "calabash-android" s.version = begin file = "#{File.expand_path(File.join(File.dirname(__FILE__), - "lib", "calabash-android", "version.rb"))}" + "lib", "calabash-android", + "version.rb"))}" m = Module.new m.module_eval IO.read(file).force_encoding("utf-8") version = m::Calabash::Android::VERSION @@ -47,7 +48,6 @@ into a valid version, e.g. 1.2.3 or 1.2.3.pre10 s.add_dependency( "awesome_print", '~> 1.2') s.add_dependency( 'httpclient', '>= 2.7.1', '< 3.0') s.add_dependency( 'escape', '~> 0.0.4') - s.add_dependency( 'luffa' ) s.add_development_dependency( 'rake', '~> 10.3' ) s.add_development_dependency( 'yard', '>= 0.9.12', '< 1.0' ) diff --git a/ruby-gem/lib/calabash-android/dependencies.rb b/ruby-gem/lib/calabash-android/dependencies.rb index 694aa959e..eaa8c43da 100644 --- a/ruby-gem/lib/calabash-android/dependencies.rb +++ b/ruby-gem/lib/calabash-android/dependencies.rb @@ -1,5 +1,4 @@ require 'rexml/document' -require 'luffa' require 'timeout' if RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/ @@ -193,13 +192,13 @@ def self.tools_directories(android_sdk_location) build_tools_directories = build_tools_files.select do |dir| begin - Luffa::Version.new(File.basename(dir)) + Calabash::Android::Version.new(File.basename(dir)) true rescue ArgumentError false end end.sort do |a, b| - Luffa::Version.compare(Luffa::Version.new(File.basename(a)), Luffa::Version.new(File.basename(b))) + Calabash::Android::Version.compare(Calabash::Android::Version.new(File.basename(a)), Calabash::Android::Version.new(File.basename(b))) end.reverse.map{|dir| File.join('build-tools', File.basename(dir))} if build_tools_directories.empty? diff --git a/ruby-gem/lib/calabash-android/version.rb b/ruby-gem/lib/calabash-android/version.rb index fba8a1454..9dc4afa5d 100644 --- a/ruby-gem/lib/calabash-android/version.rb +++ b/ruby-gem/lib/calabash-android/version.rb @@ -1,5 +1,213 @@ module Calabash module Android VERSION = "0.9.8" + + # A model of a software release version that can be used to compare two versions. + # + # Calabash and RunLoop try very hard to comply with Semantic Versioning rules. + # However, the semantic versioning spec is incompatible with RubyGem's patterns + # for pre-release gems. + # + # > "But returning to the practical: No release version of SemVer is compatible + # > with Rubygems." - _David Kellum_ + # + # Calabash and RunLoop version numbers will be in the form `..[.pre]`. + # + # @see http://semver.org/ + # @see http://gravitext.com/2012/07/22/versioning.html + # + # TODO Expand to handle versions with more than 1 "." and no "." + # ^ Needs to handle arbitrary versions from Info.plists. In particular it + # needs to handle a unix timestamp - found the DeviceAgent-Runner.app. + class Version + + # @!attribute [rw] major + # @return [Integer] the major version + attr_accessor :major + + # @!attribute [rw] minor + # @return [Integer] the minor version + attr_accessor :minor + + # @!attribute [rw] patch + # @return [Integer] the patch version + attr_accessor :patch + + # @!attribute [rw] pre + # @return [Boolean] true if this is a pre-release version + attr_accessor :pre + + # @!attribute [rw] pre_version + # @return [Integer] if this is a pre-release version, returns the + # pre-release version; otherwise this is nil + attr_accessor :pre_version + + # Creates a new Version instance with all the attributes set. + # + # @example + # version = Version.new(0.10.1) + # version.major => 0 + # version.minor => 10 + # version.patch => 1 + # version.pre => false + # version.pre_version => nil + # + # @example + # version = Version.new(1.6.3.pre5) + # version.major => 1 + # version.minor => 6 + # version.patch => 3 + # version.pre => true + # version.pre_version => 5 + # + # @param [String] version the version string to parse. + # @raise [ArgumentError] if version is not in the form 5, 6.1, 7.1.2, 8.2.3.pre1 + def initialize(version) + tokens = version.strip.split('.') + count = tokens.count + if tokens.empty? + raise ArgumentError, "expected '#{version}' to be like 5, 6.1, 7.1.2, 8.2.3.pre1" + end + + if count < 4 and tokens.any? { |elm| elm =~ /\D/ } + raise ArgumentError, "expected '#{version}' to be like 5, 6.1, 7.1.2, 8.2.3.pre1" + end + + if count == 4 + @pre = tokens[3] + pre_tokens = @pre.scan(/\D+|\d+/) + @pre_version = pre_tokens[1].to_i if pre_tokens.count == 2 + end + + @major, @minor, @patch = version.split('.').map(&:to_i) + end + + # Returns an string representation of this version. + # @return [String] a string in the form `..[.pre]` + def to_s + str = [major, minor, patch].compact.join('.') + str = "#{str}.#{pre}" if pre + str + end + + def inspect + "#" + end + + # Compare this version to another for _object_ equality. This allows + # Version instances to be used as Hash keys. + # @param [Version] other the version to compare against. + def eql?(other) + hash == other.hash + end + + # The hash method for this instance. + def hash + str = [major, minor, patch].map do |str| + str ? str : "0" + end.join(".") + + if pre + str = "#{str}.#{pre}" + end + + str.hash + end + + # Compare this version to another for equality. + # @param [Version] other the version to compare against + # @return [Boolean] true if this Version is the same as `other` + def == (other) + Version.compare(self, other) == 0 + end + + # Compare this version to another for inequality. + # @param [Version] other the version to compare against + # @return [Boolean] true if this Version is not the same as `other` + def != (other) + Version.compare(self, other) != 0 + end + + # Is this version less-than another version? + # @param [Version] other the version to compare against + # @return [Boolean] true if this Version is less-than `other` + def < (other) + Version.compare(self, other) < 0 + end + + # Is this version greater-than another version? + # @param [Version] other the version to compare against + # @return [Boolean] true if this Version is greater-than `other` + def > (other) + Version.compare(self, other) > 0 + end + + # Is this version less-than or equal to another version? + # @param [Version] other the version to compare against + # @return [Boolean] true if this Version is less-than or equal `other` + def <= (other) + Version.compare(self, other) <= 0 + end + + # Is this version greater-than or equal to another version? + # @param [Version] other the version to compare against + # @return [Boolean] true if this Version is greater-than or equal `other` + def >= (other) + Version.compare(self, other) >= 0 + end + + # Compare version `a` to version `b`. + # + # @example + # compare Version.new(0.10.0), Version.new(0.9.0) => 1 + # compare Version.new(0.9.0), Version.new(0.10.0) => -1 + # compare Version.new(0.9.0), Version.new(0.9.0) => 0 + # + # @return [Integer] an integer `(-1, 1)` + def <=> (other) + Version.compare(self, other) + end + + # Compare version `a` to version `b`. + # + # @example + # compare Version.new(0.10.0), Version.new(0.9.0) => 1 + # compare Version.new(0.9.0), Version.new(0.10.0) => -1 + # compare Version.new(0.9.0), Version.new(0.9.0) => 0 + # + # @return [Integer] an integer `(-1, 1)` + def self.compare(a, b) + + if a.major != b.major + return a.major.to_i > b.major.to_i ? 1 : -1 + end + + a_minor = a.minor ? a.minor.to_i : 0 + b_minor = b.minor ? b.minor.to_i : 0 + if a_minor != b_minor + return a_minor > b_minor.to_i ? 1 : -1 + end + + a_patch = a.patch ? a.patch.to_i : 0 + b_patch = b.patch ? b.patch.to_i : 0 + if a_patch != b_patch + return a_patch.to_i > b_patch.to_i ? 1 : -1 + end + + return -1 if a.pre && (!a.pre_version) && b.pre_version + return 1 if a.pre_version && b.pre && (!b.pre_version) + + return -1 if a.pre && (!b.pre) + return 1 if (!a.pre) && b.pre + + return -1 if a.pre_version && (!b.pre_version) + return 1 if (!a.pre_version) && b.pre_version + + if a.pre_version != b.pre_version + return a.pre_version.to_i > b.pre_version.to_i ? 1 : -1 + end + 0 + end + end end end diff --git a/ruby-gem/spec/lib/version_spec.rb b/ruby-gem/spec/lib/version_spec.rb new file mode 100644 index 000000000..77236a3ac --- /dev/null +++ b/ruby-gem/spec/lib/version_spec.rb @@ -0,0 +1,297 @@ +describe Calabash::Android::Version do + + describe '.new' do + describe 'non-pre-release versions' do + subject(:version) { Calabash::Android::Version.new('0.9.169') } + it { expect(version).not_to be nil } + it { expect(version.major).to be == 0 } + it { expect(version.minor).to be == 9 } + it { expect(version.patch).to be == 169 } + it { expect(version.pre).to be nil } + it { expect(version.pre_version).to be nil } + end + + describe 'unnumbered pre-release versions' do + subject(:version) { Calabash::Android::Version.new('0.9.169.pre') } + it { expect(version.pre).to be == 'pre' } + it { expect(version.pre_version).to be nil } + end + + describe 'numbered pre-release versions' do + subject(:version) { Calabash::Android::Version.new('0.9.169.pre1') } + it { expect(version.pre).to be == 'pre1' } + it { expect(version.pre_version).to be == 1 } + end + + describe 'invalid arguments' do + it { expect { Calabash::Android::Version.new(' ') }.to raise_error ArgumentError } + it { expect { Calabash::Android::Version.new('5.1.pre3') }.to raise_error ArgumentError } + it { expect { Calabash::Android::Version.new('5.pre2') }.to raise_error ArgumentError } + end + end + + describe '#hash' do + it '0.9.5.pre1' do + str = '0.9.5.pre1' + version = Calabash::Android::Version.new(str) + expect(str.hash).to be == version.hash + end + + it '0.9.5.pre' do + str = '0.9.5.pre' + version = Calabash::Android::Version.new(str) + expect(str.hash).to be == version.hash + end + + it '0.9.5' do + str = '0.9.5' + version = Calabash::Android::Version.new(str) + expect(str.hash).to be == version.hash + end + + it '0.9' do + version = Calabash::Android::Version.new("0.9") + expect("0.9.0".hash).to be == version.hash + end + + it '0' do + version = Calabash::Android::Version.new("0") + expect("0.0.0".hash).to be == version.hash + end + + it "9 == 9.0 == 9.0.0" do + version = Calabash::Android::Version.new("9") + expect("9.0.0".hash).to be == version.hash + + version = Calabash::Android::Version.new("9.0") + expect("9.0.0".hash).to be == version.hash + end + end + + describe '#eql?' do + it 'mocked' do + a = Calabash::Android::Version.new('9.9.9') + b = Calabash::Android::Version.new('0.0.0') + + expect(a).to receive(:hash).and_return(-1, -1) + expect(b).to receive(:hash).and_return(-1, 0) + + expect(a.eql?(b)).to be == true + expect(a.eql?(b)).to be == false + end + + it 'are equal' do + + a = Calabash::Android::Version.new('0.0.0') + b = Calabash::Android::Version.new('0.0.0') + + expect(a.eql?(b)).to be == true + end + + it 'are not equal' do + + a = Calabash::Android::Version.new('9.9.9') + b = Calabash::Android::Version.new('0.0.0') + + expect(a.eql?(b)).to be == false + end + end + + describe 'instance as a hash key' do + it 'keys match' do + a = Calabash::Android::Version.new('0.0.0') + b = Calabash::Android::Version.new('0.0.0') + + hash = { a => 'before!' } + + hash[b] = 'after!' + expect(hash[a]) == 'after!' + end + + it 'keys do not match' do + a = Calabash::Android::Version.new('0.0.0') + b = Calabash::Android::Version.new('9.9.9') + + hash = { a => 'before!' } + + hash[b] = 'after!' + expect(hash[a]) == 'before!' + expect(hash[b]) == 'after!' + end + end + + describe '==' do + it 'tests equality' do + a = Calabash::Android::Version.new('0.9.5') + b = Calabash::Android::Version.new('0.9.5') + expect(a == b).to be true + + a = Calabash::Android::Version.new('0.9.5.pre') + b = Calabash::Android::Version.new('0.9.5.pre') + expect(a == b).to be true + + a = Calabash::Android::Version.new('0.9.5.pre1') + b = Calabash::Android::Version.new('0.9.5.pre1') + expect(a == b).to be true + + a = Calabash::Android::Version.new('0.9.5.pre') + b = Calabash::Android::Version.new('0.9.5.pre1') + expect(a == b).to be false + + a = Calabash::Android::Version.new('0.9.5') + b = Calabash::Android::Version.new('0.9.5.pre1') + expect(a == b).to be false + + a = Calabash::Android::Version.new("9.0.0") + b = Calabash::Android::Version.new("9") + expect(b == a).to be true + + a = Calabash::Android::Version.new("9") + b = Calabash::Android::Version.new("9.0") + expect(b == a).to be true + + a = Calabash::Android::Version.new("9.0.0") + b = Calabash::Android::Version.new("9.0") + expect(b == a).to be true + end + end + + describe '!=' do + it 'tests not equal' do + a = Calabash::Android::Version.new('0.9.4') + b = Calabash::Android::Version.new('0.9.5') + expect(a != b).to be true + + a = Calabash::Android::Version.new('0.9.5') + b = Calabash::Android::Version.new('0.9.5.pre') + expect(a != b).to be true + + a = Calabash::Android::Version.new('0.9.5') + b = Calabash::Android::Version.new('0.9.5.pre1') + expect(a != b).to be true + + a = Calabash::Android::Version.new('0.9.5.pre') + b = Calabash::Android::Version.new('0.9.5.pre1') + expect(a != b).to be true + + a = Calabash::Android::Version.new('0.9.5.pre1') + b = Calabash::Android::Version.new('0.9.5.pre2') + expect(a != b).to be true + end + end + + describe '<' do + it 'tests less than' do + a = Calabash::Android::Version.new('0.9.4') + b = Calabash::Android::Version.new('0.9.5') + expect(a < b).to be true + + a = Calabash::Android::Version.new('0.9.5') + b = Calabash::Android::Version.new('0.9.5.pre') + expect(a > b).to be true + + a = Calabash::Android::Version.new('0.9.5') + b = Calabash::Android::Version.new('0.9.5.pre1') + expect(a > b).to be true + + a = Calabash::Android::Version.new('0.9.5.pre') + b = Calabash::Android::Version.new('0.9.5.pre1') + expect(a < b).to be true + + a = Calabash::Android::Version.new('0.9.5.pre1') + b = Calabash::Android::Version.new('0.9.5.pre2') + expect(a < b).to be true + end + end + + describe '>' do + it 'tests greater than' do + a = Calabash::Android::Version.new('0.9.4') + b = Calabash::Android::Version.new('0.9.5') + expect(b > a).to be true + + a = Calabash::Android::Version.new('0.9.5') + b = Calabash::Android::Version.new('0.9.5.pre') + expect(b < a).to be true + + a = Calabash::Android::Version.new('0.9.5') + b = Calabash::Android::Version.new('0.9.5.pre1') + expect(b < a).to be true + + a = Calabash::Android::Version.new('0.9.5.pre') + b = Calabash::Android::Version.new('0.9.5.pre1') + expect(b > a).to be true + + a = Calabash::Android::Version.new('0.9.5.pre1') + b = Calabash::Android::Version.new('0.9.5.pre2') + expect(b > a).to be true + + a = Calabash::Android::Version.new("8.0") + b = Calabash::Android::Version.new("9.0") + expect(a > b).to be false + + a = Calabash::Android::Version.new("9.0") + b = Calabash::Android::Version.new("8.0") + expect(a > b).to be true + end + end + + describe '<=' do + it 'tests less-than or equal' do + a = Calabash::Android::Version.new('0.9.4') + b = Calabash::Android::Version.new('0.9.5') + expect(a <= b).to be true + a = Calabash::Android::Version.new('0.9.5') + expect(a <= b).to be true + + a = Calabash::Android::Version.new("9.0.0") + b = Calabash::Android::Version.new("9") + expect(b <= a).to be true + + a = Calabash::Android::Version.new("9.0") + b = Calabash::Android::Version.new("9") + expect(b <= a).to be true + + a = Calabash::Android::Version.new("9.0.0") + b = Calabash::Android::Version.new("9.0") + expect(b <= a).to be true + end + end + + describe '>=' do + it 'tests greater-than or equal' do + a = Calabash::Android::Version.new('0.9.4') + b = Calabash::Android::Version.new('0.9.5') + expect(b >= a).to be true + a = Calabash::Android::Version.new('0.9.5') + expect(b >= a).to be true + + a = Calabash::Android::Version.new("9.0.0") + b = Calabash::Android::Version.new("9") + expect(b >= a).to be true + + a = Calabash::Android::Version.new("9.0") + b = Calabash::Android::Version.new("9") + expect(b >= a).to be true + + a = Calabash::Android::Version.new("9.0.0") + b = Calabash::Android::Version.new("9.0") + expect(b >= a).to be true + end + end + + describe '.compare' do + subject(:a) { Calabash::Android::Version.new('6.0') } + it 'works if there is no patch level' do + b = Calabash::Android::Version.new('5.1.1') + expect(Calabash::Android::Version.compare(a, b)).to be == 1 + expect(Calabash::Android::Version.compare(b, a)).to be == -1 + end + + it 'works if there is no minor level' do + b = Calabash::Android::Version.new('5') + expect(Calabash::Android::Version.compare(a, b)).to be == 1 + expect(Calabash::Android::Version.compare(b, a)).to be == -1 + end + end +end