Skip to content

Commit 1b957c5

Browse files
committed
Merge branch 'gix-url-fixture-tests'
2 parents 0199927 + c0be6ab commit 1b957c5

File tree

6 files changed

+314
-0
lines changed

6 files changed

+314
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@ target/
66

77
# repositories used for local testing
88
/tests/fixtures/repos
9+
/tests/fixtures/commit-graphs/
910

1011
**/generated-do-not-edit/

Cargo.lock

Lines changed: 22 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gix-url/Cargo.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ rust-version = "1.65"
1212
[lib]
1313
doctest = false
1414

15+
[[test]]
16+
name = "baseline"
17+
harness = false
18+
1519
[features]
1620
## Data structures implement `serde::Serialize` and `serde::Deserialize`.
1721
serde = ["dep:serde", "bstr/serde"]
@@ -28,6 +32,10 @@ home = "0.5.3"
2832

2933
document-features = { version = "0.2.0", optional = true }
3034

35+
[dev-dependencies]
36+
gix-testtools = { path = "../tests/tools" }
37+
libtest-mimic = "0.6.1"
38+
3139
[package.metadata.docs.rs]
3240
all-features = true
3341
features = ["document-features"]

gix-url/tests/baseline/main.rs

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
use bstr::ByteSlice;
2+
use libtest_mimic::{Arguments, Failed, Trial};
3+
4+
fn main() {
5+
// We do not need to set this hook back to its default, because this test gets compiled to its
6+
// own binary and does therefore not interfere with other tests.
7+
std::panic::set_hook(Box::new(|_| {}));
8+
9+
let args = Arguments::from_args();
10+
let tests = get_baseline_test_cases();
11+
12+
libtest_mimic::run(&args, tests).exit();
13+
}
14+
15+
fn get_baseline_test_cases() -> Vec<Trial> {
16+
baseline::URLS
17+
.iter()
18+
.map(|(url, expected)| {
19+
Trial::test(
20+
format!("baseline {}", url.to_str().expect("url is valid utf-8")),
21+
move || {
22+
std::panic::catch_unwind(|| {
23+
assert_urls_equal(expected, &gix_url::parse(url).expect("valid urls can be parsed"))
24+
})
25+
.map_err(|err| {
26+
// Succeeds whenever `panic!` was given a string literal (for example if
27+
// `assert!` is given a string literal).
28+
match err.downcast_ref::<&str>() {
29+
Some(panic_message) => panic_message.into(),
30+
None => {
31+
// Succeeds whenever `panic!` was given an owned String (for
32+
// example when using the `format!` syntax and always for
33+
// `assert_*!` macros).
34+
match err.downcast_ref::<String>() {
35+
Some(panic_message) => panic_message.into(),
36+
None => Failed::without_message(),
37+
}
38+
}
39+
}
40+
})
41+
},
42+
)
43+
.with_ignored_flag(true /* currently most of these fail */)
44+
})
45+
.collect::<_>()
46+
}
47+
48+
fn assert_urls_equal(expected: &baseline::GitDiagUrl<'_>, actual: &gix_url::Url) {
49+
assert_eq!(
50+
gix_url::Scheme::from(expected.protocol.to_str().unwrap()),
51+
actual.scheme
52+
);
53+
54+
match expected.host {
55+
baseline::GitDiagHost::NonSsh { host_and_port } => match host_and_port {
56+
Some(host_and_port) => {
57+
assert!(actual.host().is_some());
58+
59+
let mut gix_host_and_port = String::with_capacity(host_and_port.len());
60+
61+
if let Some(user) = actual.user() {
62+
gix_host_and_port.push_str(user);
63+
gix_host_and_port.push('@');
64+
}
65+
66+
gix_host_and_port.push_str(actual.host().unwrap());
67+
68+
if let Some(port) = actual.port {
69+
gix_host_and_port.push(':');
70+
gix_host_and_port.push_str(&port.to_string());
71+
}
72+
73+
assert_eq!(host_and_port, gix_host_and_port);
74+
}
75+
None => {
76+
assert!(actual.host().is_none());
77+
assert!(actual.port.is_none());
78+
}
79+
},
80+
baseline::GitDiagHost::Ssh { user_and_host, port } => {
81+
match user_and_host {
82+
Some(user_and_host) => {
83+
assert!(actual.host().is_some());
84+
85+
let mut gix_user_and_host = String::with_capacity(user_and_host.len());
86+
if let Some(user) = actual.user() {
87+
gix_user_and_host.push_str(user);
88+
gix_user_and_host.push('@');
89+
}
90+
gix_user_and_host.push_str(actual.host().unwrap());
91+
92+
assert_eq!(user_and_host, gix_user_and_host);
93+
}
94+
None => {
95+
assert!(actual.host().is_none());
96+
assert!(actual.user().is_none());
97+
}
98+
}
99+
match port {
100+
Some(port) => {
101+
assert!(actual.port.is_some());
102+
assert_eq!(port, actual.port.unwrap().to_string());
103+
}
104+
None => {
105+
assert!(actual.port.is_none());
106+
}
107+
}
108+
}
109+
}
110+
111+
match expected.path {
112+
Some(path) => {
113+
assert_eq!(path, actual.path);
114+
}
115+
None => {
116+
// I guess? This case does not happen a single time in the current fixtures...
117+
assert!(actual.path.is_empty());
118+
}
119+
}
120+
}
121+
122+
mod baseline {
123+
use bstr::{BStr, BString, ByteSlice};
124+
use gix_testtools::once_cell::sync::Lazy;
125+
126+
static BASELINE: Lazy<BString> = Lazy::new(|| {
127+
let base = gix_testtools::scripted_fixture_read_only("make_baseline.sh").unwrap();
128+
BString::from(std::fs::read(base.join("git-baseline.generic")).expect("fixture file exists"))
129+
});
130+
131+
pub static URLS: Lazy<Vec<(&'static BStr, GitDiagUrl<'static>)>> = Lazy::new(|| {
132+
let mut out = Vec::new();
133+
134+
let url_block = BASELINE
135+
.split(|c| c == &b';')
136+
.filter(|url| !url.is_empty())
137+
.map(ByteSlice::trim);
138+
139+
for block in url_block {
140+
let (url, diag_url) = GitDiagUrl::parse(block.as_bstr());
141+
out.push((url, diag_url));
142+
}
143+
out
144+
});
145+
146+
#[derive(Debug)]
147+
pub struct GitDiagUrl<'a> {
148+
pub protocol: &'a BStr,
149+
pub host: GitDiagHost<'a>,
150+
pub path: Option<&'a BStr>,
151+
}
152+
153+
impl GitDiagUrl<'_> {
154+
/// Parses the given string into a [GitDiagUrl] according to the format
155+
/// specified in [Git's `connect.c`][git_src].
156+
///
157+
/// [git_src]: https://github.com/git/git/blob/master/connect.c#L1415
158+
fn parse(diag_url: &BStr) -> (&'_ BStr, GitDiagUrl<'_>) {
159+
let mut lines = diag_url.lines().map(ByteSlice::trim);
160+
let mut next_attr = |name: &str| {
161+
lines
162+
.next()
163+
.expect("well-known format")
164+
.strip_prefix(format!("Diag: {name}=").as_bytes())
165+
.expect("attribute is at the correct location")
166+
.as_bstr()
167+
};
168+
169+
let url = next_attr("url");
170+
let protocol = next_attr("protocol");
171+
172+
let host = if protocol == "ssh" {
173+
let user_and_host = next_attr("userandhost");
174+
let port = next_attr("port");
175+
GitDiagHost::Ssh {
176+
user_and_host: if user_and_host == "NULL" {
177+
None
178+
} else {
179+
Some(user_and_host)
180+
},
181+
port: if port == "NONE" { None } else { Some(port) },
182+
}
183+
} else {
184+
let host_and_port = next_attr("hostandport");
185+
GitDiagHost::NonSsh {
186+
host_and_port: if host_and_port == "NULL" {
187+
None
188+
} else {
189+
Some(host_and_port)
190+
},
191+
}
192+
};
193+
194+
let path = next_attr("path");
195+
assert!(lines.next().is_none(), "we consume everything");
196+
(
197+
url,
198+
GitDiagUrl {
199+
protocol,
200+
host,
201+
path: if path == "NULL" { None } else { Some(path) },
202+
},
203+
)
204+
}
205+
}
206+
207+
#[derive(Debug)]
208+
pub enum GitDiagHost<'a> {
209+
NonSsh {
210+
host_and_port: Option<&'a BStr>,
211+
},
212+
Ssh {
213+
user_and_host: Option<&'a BStr>,
214+
port: Option<&'a BStr>,
215+
},
216+
}
217+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:b06970644787b54f3e74a84dfab2cdaaefe51f8a51bbe59625713ac6442b8122
3+
size 1648
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#!/bin/bash
2+
set -eu -o pipefail
3+
4+
# list of urls that should be tested for all platforms
5+
tests=()
6+
# urls only intended for testing on Unix platforms
7+
tests_unix=()
8+
# urls only intended for testing on Windows
9+
tests_windows=()
10+
11+
# The contents and structure of this loop are a adaption
12+
# from git's own test suite (t/t5500-fetch-pack.sh).
13+
# Please do not change this loop and instead add additional
14+
# test cases at the bottom of this file.
15+
for path in "repo" "re:po" "re/po"; do
16+
for protocol in "ssh+git" "git+ssh" "git" "ssh"; do
17+
for host in "host" "user@host" "user@[::1]" "user@::1"; do
18+
for port_separator in "" ":"; do
19+
tests+=("$protocol://$host$port_separator/$path")
20+
21+
tests+=("$protocol://$host$port_separator/~$path")
22+
done
23+
done
24+
for host in "host" "User@host" "User@[::1]"; do
25+
tests+=("$protocol://$host:22/$path")
26+
done
27+
done
28+
for protocol in "file"; do
29+
tests_unix+=("$protocol://$host/$path")
30+
31+
tests_windows+=("$protocol://$host/$path")
32+
tests_windows+=("$protocol:///$path")
33+
34+
tests_unix+=("$protocol://$host/~$path")
35+
tests_windows+=("$protocol://$host/~$path")
36+
done
37+
for host in "nohost" "nohost:12" "[::1]" "[::1]:23" "[" "[:aa"; do
38+
tests+=("./$host:$path")
39+
tests+=("./$protocol:$host/~$path")
40+
done
41+
protocol="ssh"
42+
for host in "host" "[::1]"; do
43+
tests+=("$host:$path")
44+
45+
tests+=("$host:/~$path")
46+
done
47+
done
48+
49+
# These two test cases are from git's test suite as well.
50+
tests_windows+=("file://c:/repo")
51+
tests_windows+=("c:repo")
52+
53+
tests_unix+=("${tests[@]}")
54+
tests_windows+=("${tests[@]}")
55+
56+
for url in "${tests[@]}"
57+
do
58+
echo ";" # there are no `;` in the tested urls
59+
git fetch-pack --diag-url "$url"
60+
done >git-baseline.generic
61+
62+
# TODO: testing of platform specific behavior
63+

0 commit comments

Comments
 (0)