#166 Test RPM dependency generators.
Opened 5 months ago by jackorp. Modified 3 months ago
rpms/ jackorp/ruby enhance_dependency_generators  into  rawhide

file added
+166
@@ -0,0 +1,166 @@ 

+ require 'tmpdir'

+ require 'tempfile'

+ require 'fileutils'

+ # Available in Ruby upstream sources under tool/lib/envutil.rb

+ # Required for finding and setting up the built ruby binary.

+ require 'envutil'

+ 

+ module RPMTestHelper

+   def setup

+     @tmpdir = Dir.mktmpdir

+     @tempfiles = []

+   end

+ 

+   def teardown

+     @tempfiles.each do |file|

+       file.close

+       file.unlink

+     end

+ 

+     FileUtils.rmtree(@tmpdir)

+   end

+ 

+   GENERATOR_SCRIPT = ENV['GENERATOR_SCRIPT'].clone.freeze

+   if GENERATOR_SCRIPT.nil? || GENERATOR_SCRIPT == ''

+     raise "GENERATOR_SCRIPT is not specified." \

+       "Specify the ENV variable with absolute path to the generator."

+   end

+ 

+   Dependency = Struct.new('Dependency', :name, :requirements) do

+     def to_rpm_str

+       "rubygem(#{self.name})"

+     end

+   end

+ 

+   def make_gemspec(gem_info)

+     file = Tempfile.new('req_gemspec', @tmpdir)

+     # Fake gemspec with enough to pass most checks

+     # Rubygems uses to validate the format.

+     gemspec_contents = <<~EOF

+       # -*- encoding: utf-8 -*-

+       # stub: #{gem_info.name} #{gem_info.version} ruby lib

+ 

+       Gem::Specification.new do |s|

+         s.name = "#{gem_info.name}".freeze

+         s.version = "#{gem_info.version}"

+ 

+         s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=

+         s.require_paths = ["lib".freeze]

+         s.authors = ["John Doe".freeze]

+         s.bindir = "bin".freeze

+         s.date = "2023-12-15"

+         s.description = "Fake gemspec helper for testing Rubygem Generators".freeze

+         s.email = ["example@example.com".freeze]

+         s.files = ["LICENSE.txt".freeze, "lib/#{gem_info.name}.rb".freeze, "#{gem_info.name}.gemspec".freeze]

+         s.homepage = "https://pkgs.fedoraproject.org/rpms/ruby".freeze

+         s.licenses = ["MIT".freeze]

+         s.required_ruby_version = Gem::Requirement.new(">= 2.5.0".freeze)

+         s.rubygems_version = "3.3.5".freeze

+         s.summary = "Fake gemspec for testing Rubygem Generators".freeze

+ 

+         if s.respond_to? :specification_version then

+           s.specification_version = 4

+         end

+ 

+         if s.respond_to? :add_runtime_dependency then

+           #{gem_info.gemspec_runtime_dep_str}

+         else

+           #{gem_info.gemspec_dep_str}

+         end

+       end

+     EOF

+ 

+     file.write gemspec_contents

+     file.rewind

+     @tempfiles << file

+     file

+   end

+ 

+   # Caller is expected to close subprocess stdin via #close_write

+   # in order to let subprocess proceed if the process is reading

+   # from STDIN in a loop.

+   def rb_subprocess(*args)

+     args = [GENERATOR_SCRIPT] if args.empty?

+     ruby = EnvUtil.rubybin

+     f = IO.popen([ruby] + args, 'r+') #, external_encoding: external_encoding)

+     yield(f)

+   ensure

+     f.close unless !f || f.closed?

+   end

+ 

+   def run_generator_single_file(gem_info)

+     lines = []

+     gemspec_f = make_gemspec(gem_info)

+ 

+     rb_subprocess do |io|

+       io.write gemspec_f.path

+       io.close_write

+       lines = io.readlines

+     end

+ 

+     lines

+   end

+ 

+   def helper_rubygems_dependency

+     "ruby(rubygems)"

+   end

+ 

+   class GemInfo

+     attr_accessor :name, :version, :dependencies

+ 

+     def initialize(name: 'foo', version: '1.2.3', dependencies: [])

+       @name = name

+       @version = version

+       @dependencies = dependencies

+     end

+ 

+     def dependencies=(other)

