From 26c8605256fe850e47097df79b69aadc10159ce6 Mon Sep 17 00:00:00 2001 From: Christoph Haas Date: Fri, 26 Feb 2016 17:32:04 +0100 Subject: [PATCH] Function to compare package versions added --- lib/deb_importer.rb | 67 +++++++++++++++++++++++++++++++++++ test/lib/deb_importer_test.rb | 14 ++++++-- 2 files changed, 79 insertions(+), 2 deletions(-) diff --git a/lib/deb_importer.rb b/lib/deb_importer.rb index 673913a..cc1ba0b 100644 --- a/lib/deb_importer.rb +++ b/lib/deb_importer.rb @@ -149,4 +149,71 @@ module DebImporter end # Enumerator end # def + # Determine which version string denotes the newer package + # v1: version of the first package + # v2: version of the second package + # op: the comparison operator (default: 'gt' / greater-than) + # + # The algorithm is described in the Debian Policy at + # https://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Version + # The reference implementation is "dpkg --compare-versions …" + def dpkg_compare_version(v1, v2, op: 'gt') + system("dpkg --compare-versions #{v1} #{op} #{v2}") + end + + class Version + def initialize(version_string) + @version_string = version_string + # Split into "[epoch:]version[-revision]" + unless version_string =~ /^(?(\d+)\:)?(?.+)(?\-[\+\.~]+)?/ + raise ArgumentError, "Cannot parse version string: #{version_string}" + end + @epoch = epoch if epoch + @version = version + @revision = revision if revision + end # /def + + def to_s + @version + end + + def >(a,b) + return true if a.epoch > b.epoch + return true if version_compare(a.version, b.version) + return true if version_compare(a.revision, b.revision) + end + + private + + def version_compare(x,y) + # Compare a version string (like the upstream_version or + # debian_revision string) against another version string. + # The algorithm works like this: + # + # The strings are compared from left to right. + # First the initial part of each string consisting entirely of non-digit + # characters is determined. These two parts (one of which may be empty) + # are compared lexically. If a difference is found it is returned. + # The lexical comparison is a comparison of ASCII values modified so + # that all the letters sort earlier than all the non-letters and so that + # a tilde sorts before anything, even the end of a part. + # For example, the following parts are in sorted order from + # earliest to latest: ~~, ~~a, ~, the empty part, a.[37] + # + # Then the initial part of the remainder of each string which + # consists entirely of digit characters is determined. The + # numerical values of these two parts are compared, and any + # difference found is returned as the result of the comparison. + # For these purposes an empty string (which can only occur at + # the end of one or both version strings being compared) counts as zero. + # + # These two steps (comparing and removing initial non-digit strings + # and initial digit strings) are repeated until a difference is + # found or both strings are exhausted. + + (x.chars).zip(y.chars) do |xchar,ychar| + puts xchar, ychar + end + end + end # /class end # module diff --git a/test/lib/deb_importer_test.rb b/test/lib/deb_importer_test.rb index cee922c..5dc0f9e 100644 --- a/test/lib/deb_importer_test.rb +++ b/test/lib/deb_importer_test.rb @@ -8,10 +8,20 @@ include DebImporter class PackagesHelperTest < ActionView::TestCase + test "should detect the proper order of package versions" do + assert(DebImporter::Version.new('2') > DebImporter::Version.new('1')) + assert(DebImporter::Version.new('1.0') > DebImporter::Version.new('0.9')) + assert(DebImporter::Version.new('2:1') > DebImporter::Version.new('1:2')) + assert(DebImporter::Version.new('1.0c') > DebImporter::Version.new('1.0aa')) + assert(DebImporter::Version.new('1.0.1') > DebImporter::Version.new('1.0.0.1')) + assert(DebImporter::Version.new('1.0c') > DebImporter::Version.new('1.0.0')) + assert(DebImporter::Version.new('1.0') > DebImporter::Version.new('1.0~pre')) + end + test "should be able to parse local Debian repository test files" do Rails.logger = Logger.new(STDOUT) - Rails.logger.level = Logger::ERROR - # Rails.logger.level = Logger::INFO + # Rails.logger.level = Logger::ERROR + Rails.logger.level = Logger::INFO # Rails.logger.level = Logger::DEBUG # Load information about test repository from environments/test.rb