Testing Pacman Package Format Implementation in FPM
This test suite validates the Pacman package format functionality in FPM (Effing Package Management), focusing on package creation, attribute handling, and file permission preservation. The tests ensure proper conversion between different package formats and validate core Pacman package specifications.
Test Coverage Overview
Implementation Analysis
Technical Details
Best Practices Demonstrated
jordansissel/fpm
spec/fpm/package/pacman_spec.rb
require "spec_setup"
require 'fileutils'
require "fpm" # local
require "fpm/package/pacman" # local
require "stud/temporary"
if !program_exists?("bsdtar")
Cabin::Channel.get("rspec") \
.warn("Skipping Pacman tests because 'bsdtar' isn't in your PATH")
end
if !program_exists?("zstd")
Cabin::Channel.get("rspec") \
.warn("Skipping Pacman tests because 'zstd' isn't in your PATH")
end
def skip?
missing = []
missing << "bsdtar" unless program_exists?("bsdtar")
missing << "zstd" unless program_exists?("zstd")
return nil if missing.empty?
return "Missing programs: #{missing.join(", ")}"
end
describe FPM::Package::Pacman do
let(:target) { Stud::Temporary.pathname + ".pkg.tar.zst" }
after do
subject.cleanup
File.unlink(target) if File.exist?(target)
end
describe "#architecture" do
it "should convert amd64 to x86_64" do
subject.architecture = "amd64"
expect(subject.architecture).to(be == "x86_64")
end
it "should convert noarch to any" do
subject.architecture = "noarch"
expect(subject.architecture).to(be == "any")
end
let(:native) { `uname -m`.chomp }
it "should default to native" do
# Convert kernel name to debian name
expect(subject.architecture).to(be == native)
end
end
describe "#iteration" do
it "should default to 1" do
expect(subject.iteration).to(be == 1)
end
end
describe "#epoch" do
it "should default to nil" do
expect(subject.epoch).to(be_nil)
end
end
describe "#optional_depends" do
it "should default to []" do
expect(subject.attributes[:pacman_optional_depends]).to(be == [])
end
end
describe "#to_s" do
before do
subject.name = "name"
subject.version = "123"
subject.architecture = "any"
subject.iteration = "100"
subject.epoch = "5"
end
it "should have a default output usable as a filename" do
# This is the default filename I see commonly produced by debuild
insist { subject.to_s } == "name-123-100-any.pkg.tar.zst"
end
context "when iteration is nil" do
before do
subject.iteration = nil
end
it "should have an iteration of `1`" do
# This is the default filename I see commonly produced by debuild
expect(subject.to_s).to(be == "name-123-1-any.pkg.tar.zst")
end
end
end
describe "#output" do
let(:original) { FPM::Package::Pacman.new }
let(:input) { FPM::Package::Pacman.new }
context "Test that empty epoch is tested properly" do
before do
skip(skip?) if skip?
original.name = "foo"
original.version = "123"
original.iteration = "100"
# original.epoch conspicuously absent
original.architecture = "all"
original.output(target)
input.input(target)
end
it "should have the correct name" do
skip("Missing bsdtar") unless program_exists?("bsdtar")
skip("Missing zstd") unless program_exists?("zstd")
insist { input.name } == original.name
end
it "should have the correct version" do
skip("Missing bsdtar") unless program_exists?("bsdtar")
skip("Missing zstd") unless program_exists?("zstd")
insist { input.version } == original.version
end
it "should have the correct iteration" do
skip("Missing bsdtar") unless program_exists?("bsdtar")
skip("Missing zstd") unless program_exists?("zstd")
insist { input.iteration } == original.iteration
end
it "should have the correct epoch" do
skip("Missing bsdtar") unless program_exists?("bsdtar")
skip("Missing zstd") unless program_exists?("zstd")
insist { input.epoch } == original.epoch
end
end
context "normal tests" do
before do
skip(skip?) if skip?
# output a package, use it as the input, set the subject to that input
# package. This helps ensure that we can write and read packages
# properly.
# The target file must not exist.
original.name = "foo"
original.version = "123"
original.iteration = "100"
original.epoch = "5"
original.architecture = "all"
original.dependencies << "hello >= 20"
original.dependencies << "something > 10"
original.dependencies << "rpmlib(bogus)"
original.dependencies << "/usr/bin/bad-dep"
original.provides << "#{original.name} = #{original.version}"
original.conflicts = ["foo < 123"]
original.attributes[:pacman_opt_depends] = ["bamb > 10"]
original.directories << '/var/lib/foo'
::Dir.chdir(original.staging_path) do
FileUtils::mkdir_p 'usr/bin'
File.open('usr/bin/foo', 'w') do |exe|
exe.write("Frankly, I think the odds are slightly in your favor " \
"at hand fighting.")
end
File.chmod(0755, 'usr')
File.chmod(0755, 'usr/bin')
File.chmod(0755, 'usr/bin/foo')
FileUtils::mkdir_p 'usr/share/doc'
File.chmod(0755, 'usr/share')
File.chmod(0755, 'usr/share/doc')
File.open('usr/share/doc/foo.txt', 'w') do |doc|
doc.write("It's not my fault I'm the biggest or the strongest.")
end
File.chmod(0644, 'usr/share/doc/foo.txt')
FileUtils::mkdir_p 'usr/lib'
# incorrectly permissioned path (but that's what these tests are for)
File.chmod(0700, 'usr/lib')
File.open('usr/lib/libfoo.so', 'w') do |lib|
lib.write("I don't even excercise.")
end
File.chmod(0755, 'usr/lib/libfoo.so')
FileUtils::mkdir_p 'etc'
File.chmod(0755, 'etc')
File.open('etc/foo.conf', 'w') do |conf|
conf.write("You mean, you'll put down your rock and I'll put down " \
"my sword, and we'll try and kill each other like " \
"civilized people?")
end
File.chmod(0600, 'etc/foo.conf')
FileUtils::mkdir_p 'var/lib/foo'
File.chmod(0755, 'var')
File.chmod(0755, 'var/lib')
File.chmod(0755, 'var/lib/foo')
end
[:before_install, :after_install, :before_remove, :after_remove,
:before_upgrade, :after_upgrade].each do |script|
original.scripts[script] = "#!/bin/sh\n\necho #{script.to_s}"
end
original.output(target)
input.input(target)
end
after do
original.cleanup
input.cleanup
end # after
context "script contents" do
[:before_install, :after_install, :before_remove, :after_remove,
:before_upgrade, :after_upgrade].each do |script|
it "should be the same both with input as with original for #{script.to_s}" do
skip("Missing bsdtar") unless program_exists?("bsdtar")
skip("Missing zstd") unless program_exists?("zstd")
expect( \
(input.scripts[script] =~ \
/[\n :]+#{Regexp.quote(original.scripts[script])}/m \
)
).to(be_truthy)
end
end
end
context "file permissions" do
{"/usr" => 0755,
"/usr/bin" => 0755,
"/usr/bin/foo" => 0755,
"/usr/lib" => 0700,
"/usr/lib/libfoo.so" => 0755,
"/usr/share" => 0755,
"/usr/share/doc" => 0755,
"/usr/share/doc/foo.txt" => 0644,
"/etc" => 0755,
"/etc/foo.conf" => 0600,
"/var" => 0755,
"/var/lib" => 0755,
"/var/lib/foo" => 0755}.each do |dir, perm|
it "should preserve file permissions for #{dir}" do
skip("Missing bsdtar") unless program_exists?("bsdtar")
skip("Missing zstd") unless program_exists?("zstd")
insist { File.stat(File.join(input.staging_path,
dir)).mode & 07777 } == perm
end
end
end
context "package attributes" do
it "should have the correct name" do
skip("Missing bsdtar") unless program_exists?("bsdtar")
skip("Missing zstd") unless program_exists?("zstd")
insist { input.name } == original.name
end
it "should have the correct version" do
skip("Missing bsdtar") unless program_exists?("bsdtar")
skip("Missing zstd") unless program_exists?("zstd")
insist { input.version } == original.version
end
it "should have the correct iteration" do
skip("Missing bsdtar") unless program_exists?("bsdtar")
skip("Missing zstd") unless program_exists?("zstd")
insist { input.iteration } == original.iteration
end
it "should have the correct epoch" do
skip("Missing bsdtar") unless program_exists?("bsdtar")
skip("Missing zstd") unless program_exists?("zstd")
insist { input.epoch } == original.epoch
end
it "should not have bogus dependencies, just correct dependencies" do
expect(input.dependencies).to(be == ["hello >= 20", "something > 10"])
end
end # package attributes
end # #output
end
# TODO: output sometimes make fu-:1.2.3.out.rpm or something. Make sure the
# version isn't screwed up in transit.
end # describe FPM::Package::Pacman