+       raise ArgumentError, "#{self.class.name}##{__method__.to_s}: Expected array of `Dependency' elements" \

+         unless other.is_a?(Array) && other.all? { |elem| elem.respond_to?(:name) && elem.respond_to?(:requirements) }

+ 

+       @dependencies = other

+     end

+ 

+     def to_rpm_str

+       "rubygem(#{self.name})"

+     end

+ 

+     def gemspec_dep_str

+       return '' if self.dependencies.nil? || self.dependencies.empty?

+       @dependencies.inject("") do |memo, dep|

+         memo += if dep.requirements && !dep.requirements.empty?

+                   %Q|s.add_dependency(%q<#{dep.name}>.freeze, #{handle_dep_requirements(dep.requirements)})|

+                 else

+                   %Q|s.add_dependency(%q<#{dep.name}>.freeze)|

+                 end

+ 

+         memo += "\n"

+       end

+     end

+ 

+     def gemspec_runtime_dep_str

+       return '' if self.dependencies.nil? || self.dependencies.empty?

+ 

+       @dependencies.inject("") do |memo, dep|

+         memo += if dep.requirements && !dep.requirements.empty?

+                   %Q|s.add_runtime_dependency(%q<#{dep.name}>.freeze, #{handle_dep_requirements(dep.requirements)})|

+                 else

+                   %Q|s.add_runtime_dependency(%q<#{dep.name}>.freeze)|

+                 end

+ 

+         memo += "\n"

+       end

+     end

+ 

+     private

+ 

+     def handle_dep_requirements(reqs)

+       raise ArgumentError, "#{self.class.name}##{__method__.to_s}: Reqs must be an array." \

+         unless reqs.is_a? Array

+       raise ArgumentError, "#{self.class.name}##{__method__.to_s}: Reqs must not be empty for this method." \

+         if reqs.empty?

+ 

+       '[ "' + reqs.join('", "') + '" ]'

+     end

+   end

+ end

file modified
+20
@@ -194,6 +194,11 @@ 

  Source14: test_systemtap.rb

  # Ruby OpenSSL FIPS tests.

  Source15: test_openssl_fips.rb

+ # RPM gem Requires dependency generator tests.

+ Source16: rpm_test_helper.rb

+ Source17: test_rubygems_req.rb

+ Source18: test_rubygems_prov.rb

+ Source19: test_rubygems_con.rb

  

  # The load directive is supported since RPM 4.12, i.e. F21+. The build process

  # fails on older Fedoras.
@@ -1084,6 +1089,21 @@ 

  make -C %{_vpath_builddir} runruby TESTRUN_SCRIPT=%{SOURCE14}

  %endif

  

+ # Test dependency generators for RPM

+ GENERATOR_SCRIPT="%{SOURCE9}" \

+ make -C %{_vpath_builddir} runruby TESTRUN_SCRIPT=" \

+   -I%{_builddir}/%{buildsubdir}/tool/lib -I%{_sourcedir} --enable-gems \

+   %{SOURCE17} --verbose"

+ GENERATOR_SCRIPT="%{SOURCE10}" \

+ make -C %{_vpath_builddir} runruby TESTRUN_SCRIPT=" \

+   -I%{_builddir}/%{buildsubdir}/tool/lib -I%{_sourcedir} --enable-gems \

+   %{SOURCE18} --verbose"

+ GENERATOR_SCRIPT="%{SOURCE11}" \

+ make -C %{_vpath_builddir} runruby TESTRUN_SCRIPT=" \

+   -I%{_builddir}/%{buildsubdir}/tool/lib -I%{_sourcedir} --enable-gems \

+   %{SOURCE19} --verbose"

+ 

+ 

  DISABLE_TESTS=""

  MSPECOPTS=""

  

file added
+124
@@ -0,0 +1,124 @@ 

+ # frozen_string_literal: true

+ 

+ require 'test/unit'

+ require 'rpm_test_helper'

+ 

+ class TestRubyGemsCon < Test::Unit::TestCase

+   include RPMTestHelper

+ 

+   def test_filter_out_regular_requirements

+     gem_i = GemInfo.new

+ 

+     lines = run_generator_single_file(gem_i)

+ 

+     assert_equal(0, lines.size)

+ 

+     deps = [ Dependency.new('bar') ]

+     gem_i.dependencies = deps

+ 

+     lines = run_generator_single_file(gem_i)

+ 

+     assert_equal(0, lines.size)

+ 

+     deps = [

+       Dependency.new('bar'),

+       Dependency.new('baq'),

+       Dependency.new('quz')

+     ]

+ 

+     gem_i.dependencies = deps

+     lines = run_generator_single_file(gem_i)

+ 

+     assert_equal(0, lines.size)

+ 

+     deps = [

+       Dependency.new('bar', ['>= 4.1']),

+       Dependency.new('baz', ['~> 3.2']),

+       Dependency.new('quz', ['>= 5.6'])

+     ]

+ 

+     gem_i.dependencies = deps

+ 

+     lines = run_generator_single_file(gem_i)

+ 

+     assert_equal(0, lines.size)

+   end

+ 

+   def test_single_gem_single_version_conflict

+     con = Dependency.new('bar', ['!= 0.4.4'])

+ 

+     gem_i = GemInfo.new(dependencies: [ con ])

+     lines = run_generator_single_file(gem_i)

+ 

+     assert_equal(1, lines.size)

+     assert_equal("#{con.to_rpm_str} = 0.4.4\n", lines.first)

+   end

+ 

+   def test_multiple_gems_with_single_conflict

+     cons = [

+       Dependency.new('bar', ['!= 1.1']),

+       Dependency.new('baq', ['!= 1.2.2']),

+       Dependency.new('quz', ['!= 1.3'])

+     ]

+ 

+     gem_i = GemInfo.new(dependencies: cons)

+ 

+     lines = run_generator_single_file(gem_i)

+ 

+     assert_equal(3, lines.size)

+ 

+     assert_equal("#{cons[0].to_rpm_str} = 1.1\n"  , lines[0])

+     assert_equal("#{cons[1].to_rpm_str} = 1.2.2\n", lines[1])

+     assert_equal("#{cons[2].to_rpm_str} = 1.3\n"  , lines[2])

+   end

+ 

+   def test_multiple_conflicts_on_single_gem

+     con = Dependency.new('bar', ['!= 2.3', '!= 2.4'])

+ 

+     gem_i = GemInfo.new(dependencies: [con])

+ 

+     lines = run_generator_single_file(gem_i)

+ 

+     assert_equal(1, lines.size)

+     rpm_name = con.to_rpm_str

+     left_rpm_constraint = "(#{rpm_name} = 2.3 with "

+     right_rpm_constraint = "#{rpm_name} = 2.4)\n"

+     assert_equal((left_rpm_constraint + right_rpm_constraint), lines[0])

+ 

+     con = Dependency.new('bar', ['!= 2.3', '!= 2.4', '!= 4.5'])

+ 

+     gem_i = GemInfo.new(dependencies: [ con ])

+ 

+     lines = run_generator_single_file(gem_i)

+ 

+     assert_equal(1, lines.size)

+ 

+     rpm_name = con.to_rpm_str

+     left_rpm_constraint = "(#{rpm_name} = 2.3 with "

+     middle_rpm_constraint = "#{rpm_name} = 2.4 with "

+     right_rpm_constraint = "#{rpm_name} = 4.5)\n"

+ 

+     assert_equal((left_rpm_constraint + middle_rpm_constraint + right_rpm_constraint), lines[0])

+   end

+ 

+   def test_generates_conflicts_while_ignoring_regular_requirements

+     deps = [

+       Dependency.new('bar', ['>= 2.3', '!= 2.4.2']),

+       Dependency.new('quz', ['~> 3.0', '!= 3.2'])

+     ]

+ 

+     gem_i = GemInfo.new(dependencies: deps)

+ 

+     lines = run_generator_single_file(gem_i)

+ 

+     assert_equal(2, lines.size)

+ 

+     rpm_name = deps[0].to_rpm_str

+     rpm_constraint = "#{rpm_name} = 2.4.2\n"

+     assert_equal(rpm_constraint, lines[0])

+ 

+     rpm_name = deps[1].to_rpm_str

+     rpm_constraint = "#{rpm_name} = 3.2\n"

+     assert_equal(rpm_constraint, lines[1])

+   end

+ end

@@ -0,0 +1,52 @@ 

+ # frozen_string_literal: true

+ 

+ require 'test/unit'

+ require 'rpm_test_helper'

+ 

+ class TestRubyGemsProv < Test::Unit::TestCase

+   include RPMTestHelper

+ 

+   def test_provides_the_gem_version

+     gem_i = GemInfo.new(version: '1.2')

+ 

+     lines = run_generator_single_file(gem_i)

+ 

+     assert_equal(1, lines.size)

+     assert_equal("#{gem_i.to_rpm_str} = #{gem_i.version}\n", lines.first)

+ 

+     gem_i = GemInfo.new(name: 'somegem_foo', version: '4.5.6')

+ 

+     lines = run_generator_single_file(gem_i)

+ 

+     assert_equal(1, lines.size)

+     assert_equal("#{gem_i.to_rpm_str} = #{gem_i.version}\n", lines.first)

+ 

+     deps = [

+       Dependency.new('bar'),

+       Dependency.new('baq', [">= 1.2"]),

+       Dependency.new('quz', ["!= 3.2"])

+     ]

+     gem_i = GemInfo.new(dependencies: deps)

+ 

+     lines = run_generator_single_file(gem_i)

+ 

+     assert_equal(1, lines.size)

+     assert_equal("#{gem_i.to_rpm_str} = #{gem_i.version}\n", lines.first)

+   end

+ 

+   def test_translates_prelease_version_provides_from_rubygems_to_rpm

+     gem_i = GemInfo.new(version: '1.2.3.dev')

+ 

+     lines = run_generator_single_file(gem_i)

+ 

+     assert_equal(1, lines.size)

+     assert_equal("#{gem_i.to_rpm_str} = 1.2.3~dev\n", lines.first)

+ 

+     gem_i = GemInfo.new(name: 'foo2', version: '1.2.3.dev.2')

+ 

+     lines = run_generator_single_file(gem_i)

+ 

+     assert_equal(1, lines.size)

+     assert_equal("#{gem_i.to_rpm_str} = 1.2.3~dev.2\n", lines.first)

+   end

+ end

file added
+205
@@ -0,0 +1,205 @@ 

+ # frozen_string_literal: true

+ 

+ require 'test/unit'

+ require 'rpm_test_helper'

+ 

+ class TestRubyGemsReq < Test::Unit::TestCase

+   include RPMTestHelper

+ 

+   def test_depends_on_rubygems

+     gem_i = GemInfo.new

+ 

+     lines = run_generator_single_file(gem_i)

+ 

+     assert_equal(1, lines.size)

+     assert_equal("#{helper_rubygems_dependency}\n", lines.first)

+   end

+ 

+   def test_requires_rubygems_and_dependency

+     dep = Dependency.new('bar')

+     gem_i = GemInfo.new(dependencies: [dep])

+ 

+     lines = run_generator_single_file(gem_i)

+ 

+     assert_equal(2, lines.size)

+     assert_equal("#{helper_rubygems_dependency}\n", lines.first)

+     assert_equal("#{dep.to_rpm_str}\n", lines[1])

+   end

+ 

+   def test_requires_multiple_dependencies_with_constraint

+     constraints = [

+       '>= 3.0',

+       '>= 3.0.0',

+       '>= 3',

+       '= 1.0.2',

+       '= 3.0',

+       '< 3.2',

+       '<= 3.4'

+     ]

+ 

+     dependencies = []

+     constraints.each_with_index do |constraint, idx|

+       dependencies << Dependency.new("bar#{idx}", [constraint])

+     end

+ 

+     gem_i = GemInfo.new(dependencies: dependencies)

+ 

+     lines = run_generator_single_file(gem_i)

+     # + 1 for the rubygems dependency

+     assert_equal(constraints.size + 1, lines.size)

+     dependencies.each_with_index do |dep, idx|

+       rpm_dep_name = dep.to_rpm_str

+       # Start indexing lines at 1, to jump over rubygems dependency

+       assert_equal("#{rpm_dep_name} #{constraints[idx]}\n", lines[idx + 1])

+     end

+   end

+ 

+   def test_expands_pessimistic_constraint_for_rpm

+     dep = Dependency.new('bar', ['~> 1.2'])

+ 

+     gem_i = GemInfo.new(dependencies: [dep])

+ 

+     lines = run_generator_single_file(gem_i)

+ 

+     assert_equal(2, lines.size)

+ 

+     rpm_dep_name = dep.to_rpm_str

+     left_constraint = "#{rpm_dep_name} >= 1.2"

+     right_constraint = "#{rpm_dep_name} < 2"

+     expected_constraint = "(#{left_constraint} with #{right_constraint})\n"

+     assert_equal(expected_constraint, lines[1])

+   end

+ 

+   def test_multiple_pessimistically_constrained_dependencies

+     dependencies = []

+     dep_map = [

+       {

+         constraint:     '~> 1.2.3',

+         expanded_left:  '>= 1.2.3',

+         expanded_rigth: '< 1.3',

+         gem_name: 'bar1'

+       },

+       {

+         constraint:     '~> 1.2',

+         expanded_left:  '>= 1.2',

+         expanded_rigth: '< 2',

+         gem_name: 'bar2'

+       },

+       {

+         constraint:     '~> 3',

+         expanded_left:  '>= 3',

+         expanded_rigth: '< 4',

+         gem_name: 'bar3'

+       }

+     ].each do |deps|

+       dependencies << Dependency.new(deps[:gem_name], [deps[:constraint]])

+     end

+ 

+     gem_i = GemInfo.new(dependencies: dependencies)

+ 

+     lines = run_generator_single_file(gem_i)

+ 

+     assert_equal(dep_map.size + 1, lines.size)

+ 

+     dep_map.each_with_index do |hash, idx|

+       rpm_dep_name = dependencies[idx].to_rpm_str

+       left_constraint = rpm_dep_name + ' ' + hash[:expanded_left]

+       right_constraint = rpm_dep_name + ' ' + hash[:expanded_rigth]

+       expected_constraint = "(#{left_constraint} with #{right_constraint})\n"

+       assert_equal(expected_constraint, lines[idx + 1])

+     end

+   end

+ 

+   def test_multiple_constraints_on_one_dependency_composes_constraints_for_RPM

+     # The quoting here depends on how the constraint is expanded in the helpers.

+     # right now the form is `["#{constraint}"]`, therefore we have to not specify

+     # left and right quotes.

+     constraints = ['>= 0.2.3', '<= 0.2.5']

+     dep = Dependency.new('baz', constraints)

+ 

+     gem_i = GemInfo.new(dependencies: [dep])

+ 

+     lines = run_generator_single_file(gem_i)

+ 

+     assert_equal(2, lines.size)

+     rpm_dep_name = dep.to_rpm_str

+     assert_equal("(#{rpm_dep_name} >= 0.2.3 with #{rpm_dep_name} <= 0.2.5)\n", lines[1])

+ 

+     # Not sure who would compose a dependency like this, but it's possible

+     # to do with the current generator

+     constraints = ['> 0.4.5', '< 0.6.4', '>= 2.3', '<= 2.5.3']

+     dep = Dependency.new('qux', constraints)

+ 

+     gem_i = GemInfo.new(dependencies: [dep])

+ 

+     lines = run_generator_single_file(gem_i)

+ 

+     rpm_dep = dep.to_rpm_str

+     expected_str = "(#{rpm_dep} > 0.4.5 with #{rpm_dep} < 0.6.4 with " \

+                    "#{rpm_dep} >= 2.3 with #{rpm_dep} <= 2.5.3)\n"

+ 

+     assert_equal(2, lines.size)

+     assert_equal(expected_str, lines[1])

+   end

+ 

+   # https://bugzilla.redhat.com/show_bug.cgi?id=1561487

+   def test_depends_on_gem_with_version_conflict

+     dep = Dependency.new('baz', ['!= 0.4'])

+ 

+     gem_i = GemInfo.new(dependencies: [dep])

+ 

+     lines = run_generator_single_file(gem_i)

+ 

+     assert_equal(2, lines.size)

+     assert_equal("#{dep.to_rpm_str}\n", lines[1])

+   end

+ 

+   def test_filters_conflict_from_regular_version_constraints

+     constraint = ['> 1.2.4', '!= 1.2.7']

+     dep = Dependency.new('baq', constraint)

+ 

+     gem_i = GemInfo.new(dependencies: [dep])

+ 

+     lines = run_generator_single_file(gem_i)

+ 

+     assert_equal(2, lines.size)

+     assert_equal("#{dep.to_rpm_str} > 1.2.4\n", lines[1])

+   end

+ 

+   def test_filtering_conflicts_is_not_depending_on_contraint_ordering

+     constraints = ['!= 1.2.7', '> 1.2.4']

+     dep = Dependency.new('baq', constraints)

+ 

+     gem_i = GemInfo.new(dependencies: [dep])

+ 

+     lines = run_generator_single_file(gem_i)

+ 

+     assert_equal(2, lines.size)

+     assert_equal("#{dep.to_rpm_str} > 1.2.4\n", lines[1])

+   end

+ 

+   def test_filters_multiple_conflicts_from_dependency

+     omit "Case not yet supported."

+     constraints = ['!= 1.2.4', '!= 1.2.5', '!= 2.3', '!= 4.8']

+     dep = Dependency.new('baf', constraints)

+ 

+     gem_i = GemInfo.new(dependencies: [dep])

+ 

+     lines = run_generator_single_file(gem_i)

+ 

+     assert_equal(2, lines.size)

+     assert_equal("#{dep.to_rpm_str}\n", lines[1])

+   end

+ 

+   def test_filters_multiple_conflicts_from_dependency_but_keeps_regular_constraint

+     constraints = ['!= 1.2.4', '!= 1.2.5', '!= 2.3', '<= 4.8']

+     dep = Dependency.new('bam', constraints)

+ 

+     gem_i = GemInfo.new(dependencies: [dep])

+ 

+     lines = run_generator_single_file(gem_i)

+ 

+     assert_equal(2, lines.size)

+     assert_equal("#{dep.to_rpm_str} <= 4.8\n", lines[1])

+   end

+ end

Add tests for RPM dependency generators that execute during build. The tests make use of methods available in tools present in the upstream ruby tar archive to find and set up the ruby executable. This is then used to execute the dependency generator script with a given test input passed into the subprocess and collect the output given out by the generator for testing.

Skip "test_generator_on_gem_with_multiple_conflict_constraints" for now. rubygems.req is currently only capable of filtering out a single conflict requirement that is specified on a given dependency.

rebased onto 78d7784

5 months ago

I might've done a mistake transforming this line, I think this means each and every != requirement is ignored?

2 new commits added

  • dep generators: Move the RPM dependency prefix into a consistent place.
  • rubygems.req: Flatten expand_requirement. Move guard clause out of #inject.
5 months ago

rebased onto 0fb7d0c

5 months ago

I have not studied the PR in great detail, but two questions:

1) Should the whole rubygem be treated as a prefix? I suspect that the idea is to keep the rubygem(foo) format, but be able to extend it to rubyXX-rubygem(foo).

2) Wouldn't it be possible/desirable to submit the additional rubyXX- prefix via e.g. command line option?

Build succeeded.
https://fedora.softwarefactory-project.io/zuul/buildset/06805710cb6b4a33b400a4dc6cf95835

1) Should the whole rubygem be treated as a prefix? I suspect that the idea is to keep the rubygem(foo) format, but be able to extend it to rubyXX-rubygem(foo).

I am quite unclear on what's desired/expected when it comes to these dependencies. Of course, if we always want to have rubygem and only prefix in the moment the dependency becomes "nonstandard", then it makes sense to be able to extend it rather than replace it.

2) Wouldn't it be possible/desirable to submit the additional rubyXX- prefix via e.g. command line option?

