Krolyxon
Tag v0.3.0

ferreniumtestscommonmod.rs

153 lines · 5.0 KB · Rust

1//! Fixture helpers: build real repositories with the git CLI.
2
3#![allow(dead_code)]
4
5use std::path::{Path, PathBuf};
6use std::process::Command;
7
8/// Run git in `dir` with an isolated environment; panics on failure.
9pub fn git(dir: &Path, args: &[&str]) -> String {
10 let home = std::env::temp_dir();
11 let out = Command::new("git")
12 .args(args)
13 .current_dir(dir)
14 .env("GIT_CONFIG_NOSYSTEM", "1")
15 .env("GIT_CONFIG_GLOBAL", "/dev/null")
16 .env("HOME", home)
17 .env("GIT_AUTHOR_NAME", "Ada")
18 .env("GIT_AUTHOR_EMAIL", "ada@example.com")
19 .env("GIT_COMMITTER_NAME", "Ada")
20 .env("GIT_COMMITTER_EMAIL", "ada@example.com")
21 .env("GIT_TERMINAL_PROMPT", "0")
22 .output()
23 .expect("git runs");
24 assert!(
25 out.status.success(),
26 "git {args:?} failed:\n{}",
27 String::from_utf8_lossy(&out.stderr)
28 );
29 String::from_utf8_lossy(&out.stdout).trim().to_string()
30}
31
32/// A bare repo at `root/rel` (created with `init --bare`).
33pub fn bare(root: &Path, rel: &str, extra: &[&str]) -> PathBuf {
34 let path = root.join(rel);
35 std::fs::create_dir_all(&path).unwrap();
36 let mut args = vec!["init", "--bare", "-q", "-b", "main"];
37 args.extend_from_slice(extra);
38 git(&path, &args);
39 path
40}
41
42/// A scratch clone of `bare` used to make commits and push them.
43pub struct Work {
44 pub dir: tempfile::TempDir,
45}
46
47impl Work {
48 pub fn clone_of(bare: &Path) -> Work {
49 let dir = tempfile::tempdir().unwrap();
50 git(dir.path(), &["clone", "-q", bare.to_str().unwrap(), "."]);
51 git(dir.path(), &["checkout", "-q", "-B", "main"]);
52 Work { dir }
53 }
54
55 pub fn path(&self) -> &Path {
56 self.dir.path()
57 }
58
59 /// Write `file`, commit it and return the new commit id.
60 pub fn commit(&self, file: &str, content: &str, message: &str) -> String {
61 let path = self.path().join(file);
62 if let Some(parent) = path.parent() {
63 std::fs::create_dir_all(parent).unwrap();
64 }
65 std::fs::write(&path, content).unwrap();
66 git(self.path(), &["add", "-A"]);
67 git(self.path(), &["commit", "-q", "-m", message]);
68 git(self.path(), &["rev-parse", "HEAD"])
69 }
70
71 pub fn push(&self, refspec: &str) {
72 git(self.path(), &["push", "-q", "origin", refspec]);
73 }
74}
75
76/// Bare repo with one commit on `main` containing `files`.
77pub fn repo_with(root: &Path, rel: &str, files: &[(&str, &str)]) -> (PathBuf, String) {
78 let bare = bare(root, rel, &[]);
79 let work = Work::clone_of(&bare);
80 let mut head = String::new();
81 for (name, content) in files {
82 head = work.commit(name, content, &format!("add {name}"));
83 }
84 work.push("main");
85 (bare, head)
86}
87
88/// Ids of interesting objects in [`rich_repo`].
89pub struct Rich {
90 pub bare: PathBuf,
91 pub head: String,
92 pub first: String,
93 pub feature: String,
94}
95
96/// A bare repo exercising the browsing edge cases: nested dirs, a README
97/// with relative links, a binary and an oversize file, a unicode path, a
98/// symlink, a submodule entry, a slashy branch and both kinds of tags.
99pub fn rich_repo(root: &Path, rel: &str) -> Rich {
100 let bare = bare(root, rel, &[]);
101 let work = Work::clone_of(&bare);
102 let w = work.path();
103 let write = |name: &str, data: &[u8]| {
104 let path = w.join(name);
105 std::fs::create_dir_all(path.parent().unwrap()).unwrap();
106 std::fs::write(path, data).unwrap();
107 };
108 write(
109 "src/main.rs",
110 b"// entry\nfn main() {\n println!(\"<hi>\");\n}\n",
111 );
112 let first = work.commit(
113 "README.md",
114 "# Rich\n\nSee [the guide](docs/guide.md) and ![logo](img/logo.png).\n\n\
115 <script>alert(1)</script>\n",
116 "first commit",
117 );
118 write("docs/guide.md", b"# Guide\n\n[Back](../README.md)\n");
119 write("docs/README", b"Plain docs readme <b>not html</b>\n");
120 write("page.html", b"<html><script>alert(1)</script></html>\n");
121 write("img/logo.png", b"\x89PNG\r\n\x1a\n\0\0\0\rIHDR");
122 write("data.bin", b"\0\x01\x02binary\0");
123 write("big.txt", &b"0123456789abcdef\n".repeat(200)); // 3400 bytes
124 write("ünïcode dir/fïle #1.txt", b"unicode\n");
125 std::os::unix::fs::symlink("src/main.rs", w.join("link-to-main")).unwrap();
126 git(w, &["add", "-A"]);
127 git(
128 w,
129 &[
130 "update-index",
131 "--add",
132 "--cacheinfo",
133 &format!("160000,{first},vendor/lib"),
134 ],
135 );
136 git(w, &["commit", "-q", "-m", "add everything"]);
137 let head = git(w, &["rev-parse", "HEAD"]);
138 git(w, &["tag", "v1.0"]);
139 git(
140 w,
141 &["tag", "-a", "v1.1", "-m", "Release 1.1", first.as_str()],
142 );
143 git(w, &["checkout", "-q", "-b", "feature/x"]);
144 let feature = work.commit("only-on-feature.txt", "feature\n", "feature work");
145 git(w, &["checkout", "-q", "main"]);
146 git(w, &["push", "-q", "origin", "main", "feature/x", "--tags"]);
147 Rich {
148 bare,
149 head,
150 first,
151 feature,
152 }
153}