From 8118e97adb76e8c0ecb9bfbfb30c1f47bc04c666 Mon Sep 17 00:00:00 2001 From: Mike Dalessio Date: Mon, 21 Dec 2020 16:54:35 -0500 Subject: [PATCH] test: scripts to assert on packaged gem files and installed gems some tests are skipped because they assert new behavior which will be implemented in the next few commits. Closes #2076 Closes #2078 --- scripts/test-gem-file-contents | 210 +++++++++++++++++++++++++++++++++ scripts/test-gem-installation | 114 ++++++++++++++++++ 2 files changed, 324 insertions(+) create mode 100755 scripts/test-gem-file-contents create mode 100755 scripts/test-gem-installation diff --git a/scripts/test-gem-file-contents b/scripts/test-gem-file-contents new file mode 100755 index 0000000000..1581e696b4 --- /dev/null +++ b/scripts/test-gem-file-contents @@ -0,0 +1,210 @@ +#! /usr/bin/env ruby +# frozen_string_literal: true +# +# this script is intended to run as part of the CI test suite. +# +# it inspects the contents of a nokogiri gem file -- both the files and the gemspec -- to ensure +# we're packaging what we expect, and that we're not packaging anything we don't expect. +# +# this file isn't in the `test/` subdirectory because it's intended to be run standalone against a +# built gem file (and not against the source code or behavior of the gem itself). +# + +require "bundler/inline" + +gemfile do + source "https://rubygems.org" + gem "minitest" + gem "minitest-reporters" +end + +require "yaml" + +def usage_and_exit(message = nil) + puts "ERROR: #{message}" if message + puts "USAGE: #{File.basename(__FILE__)} [options]" + exit(1) +end + +usage_and_exit if ARGV.include?("-h") +usage_and_exit unless gemfile = ARGV[0] +usage_and_exit("#{gemfile} does not exist") unless File.file?(gemfile) +usage_and_exit("#{gemfile} is not a gem") unless /\.gem$/.match(gemfile) +gemfile = File.expand_path(gemfile) + +gemfile_contents = Dir.mktmpdir do |dir| + Dir.chdir(dir) do + unless system("tar -xf #{gemfile} data.tar.gz") + raise "could not unpack gem #{gemfile}" + end + %x(tar -ztf data.tar.gz).split("\n") + end +end + +gemspec = Dir.mktmpdir do |dir| + Dir.chdir(dir) do + unless system("tar -xf #{gemfile} metadata.gz") + raise "could not unpack gem #{gemfile}" + end + YAML.load(%x(gunzip -c metadata.gz)) + end +end + +if ARGV.include?("-v") + puts "---------- gemfile contents ----------" + puts gemfile_contents + puts + puts "---------- gemspec ----------" + puts gemspec.to_ruby + puts +end + +require "minitest/autorun" +require "minitest/reporters" +Minitest::Reporters.use!([Minitest::Reporters::SpecReporter.new]) + +puts "Testing '#{gemfile}' (#{gemspec.platform})" +describe File.basename(gemfile) do + let(:cross_rubies_path) { File.join(File.dirname(__FILE__), "..", ".cross_rubies") } + let(:native_ruby_versions) do + File.read(cross_rubies_path).split("\n").map do |line| + line.split(":").first.split(".").take(2).join(".") # ugh + end.uniq.sort + end + + describe "setup" do + it "gemfile contains some files" do + actual = gemfile_contents.length + assert_operator(actual, :>, 60, "expected gemfile to contain more than #{actual} files") + end + + it "gemspec is a Gem::Specfication" do + assert_equal(Gem::Specification, gemspec.class) + end + end + + describe "all platforms" do + it "contains every ruby file in lib/" do + expected = %x(git ls-files lib).split("\n").grep(/\.rb$/).sort + actual = gemfile_contents.grep(%r{^lib/}).grep(/\.rb$/).sort + assert_equal(expected, actual) + end + end + + describe "ruby platform" do + it "contains the port files" do + actual_ports = gemfile_contents.grep(%r{^ports/}) + assert(actual_ports.grep(/libxml2-\d+\.\d+\.\d+\.tar\.gz/).length == 1, + "expected #{actual_ports} to include libxml2") + assert(actual_ports.grep(/libxslt-\d+\.\d+\.\d+\.tar\.gz/).length == 1, + "expected #{actual_ports} to include libxslt") + assert_equal(2, actual_ports.length) + end + + it "contains the patch files" do + actual_patches = gemfile_contents.grep(%r{^patches/}) + assert_operator(actual_patches.length, :>, 0) + end + + it "does not contain header files in lib/nokogiri/include" do + # these are copied to lib/nokogiri/include during compilation, not packaged + assert_empty(gemfile_contents.grep(%r{^lib/.*\.h$})) + end + + it "contains ext/nokogiri" do + assert_operator(gemfile_contents.grep(%r{^ext/nokogiri/.*\.[ch]}).length, :>, 20) + end + + it "does not contain the java jar files" do + assert_empty(gemfile_contents.grep(/.*\.jar$/)) + end + + it "depends on mini_portile2" do + assert(gemspec.dependencies.find { |d| d.name == "mini_portile2" }) + end + + it "includes C files in extra_rdoc_files" do + assert_operator(gemspec.extra_rdoc_files.grep(%r{ext/nokogiri/.*\.c$}).length, :>, 10) + end + end if gemspec.platform == Gem::Platform::RUBY + + describe "native platform" do + it "does not contain the port files" do + actual_ports = gemfile_contents.grep(%r{^ports/}) + assert_empty(actual_ports) + end + + it "does not contain the patch files" do + actual_patches = gemfile_contents.grep(%r{^patches/}) + assert_empty(actual_patches) + end + + it "contains nokogiri header files in lib/nokogiri/include" do + skip "until we fix https://github.com/sparklemotion/nokogiri/pull/1788" + assert_includes(gemfile_contents, "lib/nokogiri/include/nokogiri.h") + end + + it "contains packaged libraries' header files in lib/nokogiri/include" do + skip "until we fix https://github.com/sparklemotion/nokogiri/pull/1788" + assert_includes(gemfile_contents, "lib/nokogiri/include/libxml2/libxml/tree.h") + assert_includes(gemfile_contents, "lib/nokogiri/include/libxslt/xslt.h") + assert_includes(gemfile_contents, "lib/nokogiri/include/libexslt/exslt.h") + end + + it "does not contain ext/nokogiri" do + skip "until we fix https://github.com/sparklemotion/nokogiri/issues/2077" + assert_empty(gemfile_contents.grep(%r{^ext/nokogiri/})) + end + + it "does not contain the java jar files" do + assert_empty(gemfile_contents.grep(/.*\.jar$/)) + end + + it "does not depend on mini_portile2" do + # https://github.com/sparklemotion/nokogiri/issues/2078 + refute(gemspec.dependencies.find { |d| d.name == "mini_portile2" }) + end + + it "packages only ruby-version-specific libraries" do + # https://github.com/sparklemotion/nokogiri/issues/2076 + native_ruby_versions.each do |version| + assert_includes(gemfile_contents, "lib/nokogiri/#{version}/nokogiri.so") + end + refute_includes(gemfile_contents, "lib/nokogiri/nokogiri.so") + end + end if gemspec.platform.is_a?(Gem::Platform) && gemspec.platform.cpu + + describe "java platform" do + it "does not contain the port files" do + actual_ports = gemfile_contents.grep(%r{^ports/}) + assert_empty(actual_ports) + end + + it "does not contain the patch files" do + actual_patches = gemfile_contents.grep(%r{^patches/}) + assert_empty(actual_patches) + end + + it "does not contain header files in lib/nokogiri/include" do + skip "until we fix https://github.com/sparklemotion/nokogiri/pull/1788" + assert_empty(gemfile_contents.grep(%r{^lib/.*\.h$})) + end + + it "does not contain ext/nokogiri" do + skip "until we fix https://github.com/sparklemotion/nokogiri/issues/2099" + assert_empty(gemfile_contents.grep(%r{^ext/nokogiri/})) + end + + it "does contains the java jar files" do + actual_jars = gemfile_contents.grep(/.*\.jar$/) + ["nokogiri", "isorelax", "jing", "nekodtd", "nekohtml", "serializer", "xalan", "xercesImpl", "xml-apis"].each do |jar| + assert_equal(1, actual_jars.grep(%r{/#{jar}\.jar$}).length, "expected to contain #{jar}.jar") + end + end + + it "does not depend on mini_portile2" do + # https://github.com/sparklemotion/nokogiri/issues/2078 + refute(gemspec.dependencies.find { |d| d.name == "mini_portile2" }) + end + end if gemspec.platform == Gem::Platform.new("java") +end diff --git a/scripts/test-gem-installation b/scripts/test-gem-installation new file mode 100755 index 0000000000..70a7107807 --- /dev/null +++ b/scripts/test-gem-installation @@ -0,0 +1,114 @@ +#! /usr/bin/env ruby +# frozen_string_literal: true +# +# this script is intended to run as part of the CI test suite. +# +# it inspects the filesystem of a nokogiri gem installation to ensure it's complete and sane, and +# doesn't install anything we don't expect. +# +# this file isn't in the `test/` subdirectory because it's intended to be run standalone against an +# installed gem (and not against the source code or behavior of the gem itself). +# + +# this line needs to come before the bundler bit, to assert that we're running against an +# already-installed version (and not some other version that bundler/inline might install if it came +# first) +gemspec = Gem::Specification.find_by_name('nokogiri') +raise "could not find installed gem" unless gemspec + +require "bundler/inline" + +gemfile do + source "https://rubygems.org" + gem "minitest" + gem "minitest-reporters" + gem "nokogiri" +end + +require 'nokogiri' +require 'yaml' + +if ARGV.include?("-v") + puts "---------- Nokogiri version info ----------" + puts Nokogiri::VERSION_INFO.to_yaml + puts + puts "---------- Nokogiri installed gemspec ----------" + puts gemspec.to_ruby + puts +end + +require "minitest/autorun" +require "minitest/reporters" +Minitest::Reporters.use!([Minitest::Reporters::SpecReporter.new]) + +puts "Testing #{gemspec.full_name} installed in #{gemspec.base_dir}" +describe gemspec.full_name do + let(:nokogiri_include_dir) { File.join(gemspec.gem_dir, "lib/nokogiri/include") } + + # representative sample of the files + let(:nokogiri_header_files) { ["nokogiri.h", "xml_document.h", "xml_node.h"] } + let(:packaged_library_header_files) { ["libxml2/libxml/tree.h", "libxslt/xslt.h", "libexslt/exslt.h"] } + + it "loads the same version as the spec we've loaded" do + assert_equal(Nokogiri::VERSION, gemspec.version.to_s) + end + + describe "cruby" do + it "installs nokogiri headers" do + skip "until we fix https://github.com/sparklemotion/nokogiri/pull/1788" + assert(File.directory?(nokogiri_include_dir), + "expected directory to exist: #{nokogiri_include_dir}") + + nokogiri_header_files.each do |header| + assert(File.file?(File.join(nokogiri_include_dir, header)), + "expected #{header} to be installed in #{nokogiri_include_dir}") + end + end + + describe "native platform" do + it "acknowledges it is using packaged, precompiled libraries" do + assert(Nokogiri::VersionInfo.instance.libxml2_using_packaged?) + assert(Nokogiri::VERSION_INFO["libxml"].key?("source")) + assert_equal("packaged", Nokogiri::VERSION_INFO["libxml"]["source"]) + + assert(Nokogiri::VersionInfo.instance.libxml2_precompiled?) + assert(Nokogiri::VERSION_INFO["libxml"].key?("precompiled")) + assert(Nokogiri::VERSION_INFO["libxml"]["precompiled"]) + end + end if gemspec.platform.is_a?(Gem::Platform) && gemspec.platform.cpu + + describe "library" do + describe "packaged" do + it "declares where headers are installed" do + skip "until we fix https://github.com/sparklemotion/nokogiri/pull/1788" + # this is for nokogumbo and shouldn't be forever + nokogiri_include_parent_dir = File.dirname(nokogiri_include_dir) + assert_equal(nokogiri_include_parent_dir, Nokogiri::VERSION_INFO["libxml"]["libxml2_path"], + "expected Nokogiri::VERSION_INFO to point to #{nokogiri_include_parent_dir}") + end + + it "installs packaged libraries' headers" do + skip "until we fix https://github.com/sparklemotion/nokogiri/pull/1788" + packaged_library_header_files.each do |header| + assert(File.file?(File.join(nokogiri_include_dir, header)), + "expected #{header} to be installed in #{nokogiri_include_dir}") + end + end + end if Nokogiri::VersionInfo.instance.libxml2_using_packaged? + + describe "using system libraries" do + it "doesn't declare where headers are installed" do + refute(Nokogiri::VERSION_INFO["libxml"].key?("libxml2_path")) + end + + it "does not install packaged libraries' headers" do + packaged_library_header_files.each do |header| + dir = File.join(nokogiri_include_dir, File.dirname(header)) + refute(File.directory?(dir), + "expected directory #{dir} to not exist") + end + end + end unless Nokogiri::VersionInfo.instance.libxml2_using_packaged? + end + end unless gemspec.platform == Gem::Platform.new("java") +end