Possible? I do not actually know. I'll test it :)
Desirable? That's a question that I wasn't originally targeting. I am generally more careful about introducing CLI. But it doesn't sound as a bad road to try walking.

Possible? I do not actually know. I'll test it :)

It does seem doable, at least from the local_generator point. I have yet to test whether it will be picked up when specified in rubygems.attr, but the RPM docs seem to say that it is possible.
But gets needs to be replaced with different reading method for this purpose, as the method looks at ARGV in addition to stdin.

It does further consolidate the places.

Question is whether we want to edit rubygems.{req,prov,con} or the lines for local_generator statements in ruby.spec and the rubygems.attr lines.

I am quite unclear on what's desired/expected when it comes to these dependencies.

I have Vagrant on my mind, when I was asking this question. I just did not want to imply anything ;)

Also (and sorry in advance :see_no_evil:) since you are poking around, we don't have any test suite. Can we have some? Can we execute it during build? Or should be create separate directory which will have some tests on PR? Should we have both?

Question is whether we want to edit rubygems.{req,prov,con} or the lines for local_generator statements in ruby.spec and the rubygems.attr lines.

Right, I don't have right answer. But I think that it is a bit related to the "test suite" question, because sometimes it is useful to be able to feed in some data

And BTW, there is one more thing. I believe that since RPM 4.19, the generators can be executed in parallel. IOW speaking about gets, we should not close our doors for this opportunity.

