Verified Commit a3be4a0d authored by Jelle van der Waa's avatar Jelle van der Waa 🚧 Committed by Jelle van der Waa
Browse files

Add unit tests for basic rebuild order

Create a basic test suite for rebuilder using rstest which works as
following. With the tarfile crate the tests create fake pacman sync db's
to be used by the main rebuild function to create various test
scenario's.

Closes: #8
parent 5286c466
Pipeline #4430 passed with stages
in 2 minutes and 55 seconds
......@@ -7,6 +7,7 @@ stages:
- format
- clippy
- check
- test
format:
stage: format
......@@ -22,3 +23,8 @@ check:
stage: check
script:
- cargo check --all --verbose --release
test:
stage: test
script:
- cargo test --all --verbose --release
......@@ -12,9 +12,9 @@ dependencies = [
[[package]]
name = "alpm-sys"
version = "1.1.0"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d03b6220fa6c6eeace60fbc13ab5195180ae8ab3a7fad08369ca1a95552f0b9"
checksum = "b7ec21bce781ec06de31204090442cfdc33d6b4be496239e82d796cfd8b62b23"
[[package]]
name = "ansi_term"
......@@ -48,6 +48,18 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "2.33.3"
......@@ -63,12 +75,35 @@ dependencies = [
"vec_map",
]
[[package]]
name = "filetime"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c122a393ea57648015bf06fbd3d372378992e86b9ff5a7a497b076a28c79efe"
dependencies = [
"cfg-if 1.0.0",
"libc",
"redox_syscall",
"winapi",
]
[[package]]
name = "fixedbitset"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d"
[[package]]
name = "getrandom"
version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6"
dependencies = [
"cfg-if 0.1.10",
"libc",
"wasi",
]
[[package]]
name = "hashbrown"
version = "0.9.1"
......@@ -111,9 +146,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.79"
version = "0.2.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2448f6066e80e3bfc792e9c98bf705b4b0fc6e8ef5b43e5889aff0eaa9c58743"
checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614"
[[package]]
name = "petgraph"
......@@ -125,6 +160,12 @@ dependencies = [
"indexmap",
]
[[package]]
name = "ppv-lite86"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
[[package]]
name = "proc-macro-error"
version = "1.0.4"
......@@ -167,15 +208,111 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
dependencies = [
"getrandom",
"libc",
"rand_chacha",
"rand_core",
"rand_hc",
]
[[package]]
name = "rand_chacha"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
dependencies = [
"getrandom",
]
[[package]]
name = "rand_hc"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
dependencies = [
"rand_core",
]
[[package]]
name = "rebuilder"
version = "0.1.0"
dependencies = [
"alpm",
"petgraph",
"rstest",
"structopt",
"tar",
"tempfile",
]
[[package]]
name = "redox_syscall"
version = "0.1.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
[[package]]
name = "remove_dir_all"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
dependencies = [
"winapi",
]
[[package]]
name = "rstest"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dec448bc157977efdc0a71369cf923915b0c4806b1b2449c3fb011071d6f7c38"
dependencies = [
"cfg-if 0.1.10",
"proc-macro2",
"quote",
"rustc_version",
"syn",
]
[[package]]
name = "rustc_version"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
dependencies = [
"semver",
]
[[package]]
name = "semver"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
dependencies = [
"semver-parser",
]
[[package]]
name = "semver-parser"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]]
name = "strsim"
version = "0.8.0"
......@@ -208,15 +345,41 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.45"
version = "1.0.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea9c5432ff16d6152371f808fb5a871cd67368171b09bb21b43df8e4a47a3556"
checksum = "cc371affeffc477f42a221a1e4297aedcea33d47d19b61455588bd9d8f6b19ac"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "tar"
version = "0.4.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "489997b7557e9a43e192c527face4feacc78bfbe6eed67fd55c4c9e381cba290"
dependencies = [
"filetime",
"libc",
"redox_syscall",
"xattr",
]
[[package]]
name = "tempfile"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
dependencies = [
"cfg-if 0.1.10",
"libc",
"rand",
"redox_syscall",
"remove_dir_all",
"winapi",
]
[[package]]
name = "textwrap"
version = "0.11.0"
......@@ -228,9 +391,9 @@ dependencies = [
[[package]]
name = "unicode-segmentation"
version = "1.6.0"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
checksum = "db8716a166f290ff49dabc18b44aa407cb7c6dbe1aa0971b44b8a24b0ca35aae"
[[package]]
name = "unicode-width"
......@@ -256,6 +419,12 @@ version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
[[package]]
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
[[package]]
name = "winapi"
version = "0.3.9"
......@@ -277,3 +446,12 @@ name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "xattr"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "244c3741f4240ef46274860397c7c74e50eb23624996930e484c16679633a54c"
dependencies = [
"libc",
]
......@@ -18,6 +18,12 @@ alpm = "0.10"
petgraph = "0.5"
structopt = "0.3"
[dev-dependencies]
rstest = "0.6"
tar = "0.4"
tempfile = "3"
[profile.release]
lto = true
codegen-units = 1
use rstest::fixture;
use std::convert::TryFrom;
use std::fmt;
use std::fs;
use std::fs::File;
use std::io::Write;
use tar::{Builder, Header};
use tempfile::{tempdir, TempDir};
// pacman database version (lib/libalpm/be_local.c)
const ALPM_DB_VERSION: &str = "9";
#[derive(Hash, Clone, Eq, PartialEq, Debug)]
pub struct Package {
pub name: String,
pub base: String,
pub version: String,
pub depends: Vec<String>,
pub makedepends: Vec<String>,
}
impl Package {
fn new(
name: &str,
base: &str,
version: &str,
depends: Vec<String>,
makedepends: Vec<String>,
) -> Package {
Package {
name: name.to_string(),
base: base.to_string(),
version: version.to_string(),
depends,
makedepends,
}
}
fn desc(&self) -> String {
let mut desc = String::from("");
let name = format!("%NAME%\n{}\n", self.name);
desc.push_str(&name);
let base = format!("%BASE%\n{}\n", self.base);
desc.push_str(&base);
if !self.depends.is_empty() {
desc.push_str("%DEPENDS%\n");
for dep in self.depends.iter() {
desc.push_str(dep);
desc.push_str("\n");
}
desc.push_str("\n");
}
if !self.makedepends.is_empty() {
desc.push_str("%MAKEDEPENDS%\n");
for dep in self.makedepends.iter() {
desc.push_str(dep);
desc.push_str("\n");
}
desc.push_str("\n");
}
desc
}
fn path(&self) -> String {
format!("{}-{}/desc", self.name, self.version)
}
fn tarheader(&self) -> Header {
let mut header = Header::new_gnu();
let desc = self.desc();
let datalen = u64::try_from(desc.len()).unwrap();
header.set_path(self.path()).unwrap();
header.set_size(datalen);
header.set_mode(0o644);
header.set_cksum();
header
}
}
impl fmt::Display for Package {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.name)
}
}
impl PartialEq<&str> for Package {
fn eq(&self, other: &&str) -> bool {
&self.name.as_str() == other
}
}
fn init_repodb(reponame: String, packages: Vec<Package>) -> (TempDir, String) {
let tempdir = tempdir().unwrap();
let dbpath = tempdir.path().display().to_string();
// local dir
let localdir = tempdir.path().join("local");
fs::create_dir(&localdir).unwrap();
let mut file = File::create(localdir.join("ALPM_DB_VERSION")).unwrap();
file.write_all(ALPM_DB_VERSION.as_bytes()).unwrap();
// sync dir
let syncdir = tempdir.path().join("sync");
fs::create_dir(&syncdir).unwrap();
let dbloc = syncdir.join(format!("{}.db", reponame));
create_db(dbloc.display().to_string(), packages);
(tempdir, dbpath)
}
fn create_db(dbloc: String, pkgs: Vec<Package>) {
let mut archive = Builder::new(Vec::new());
for pkg in pkgs {
let header = pkg.tarheader();
let desc = pkg.desc();
let data = desc.as_bytes();
archive.append(&header, data).unwrap();
}
archive.finish().unwrap();
let data = archive.into_inner().unwrap();
let mut afile = File::create(dbloc).unwrap();
afile.write_all(&data).unwrap();
}
#[fixture]
pub fn invalid_dbpath() -> (Vec<String>, Option<String>) {
let pkgnames = vec![String::from("testpkg1")];
let dbpath = Some(String::from("/non-existant-path"));
(pkgnames, dbpath)
}
#[fixture]
pub fn no_reverse_deps() -> (Vec<Package>, Option<String>, Vec<String>, TempDir) {
let testpkg = Package::new("testpkg1", "testpkg1", "1.0-1", vec![], vec![]);
let packages = vec![testpkg];
let reponame = "test";
let (rootdir, dbpath) = init_repodb(reponame.to_string(), packages.clone());
(packages, Some(dbpath), vec![reponame.to_string()], rootdir)
}
#[fixture]
pub fn reverse_deps() -> (Vec<String>, Option<String>, Vec<String>, TempDir) {
let testpkg = Package::new("testpkg1", "testpkg1", "1-1", vec![], vec![]);
let testpkg2 = Package::new(
"testpkg2",
"testpkg2",
"1-1",
vec![testpkg.name.clone()],
vec![],
);
let pkgnames = vec![testpkg.name.clone(), testpkg2.name.clone()];
let packages = vec![testpkg, testpkg2];
let reponame = "test";
let (tempdir, dbpath) = init_repodb(reponame.to_string(), packages);
(pkgnames, Some(dbpath), vec![reponame.to_string()], tempdir)
}
#[fixture]
pub fn multiple_deps() -> (Vec<Package>, Option<String>, Vec<String>, TempDir) {
let testpkg = Package::new("testpkg1", "testpkg1", "1.0-1", vec![], vec![]);
let testpkg2 = Package::new(
"testpkg2",
"testpkg2",
"1.0-1",
vec![testpkg.name.clone()],
vec![],
);
let testpkg3 = Package::new(
"testpkg3",
"testpkg3",
"1-1",
vec![testpkg.name.clone(), testpkg2.name.clone()],
vec![],
);
let packages = vec![testpkg3, testpkg2, testpkg];
let reponame = "test";
let (tempdir, dbpath) = init_repodb(reponame.to_string(), packages.clone());
(packages, Some(dbpath), vec![reponame.to_string()], tempdir)
}
#[fixture]
pub fn reverse_make_deps() -> (Vec<Package>, Option<String>, Vec<String>, TempDir) {
let testpkg = Package::new("testpkg1", "testpkg1", "1-1", vec![], vec![]);
let testpkg2 = Package::new(
"testpkg2",
"testpkg2",
"1-1",
vec![],
vec![testpkg.name.clone()],
);
let packages = vec![testpkg, testpkg2];
let reponame = "test";
let (tempdir, dbpath) = init_repodb(reponame.to_string(), packages.clone());
(packages, Some(dbpath), vec![reponame.to_string()], tempdir)
}
#[fixture]
pub fn dependency_depth() -> (Vec<Package>, Option<String>, Vec<String>, TempDir) {
let testpkg = Package::new("testpkg1", "testpkg1", "1-1", vec![], vec![]);
let testpkg2 = Package::new(
"testpkg2",
"testpkg2",
"1-1",
vec![],
vec![testpkg.name.clone()],
);
let testpkg3 = Package::new(
"testpkg3",
"testpkg3",
"1-1",
vec![],
vec![testpkg2.name.clone()],
);
let packages = vec![testpkg, testpkg2, testpkg3];
let reponame = "test";
let (tempdir, dbpath) = init_repodb(reponame.to_string(), packages.clone());
(packages, Some(dbpath), vec![reponame.to_string()], tempdir)
}
#[fixture]
pub fn dependency_cycle() -> (Vec<Package>, Option<String>, Vec<String>, TempDir) {
let testpkg = Package::new(
"testpkg1",
"testpkg1",
"1-1",
vec![String::from("testpkg2")],
vec![],
);
let testpkg2 = Package::new(
"testpkg2",
"testpkg2",
"1-1",
vec![testpkg.name.clone()],
vec![],
);
dbg!(testpkg.clone());
dbg!(testpkg2.clone());
let packages = vec![testpkg, testpkg2];
let reponame = "test";
let (tempdir, dbpath) = init_repodb(reponame.to_string(), packages.clone());
(packages, Some(dbpath), vec![reponame.to_string()], tempdir)
}
use rstest::rstest;
use tempfile::TempDir;
pub mod fixtures;
use fixtures::{
dependency_cycle, dependency_depth, invalid_dbpath, multiple_deps, no_reverse_deps,
reverse_deps, reverse_make_deps, Package,
};
#[rstest]
#[should_panic]
fn test_invalid_dbpath(invalid_dbpath: (Vec<String>, Option<String>)) {
let pkgnames = invalid_dbpath.0;
let dbpath = invalid_dbpath.1;
rebuilder::run(pkgnames, dbpath, vec![], None).unwrap();
}
/// A package without any reverse dependencies should only print the given package
#[rstest]
fn test_no_reverse_deps(no_reverse_deps: (Vec<Package>, Option<String>, Vec<String>, TempDir)) {
let packages = no_reverse_deps.0;
let res = rebuilder::run(
vec![packages[0].name.clone()],
no_reverse_deps.1,
no_reverse_deps.2,
None,
)
.unwrap();
assert_eq!(packages[0], res.trim());
}
/// Given a package 'testpkg1' with a reverse dependency on 'testpkg2', the rebuild order should be
/// 'testpkg1 testpkg2'
#[rstest]
fn test_reverse_deps(reverse_deps: (Vec<String>, Option<String>, Vec<String>, TempDir)) {
let pkgnames = reverse_deps.0;
let pkgname = &pkgnames[0];
let res = rebuilder::run(
vec![pkgname.to_string()],
reverse_deps.1,
reverse_deps.2,
None,
)
.unwrap();
let res_pkgs: Vec<&str> = res.trim().split_ascii_whitespace().collect();
assert_eq!(pkgnames, res_pkgs);
}
/// Given a package 'testpkg1' with a reverse make dependency on 'testpkg2', the rebuild order
/// should be 'testpkg1 testpkg2'
#[rstest]
fn test_reverse_make_deps(reverse_make_deps: (Vec<Package>, Option<String>, Vec<String>, TempDir)) {
let packages = reverse_make_deps.0;
let pkgname = &packages[0].name;