| 1 |
|
| 2 |
|
| 3 |
|
| 4 |
use serde_json::Value; |
| 5 |
use std::path::Path; |
| 6 |
use std::process::Command; |
| 7 |
|
| 8 |
fn oxy(dir: &Path, home: &Path, args: &[&str]) -> (i32, Vec<u8>, String) { |
| 9 |
let out = Command::new(env!("CARGO_BIN_EXE_oxy")) |
| 10 |
.arg("--plain") |
| 11 |
.args(args) |
| 12 |
.current_dir(dir) |
| 13 |
.env("OXY_HOME", home) |
| 14 |
.env("NO_COLOR", "1") |
| 15 |
.output() |
| 16 |
.unwrap(); |
| 17 |
( |
| 18 |
out.status.code().unwrap_or(-1), |
| 19 |
out.stdout, |
| 20 |
String::from_utf8_lossy(&out.stderr).to_string(), |
| 21 |
) |
| 22 |
} |
| 23 |
|
| 24 |
fn json(dir: &Path, home: &Path, args: &[&str]) -> Value { |
| 25 |
let (code, out, err) = oxy(dir, home, args); |
| 26 |
assert!( |
| 27 |
code == 0 || code == 2 || code == 1, |
| 28 |
"oxy {args:?} exited {code}: {err}" |
| 29 |
); |
| 30 |
serde_json::from_slice(&out).unwrap_or_else(|e| { |
| 31 |
panic!( |
| 32 |
"oxy {args:?} is not JSON: {e}\n{}", |
| 33 |
String::from_utf8_lossy(&out) |
| 34 |
) |
| 35 |
}) |
| 36 |
} |
| 37 |
|
| 38 |
fn write(dir: &Path, rel: &str, content: &str) { |
| 39 |
let p = dir.join(rel); |
| 40 |
std::fs::create_dir_all(p.parent().unwrap()).unwrap(); |
| 41 |
std::fs::write(p, content).unwrap(); |
| 42 |
} |
| 43 |
|
| 44 |
#[test] |
| 45 |
fn machine_interface_round_trips() { |
| 46 |
let tmp = tempfile::tempdir().unwrap(); |
| 47 |
let home = tmp.path().join("home"); |
| 48 |
std::fs::create_dir_all(&home).unwrap(); |
| 49 |
let origin = tmp.path().join("origin"); |
| 50 |
assert_eq!( |
| 51 |
oxy(tmp.path(), &home, &["init", origin.to_str().unwrap()]).0, |
| 52 |
0 |
| 53 |
); |
| 54 |
let repo = tmp.path().join("repo"); |
| 55 |
assert_eq!( |
| 56 |
oxy( |
| 57 |
tmp.path(), |
| 58 |
&home, |
| 59 |
&["clone", origin.to_str().unwrap(), repo.to_str().unwrap()] |
| 60 |
) |
| 61 |
.0, |
| 62 |
0 |
| 63 |
); |
| 64 |
let r = repo.as_path(); |
| 65 |
assert_eq!( |
| 66 |
oxy( |
| 67 |
r, |
| 68 |
&home, |
| 69 |
&[ |
| 70 |
"identity", |
| 71 |
"create", |
| 72 |
"--name", |
| 73 |
"Ada", |
| 74 |
"--email", |
| 75 |
"[email protected]" |
| 76 |
] |
| 77 |
) |
| 78 |
.0, |
| 79 |
0 |
| 80 |
); |
| 81 |
assert_eq!( |
| 82 |
oxy(r, &home, &["config", "--user", "core.sign", "change"]).0, |
| 83 |
0 |
| 84 |
); |
| 85 |
|
| 86 |
let v = json(r, &home, &["version", "--json"]); |
| 87 |
assert_eq!(v["protocol"], 1); |
| 88 |
let root = json(r, &home, &["root", "--json"]); |
| 89 |
assert_eq!(root["bookmark"], "main"); |
| 90 |
assert!(root["root"].as_str().unwrap().ends_with("repo")); |
| 91 |
|
| 92 |
write(r, "a.txt", "1\n2\n3\n"); |
| 93 |
write(r, "b.txt", "b\n"); |
| 94 |
let st = json(r, &home, &["status", "--json"]); |
| 95 |
assert_eq!(st["paths"].as_array().unwrap().len(), 2); |
| 96 |
assert_eq!(oxy(r, &home, &["commit", "-m", "base"]).0, 0); |
| 97 |
write(r, "a.txt", "0\n1\n2\n3\n"); |
| 98 |
write(r, "b.txt", "b\nbb\n"); |
| 99 |
write(r, "c.txt", "c\n"); |
| 100 |
|
| 101 |
|
| 102 |
let d = json(r, &home, &["diff", "--json"]); |
| 103 |
let files = d["files"].as_array().unwrap(); |
| 104 |
assert_eq!(files.len(), 3, "{d}"); |
| 105 |
let a = files.iter().find(|f| f["path"] == "a.txt").unwrap(); |
| 106 |
assert_eq!(a["kind"], "modified"); |
| 107 |
assert_eq!(a["hunks"][0]["base_line"], 0); |
| 108 |
assert_eq!(a["hunks"][0]["added"][0], "0\n"); |
| 109 |
let c = files.iter().find(|f| f["path"] == "c.txt").unwrap(); |
| 110 |
assert_eq!(c["kind"], "added"); |
| 111 |
let hunks = json(r, &home, &["hunks", "--json"]); |
| 112 |
let hunks = hunks.as_array().unwrap(); |
| 113 |
assert!(hunks.len() >= 3, "{hunks:?}"); |
| 114 |
let b_hunk = hunks.iter().find(|h| h["path"] == "b.txt").unwrap()["index"] |
| 115 |
.as_u64() |
| 116 |
.unwrap(); |
| 117 |
|
| 118 |
assert_eq!( |
| 119 |
oxy( |
| 120 |
r, |
| 121 |
&home, |
| 122 |
&["commit", "-m", "b only", "--hunks", &b_hunk.to_string()] |
| 123 |
) |
| 124 |
.0, |
| 125 |
0 |
| 126 |
); |
| 127 |
assert_eq!(oxy(r, &home, &["discard", "c.txt"]).0, 0); |
| 128 |
assert!(!r.join("c.txt").exists()); |
| 129 |
let hunks = json(r, &home, &["hunks", "--json"]); |
| 130 |
let a_hunk = hunks |
| 131 |
.as_array() |
| 132 |
.unwrap() |
| 133 |
.iter() |
| 134 |
.find(|h| h["path"] == "a.txt") |
| 135 |
.unwrap()["index"] |
| 136 |
.as_u64() |
| 137 |
.unwrap(); |
| 138 |
assert_eq!( |
| 139 |
oxy(r, &home, &["discard", "--hunks", &a_hunk.to_string()]).0, |
| 140 |
0 |
| 141 |
); |
| 142 |
assert_eq!( |
| 143 |
std::fs::read_to_string(r.join("a.txt")).unwrap(), |
| 144 |
"1\n2\n3\n" |
| 145 |
); |
| 146 |
let st = json(r, &home, &["status", "--json"]); |
| 147 |
assert_eq!(st["clean"], true, "{st}"); |
| 148 |
|
| 149 |
|
| 150 |
let (code, out, _) = oxy(r, &home, &["cat", "b.txt"]); |
| 151 |
assert_eq!((code, out.as_slice()), (0, b"b\nbb\n".as_slice())); |
| 152 |
let (code, out, _) = oxy(r, &home, &["cat", "b.txt", "-r", "main"]); |
| 153 |
assert_eq!((code, out.as_slice()), (0, b"b\nbb\n".as_slice())); |
| 154 |
|
| 155 |
|
| 156 |
let log = json(r, &home, &["log", "--json"]); |
| 157 |
let changes = log["changes"].as_array().unwrap(); |
| 158 |
assert_eq!(changes.len(), 3, "working copy plus two changes: {log}"); |
| 159 |
let head = changes.iter().find(|c| c["message"] == "b only").unwrap(); |
| 160 |
assert_eq!(head["signature"]["status"], "signed"); |
| 161 |
assert_eq!(head["author"], "Ada"); |
| 162 |
let hash = head["hash"].as_str().unwrap().to_string(); |
| 163 |
let by_path = json(r, &home, &["log", "--json", "--path", "b.txt"]); |
| 164 |
assert_eq!(by_path["changes"].as_array().unwrap().len(), 2, "{by_path}"); |
| 165 |
let show = json(r, &home, &["show", "--json", &format!("#{}", &hash[..12])]); |
| 166 |
assert_eq!(show["files"][0]["path"], "b.txt"); |
| 167 |
let verify = json(r, &home, &["verify", "--json"]); |
| 168 |
assert!(verify |
| 169 |
.as_array() |
| 170 |
.unwrap() |
| 171 |
.iter() |
| 172 |
.all(|v| v["status"] == "signed")); |
| 173 |
let ops = json(r, &home, &["op", "log", "--json"]); |
| 174 |
assert!(ops[0]["current"] == true && ops.as_array().unwrap().len() > 3); |
| 175 |
assert_eq!(oxy(r, &home, &["bookmark", "create", "feature"]).0, 0); |
| 176 |
let bms = json(r, &home, &["bookmark", "list", "--json"]); |
| 177 |
assert_eq!( |
| 178 |
bms.as_array() |
| 179 |
.unwrap() |
| 180 |
.iter() |
| 181 |
.map(|b| b["name"].as_str().unwrap()) |
| 182 |
.collect::<Vec<_>>(), |
| 183 |
vec!["feature", "main"] |
| 184 |
); |
| 185 |
let remotes = json(r, &home, &["remote", "list", "--json"]); |
| 186 |
assert_eq!(remotes[0]["name"], "origin"); |
| 187 |
let push = json(r, &home, &["push", "--json"]); |
| 188 |
assert_eq!(push[0]["ok"], true, "{push}"); |
| 189 |
let fetch = json(r, &home, &["fetch", "--json"]); |
| 190 |
assert_eq!(fetch["remote"], "origin"); |
| 191 |
let pull = json(r, &home, &["pull", "--json"]); |
| 192 |
assert_eq!(pull["changed"], false); |
| 193 |
assert_eq!(json(r, &home, &["resolve", "--json"])["conflicted"], false); |
| 194 |
assert!(json(r, &home, &["submodule", "status", "--json"]) |
| 195 |
.as_array() |
| 196 |
.unwrap() |
| 197 |
.is_empty()); |
| 198 |
assert!(json(r, &home, &["bisect", "status", "--json"]).is_null()); |
| 199 |
assert!(json(r, &home, &["policy", "show", "--json"]).is_null()); |
| 200 |
assert_eq!(oxy(r, &home, &["policy", "init"]).0, 0); |
| 201 |
let policy = json(r, &home, &["policy", "show", "--json"]); |
| 202 |
assert_eq!(policy["roots"][0]["name"], "Ada"); |
| 203 |
assert_eq!( |
| 204 |
oxy(r, &home, &["tag", "create", "v1", "-m", "first tag"]).0, |
| 205 |
0 |
| 206 |
); |
| 207 |
let tags = json(r, &home, &["tag", "list", "--json"]); |
| 208 |
assert_eq!( |
| 209 |
(tags[0]["name"].as_str(), tags[0]["message"].as_str()), |
| 210 |
(Some("v1"), Some("first tag")) |
| 211 |
); |
| 212 |
let ws = json(r, &home, &["workspace", "list", "--json"]); |
| 213 |
assert_eq!( |
| 214 |
(ws.as_array().unwrap().len(), ws[0]["current"].as_bool()), |
| 215 |
(1, Some(true)) |
| 216 |
); |
| 217 |
} |
| 218 |
|
| 219 |
|
| 220 |
|
| 221 |
#[test] |
| 222 |
fn commit_from_a_file_and_describe_for_builds() { |
| 223 |
let tmp = tempfile::tempdir().unwrap(); |
| 224 |
let home = tmp.path().join("home"); |
| 225 |
std::fs::create_dir_all(&home).unwrap(); |
| 226 |
let dir = tmp.path().join("r"); |
| 227 |
std::fs::create_dir_all(&dir).unwrap(); |
| 228 |
assert_eq!(oxy(&dir, &home, &["init"]).0, 0); |
| 229 |
write(&dir, "a.txt", "a\n"); |
| 230 |
write( |
| 231 |
tmp.path(), |
| 232 |
"msg.txt", |
| 233 |
"Subject from a file\n\nA body\nof two lines.\n", |
| 234 |
); |
| 235 |
let msg = tmp.path().join("msg.txt"); |
| 236 |
let (code, _, err) = oxy(&dir, &home, &["commit", "-F", msg.to_str().unwrap()]); |
| 237 |
assert_eq!(code, 0, "{err}"); |
| 238 |
let log = json(&dir, &home, &["log", "--json", "-n", "2"]); |
| 239 |
assert_eq!( |
| 240 |
log["changes"][1]["message"], |
| 241 |
"Subject from a file\n\nA body\nof two lines." |
| 242 |
); |
| 243 |
assert_ne!(oxy(&dir, &home, &["commit"]).0, 0, "a message is required"); |
| 244 |
|
| 245 |
|
| 246 |
let d = json(&dir, &home, &["describe", "--json"]); |
| 247 |
assert_eq!( |
| 248 |
(d["tag"].clone(), d["dirty"].clone()), |
| 249 |
(Value::Null, Value::Bool(false)) |
| 250 |
); |
| 251 |
let change = d["change"].as_str().unwrap().to_string(); |
| 252 |
assert_eq!(d["describe"], format!("main-{}", &change[..8])); |
| 253 |
assert_eq!( |
| 254 |
oxy(&dir, &home, &["tag", "create", "v0.1.0", "-m", "first"]).0, |
| 255 |
0 |
| 256 |
); |
| 257 |
let (_, out, _) = oxy(&dir, &home, &["describe"]); |
| 258 |
assert_eq!(String::from_utf8_lossy(&out).trim(), "v0.1.0"); |
| 259 |
let (code, out, err) = oxy(&dir, &home, &["show", "v0.1.0"]); |
| 260 |
assert_eq!(code, 0, "a tag names its change: {err}"); |
| 261 |
assert!(String::from_utf8_lossy(&out).contains("Subject from a file")); |
| 262 |
write(&dir, "b.txt", "b\n"); |
| 263 |
let d = json(&dir, &home, &["describe", "--json"]); |
| 264 |
assert_eq!(d["describe"], "v0.1.0-dirty"); |
| 265 |
assert_eq!(oxy(&dir, &home, &["commit", "-m", "b"]).0, 0); |
| 266 |
write(&dir, "c.txt", "c\n"); |
| 267 |
assert_eq!(oxy(&dir, &home, &["commit", "-m", "c"]).0, 0); |
| 268 |
let d = json(&dir, &home, &["describe", "--json"]); |
| 269 |
let change = d["change"].as_str().unwrap(); |
| 270 |
assert_eq!(d["distance"], 2); |
| 271 |
assert_eq!(d["describe"], format!("v0.1.0-2-{}", &change[..8])); |
| 272 |
} |