Also (and sorry in advance 🙈) since you are poking around, we don't have any test suite. Can we have some? Can we execute it during build? Or should be create separate directory which will have some tests on PR? Should we have both?

Oh yeah, I was thinking that its a shame this doesn't have tests when I was rewriting the lines :D

At the very least unit tests are IMO a very good idea. We describe in tests expected functionality of each generator and validate our expectations.

Then I'd be happy if we have some reasonable CLI tests that cover at the very least the basic Provides Requires and Conflicts we might get in a regular Ruby tar release with the bundled gems. (If the tests fail, then build fails and we know we might generate bogus dependencies)

So both of these should be build time (sounds relatively simple in my mind so far :) ),

we can then leverage the testing for PRs and have different set of tests that test building rubygem packages with Ruby and the generated requires, or get really deep with the testing. Not quite sure what opportunity we have there.

Lastly, this might bring at least 1 to 4 new files at the very least (1 per generator + all integrated?) to the package if the tests will be build-time. It is OK IMO, just thought I'd mention it.

Right, I don't have right answer. But I think that it is a bit related to the "test suite" question, because sometimes it is useful to be able to feed in some data

It is nice, question is how often does this need to feed some new data come. I am getting warmed up for the CLI, though.

And BTW, there is one more thing. I believe that since RPM 4.19, the generators can be executed in parallel. IOW speaking about gets, we should not close our doors for this opportunity.

Gets is just difficult to work with as it combines ARGV and STDIN when looking for files, which complicates reading AND providing arguments at the same time. Of course, all can be worked around.

Would you happen have some reference to RPM 4.19 parallel generating? If I am going to be creating tests, I might as well account for this properly first time. I know about it, but I don't remember how it will pass the files.

Then I'd be happy if we have some reasonable CLI tests that cover at the very least the basic Provides Requires and Conflicts we might get in a regular Ruby tar release with the bundled gems. (If the tests fail, then build fails and we know we might generate bogus dependencies)

This would be preferred to me.

Would you happen have some reference to RPM 4.19 parallel generating? If I am going to be creating tests, I might as well account for this properly first time. I know about it, but I don't remember how it will pass the files.

https://github.com/rpm-software-management/rpm/pull/2537

BTW it you look at the test cases in the PR, they are actually exclusively testing the CLI. Actually, this PR is probably bad example, but generally RPM tests CLI AFAICT

This would be preferred to me.

I might start with those then.

BTW it you look at the test cases in the PR, they are actually exclusively testing the CLI. Actually, this PR is probably bad example, but generally RPM tests CLI AFAICT

With Ruby being Ruby, when rewriting any part (which is done very sparsely, git blame seems to suggest circa every 4 years), unit tests come in very useful, even if it would only be to black-box test that when I insert correct format I get correct output in a function (useful to me anyway).

All in all however, if I am reading it correctly the files are actually still not passed via ARGV, which was my main concern for continuing using gets. We can adjust ARGV before we even start reading any files.

All in all however, if I am reading it correctly the files are actually still not passed via ARGV, which was my main concern for continuing using gets. We can adjust ARGV before we even start reading any files.

Not sure I follow you. The files are read from $STDIN via gets, while ARGV is about command line arguments. If you talked about ARGF, that would be different.

Not sure I follow you. The files are read from $STDIN via gets, while ARGV is about command line arguments. If you talked about ARGF, that would be different.

Then let me demostrate.
This is the problem:

$ cat gets_test.rb
p ARGV
while line = gets
  puts line
end
$ ruby gets_test.rb --hello
["--hello"]
gets_test.rb:2:in `gets': No such file or directory @ rb_sysopen - --hello (Errno::ENOENT)
    from gets_test.rb:2:in `gets'
    from gets_test.rb:2:in `<main>'

What I am saying is that we cannot leave command line options in ARGV (the program's argument vector), as then Kernel#gets uses ARGF#gets.

So, I am talking that we are reading ARGF.

And the generators use while filename = gets.

Not sure I follow you. The files are read from $STDIN via gets, while ARGV is about command line arguments. If you talked about ARGF, that would be different.

Then let me demostrate.

Oh, I was not aware about this behavior. But you are right. Thank you for enlightening me.

And the generators use while filename = gets.

The simplest fix is to use $stdin.get instead. Or anything else which makes sense :)

2 new commits added

  • Test RPM Require dependency generator during build.
  • Fix bug
5 months ago

rebased onto 96f161a

5 months ago

@vondruch I have started by implementing tests for rubygems Require generator for the features that are currently present. That one seemed most complex out of the 3.

There is a test of test_generator_on_gem_with_multiple_conflict_constraints, where I am not 100% sure if it is something to support, it's just multiple conflicts on one gem without any other constraint, which I think should generator just the dependency.

Currently it fails like so, both for my rewrite and for the req generator that is in rawhide.

  1) Failure:
TestRubyGemsReq#test_generator_on_gem_with_multiple_conflict_constraints [/builddir/build/SOURCES/test_rubygems_req.rb:303]:
<"rubygem(baf)\n"> expected but was
<"\n">.

But i think the expectation is correct for this case (even though the case seems bit artificial).

I have reused tool/lib/envutil.rb from Ruby's sources to find the built binary then run tested generator as a subprocess with IO pipes attached, which is inspired from Ruby's argf_test.rb.

Build failed. More information on how to proceed and troubleshoot errors available at https://fedoraproject.org/wiki/Zuul-based-ci
https://fedora.softwarefactory-project.io/zuul/buildset/c931fd13b1684694a1e5e462b6e3d335

Looks great!
Tests are paramount.

I think locally I did another WIP testfile for .con generator, and i have the .prov as the last item, as I don't expect much problems/features to test with provider generator.

rebased onto 611ddc5

4 months ago

Build failed. More information on how to proceed and troubleshoot errors available at https://fedoraproject.org/wiki/Zuul-based-ci
https://fedora.softwarefactory-project.io/zuul/buildset/1a5543ee3fb44825a49264ef292613cd

1 new commit added

  • Add rubygems.prov generator tests.
3 months ago

Build failed. More information on how to proceed and troubleshoot errors available at https://fedoraproject.org/wiki/Zuul-based-ci
https://fedora.softwarefactory-project.io/zuul/buildset/d0955070b3304b6faffe215062151ce3

5 new commits added

  • Make the RPM Generator test helper available in spec and also to the tests.
  • Requirement generator test refactor.
  • Provider generator test refactor.
  • Conflicts generator tests refactor.
  • Factor out common methods to deduplicate test implementation.
3 months ago

Build succeeded.
https://fedora.softwarefactory-project.io/zuul/buildset/b7319117963a41459202a4fd14c12efc

8 new commits added

  • Make the RPM Generator test helper available in spec and also to the tests.
  • Add rubygems.prov generator tests.
  • Add tests for rubygems.con generator.
  • Factor out common methods to deduplicate test implementation.
  • Test RPM Require dependency generator during build.
  • Warn on errors (=~ $stderr.puts) to provide information why generator failed.
  • dep generators: Move the RPM dependency prefix into a consistent place.
  • rubygems.req: Flatten expand_requirement. Move guard clause out of #inject.
3 months ago

rebased onto c690883

3 months ago

Rewrote the rubygems.req version expansion yet again since I finished tests for it, this time I went back to the original #inject, it seems to hold the best WRT to speed on some toy benchmark I cooked up locally.

I'd mention I rewrote it so that it works when there are multiple conflicts, or a conflict and a regular version requirement.

Build succeeded.
https://fedora.softwarefactory-project.io/zuul/buildset/d402c3d868c44c0b8af84a111f8bf0a0

2 new commits added

  • Test RPM dependency generators during build.
  • rubygems.req: Ignore any number of conflict version constraints.
3 months ago

rebased onto 6e0ecf7

3 months ago

Build failed. More information on how to proceed and troubleshoot errors available at https://fedoraproject.org/wiki/Zuul-based-ci
https://fedora.softwarefactory-project.io/zuul/buildset/1f668f5bf0824aae9608b2c55e017f18

rebased onto 0df3931

3 months ago

Only concern this PR with testing the dependency generators for now. Enhancement can come later as a separate PR

Build failed. More information on how to proceed and troubleshoot errors available at https://fedoraproject.org/wiki/Zuul-based-ci
https://fedora.softwarefactory-project.io/zuul/buildset/725917eb30574bb680e9cdc0cadf956d