summaryrefslogtreecommitdiffstats
path: root/vendor/gix-negotiate/tests
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gix-negotiate/tests')
-rw-r--r--vendor/gix-negotiate/tests/baseline/mod.rs226
-rw-r--r--vendor/gix-negotiate/tests/fixtures/generated-archives/make_repos.tar.xzbin0 -> 92828 bytes
-rw-r--r--vendor/gix-negotiate/tests/fixtures/make_repos.sh146
-rw-r--r--vendor/gix-negotiate/tests/negotiate.rs46
4 files changed, 418 insertions, 0 deletions
diff --git a/vendor/gix-negotiate/tests/baseline/mod.rs b/vendor/gix-negotiate/tests/baseline/mod.rs
new file mode 100644
index 000000000..e3e209bab
--- /dev/null
+++ b/vendor/gix-negotiate/tests/baseline/mod.rs
@@ -0,0 +1,226 @@
+use std::cell::RefCell;
+
+use gix_negotiate::Algorithm;
+use gix_object::{bstr, bstr::ByteSlice};
+use gix_odb::{Find, FindExt};
+use gix_ref::{file::ReferenceExt, store::WriteReflog};
+
+#[test]
+fn run() -> crate::Result {
+ let root = gix_testtools::scripted_fixture_read_only("make_repos.sh")?;
+ for case in [
+ "no_parents",
+ "clock_skew",
+ "two_colliding_skips",
+ "multi_round",
+ "advertisement_as_filter",
+ ] {
+ let base = root.join(case);
+
+ for (algo_name, algo) in [
+ ("noop", Algorithm::Noop),
+ ("consecutive", Algorithm::Consecutive),
+ ("skipping", Algorithm::Skipping),
+ ] {
+ let obj_buf = RefCell::new(Vec::new());
+ let buf = std::fs::read(base.join(format!("baseline.{algo_name}")))?;
+ let store = gix_odb::at(base.join("client").join(".git/objects"))?;
+ let refs = gix_ref::file::Store::at(
+ base.join("client").join(".git"),
+ WriteReflog::Disable,
+ gix_hash::Kind::Sha1,
+ );
+ let lookup_names = |names: &[&str]| -> Vec<gix_hash::ObjectId> {
+ names
+ .iter()
+ .filter_map(|name| {
+ refs.try_find(*name).expect("one tag per commit").map(|mut r| {
+ r.peel_to_id_in_place(&refs, |id, buf| {
+ store.try_find(id, buf).map(|d| d.map(|d| (d.kind, d.data)))
+ })
+ .expect("works");
+ r.target.into_id()
+ })
+ })
+ .collect()
+ };
+ let message = |id| {
+ store
+ .find_commit(id, obj_buf.borrow_mut().as_mut())
+ .expect("present")
+ .message
+ .trim()
+ .as_bstr()
+ .to_owned()
+ };
+
+ let debug = false;
+ for use_cache in [false, true] {
+ let cache = use_cache
+ .then(|| gix_commitgraph::at(store.store_ref().path().join("info")).ok())
+ .flatten();
+ let mut graph = gix_revision::Graph::new(
+ |id, buf| {
+ store
+ .try_find(id, buf)
+ .map(|r| r.and_then(|d| d.try_into_commit_iter()))
+ },
+ cache,
+ );
+ let mut negotiator = algo.into_negotiator();
+ if debug {
+ eprintln!("ALGO {algo_name} CASE {case}");
+ }
+ // // In --negotiate-only mode, which seems to be the only thing that's working after trying --dry-run, we unfortunately
+ // // don't get to see what happens if known-common commits are added as git itself doesn't do that in this mode
+ // // for some reason.
+ // for common in lookup_names(&["origin/main"]) {
+ // eprintln!("COMMON {name} {common}", name = message(common));
+ // negotiator.known_common(common)?;
+ // }
+ for tip in lookup_names(&["HEAD"]).into_iter().chain(
+ refs.iter()?
+ .prefixed("refs/heads")?
+ .filter_map(Result::ok)
+ .map(|r| r.target.into_id()),
+ ) {
+ if debug {
+ eprintln!("TIP {name} {tip}", name = message(tip));
+ }
+ negotiator.add_tip(tip, &mut graph)?;
+ }
+ for (round, Round { mut haves, common }) in ParseRounds::new(buf.lines()).enumerate() {
+ if algo == Algorithm::Skipping {
+ if case == "clock_skew" {
+ // Here for some reason the prio-queue of git manages to not sort the parent of C2, which is in the future, to be
+ // ahead of old4 that is in the past. In the git version of this test, they say to expect exactly this sequence
+ // as well even though it's not actually happening (but that they can't see due to the way they are testing).
+ haves = lookup_names(&["c2", "c1", "old4", "old2", "old1"]);
+ } else if case == "two_colliding_skips" {
+ // The same thing, we actually get exactly the right order, whereas git for some reason doesn't.
+ // This is the order expected in the git tests.
+ haves = lookup_names(&["c5side", "c11", "c9", "c6", "c1"]);
+ } else if case == "multi_round" && round == 1 {
+ // Here, probably also because of priority queue quirks, `git` emits the commits out of order, with only one
+ // branch, b5 I think, being out of place. This list puts the expectation in the right order, which is ordered
+ // by commit date.
+ haves = lookup_names(&[
+ "b8.c14", "b7.c14", "b6.c14", "b5.c14", "b4.c14", "b3.c14", "b2.c14", "b8.c9", "b7.c9",
+ "b6.c9", "b5.c9", "b4.c9", "b3.c9", "b2.c9", "b8.c1", "b7.c1", "b6.c1", "b5.c1",
+ "b4.c1", "b3.c1", "b2.c1", "b8.c0", "b7.c0", "b6.c0", "b5.c0", "b4.c0", "b3.c0",
+ "b2.c0",
+ ]);
+ } else if case == "advertisement_as_filter" {
+ haves = lookup_names(&["c2side", "c5", "origin/main"])
+ .into_iter()
+ .chain(Some(
+ gix_hash::ObjectId::from_hex(b"f36cefa0be2ac180d360a54b1cc4214985cea60a").unwrap(),
+ ))
+ .collect();
+ }
+ }
+ for have in haves {
+ let actual = negotiator.next_have(&mut graph).unwrap_or_else(|| {
+ panic!("{algo_name}:cache={use_cache}: one have per baseline: {have} missing or in wrong order", have = message(have))
+ })?;
+ assert_eq!(
+ actual,
+ have,
+ "{algo_name}:cache={use_cache}: order and commit matches exactly, wanted {expected}, got {actual}, commits left: {:?}",
+ std::iter::from_fn(|| negotiator.next_have(&mut graph)).map(|id| message(id.unwrap())).collect::<Vec<_>>(),
+ actual = message(actual),
+ expected = message(have)
+ );
+ if debug {
+ eprintln!("have {}", message(actual));
+ }
+ }
+ for common_revision in common {
+ if debug {
+ eprintln!("ACK {}", message(common_revision));
+ }
+ negotiator.in_common_with_remote(common_revision, &mut graph)?;
+ }
+ }
+ assert!(
+ negotiator.next_have(&mut graph).is_none(),
+ "{algo_name}:cache={use_cache}: negotiator should be depleted after all recorded baseline rounds"
+ );
+ }
+ }
+ }
+ Ok(())
+}
+
+struct ParseRounds<'a> {
+ lines: bstr::Lines<'a>,
+}
+
+impl<'a> ParseRounds<'a> {
+ pub fn new(mut lines: bstr::Lines<'a>) -> Self {
+ parse::command(&mut lines, parse::Command::Incoming).expect("handshake");
+ Self { lines }
+ }
+}
+
+impl<'a> Iterator for ParseRounds<'a> {
+ type Item = Round;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ let haves = parse::object_ids("have", parse::command(&mut self.lines, parse::Command::Outgoing)?);
+ let common = parse::object_ids("ACK", parse::command(&mut self.lines, parse::Command::Incoming)?);
+ if haves.is_empty() {
+ assert!(common.is_empty(), "cannot ack what's not there");
+ return None;
+ }
+ Round { haves, common }.into()
+ }
+}
+
+struct Round {
+ pub haves: Vec<gix_hash::ObjectId>,
+ pub common: Vec<gix_hash::ObjectId>,
+}
+
+mod parse {
+ use gix_object::{
+ bstr,
+ bstr::{BStr, ByteSlice},
+ };
+
+ #[derive(Debug, Eq, PartialEq, Copy, Clone)]
+ pub enum Command {
+ Incoming,
+ Outgoing,
+ }
+
+ pub fn object_ids(prefix: &str, lines: impl IntoIterator<Item = impl AsRef<[u8]>>) -> Vec<gix_hash::ObjectId> {
+ lines
+ .into_iter()
+ .filter_map(|line| {
+ line.as_ref()
+ .strip_prefix(prefix.as_bytes())
+ .map(|id| gix_hash::ObjectId::from_hex(id.trim()).expect("valid hash"))
+ })
+ .collect()
+ }
+
+ pub fn command<'a>(lines: &mut bstr::Lines<'a>, wanted: Command) -> Option<Vec<&'a BStr>> {
+ let mut out = Vec::new();
+ for line in lines {
+ let pos = line.find(b"fetch").expect("fetch token");
+ let line_mode = match &line[pos + 5..][..2] {
+ b"< " => Command::Incoming,
+ b"> " => Command::Outgoing,
+ invalid => unreachable!("invalid fetch token: {:?}", invalid.as_bstr()),
+ };
+ assert_eq!(line_mode, wanted, "command with unexpected mode");
+ let line = line[pos + 7..].as_bstr();
+ if line == "0000" {
+ break;
+ }
+ out.push(line);
+ }
+ (!out.is_empty()).then_some(out)
+ }
+}
diff --git a/vendor/gix-negotiate/tests/fixtures/generated-archives/make_repos.tar.xz b/vendor/gix-negotiate/tests/fixtures/generated-archives/make_repos.tar.xz
new file mode 100644
index 000000000..93e836931
--- /dev/null
+++ b/vendor/gix-negotiate/tests/fixtures/generated-archives/make_repos.tar.xz
Binary files differ
diff --git a/vendor/gix-negotiate/tests/fixtures/make_repos.sh b/vendor/gix-negotiate/tests/fixtures/make_repos.sh
new file mode 100644
index 000000000..b1b96527d
--- /dev/null
+++ b/vendor/gix-negotiate/tests/fixtures/make_repos.sh
@@ -0,0 +1,146 @@
+#!/bin/bash
+set -eu -o pipefail
+
+function tick () {
+ if test -z "${tick+set}"
+ then
+ tick=1112911993
+ else
+ tick=$(($tick + 60))
+ fi
+ GIT_COMMITTER_DATE="$tick -0700"
+ GIT_AUTHOR_DATE="$tick -0700"
+ export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
+}
+
+tick
+function commit() {
+ local message=${1:?first argument is the commit message}
+ local file="$message.t"
+ echo "$1" > "$file"
+ git add -- "$file"
+ tick
+ git commit -m "$message"
+ git tag "$message"
+}
+
+function negotiation_tips () {
+ local tips=""
+ for arg in "$@"; do
+ tips+=" --negotiation-tip=$arg"
+ done
+ echo "$tips"
+}
+
+function trace_fetch_baseline () {
+ local remote="${1:?need remote url}"; shift
+ git -C client commit-graph write --no-progress --reachable
+ git -C client repack -adq
+
+ for algo in noop consecutive skipping; do
+ GIT_TRACE_PACKET="$PWD/baseline.$algo" \
+ git -C client -c fetch.negotiationAlgorithm="$algo" fetch --negotiate-only $(negotiation_tips "$@") \
+ --upload-pack 'unset GIT_TRACE_PACKET; git-upload-pack' \
+ "$remote" || :
+ done
+}
+
+
+(mkdir no_parents && cd no_parents
+ git init -q server && cd server
+ commit to_fetch
+ cd ..
+
+ (git init -q client && cd client
+ for i in $(seq 7); do
+ commit c$i
+ done
+ )
+
+ trace_fetch_baseline file://$PWD/server main
+)
+
+(mkdir two_colliding_skips && cd two_colliding_skips
+ git init -q server && cd server
+ commit to_fetch
+ cd ..
+
+ (git init -q client && cd client
+ for i in $(seq 11); do
+ commit c$i
+ done
+ git checkout c5
+ commit c5side
+ )
+
+ trace_fetch_baseline file://$PWD/server HEAD main
+)
+
+(mkdir advertisement_as_filter && cd advertisement_as_filter
+ git init -q server && cd server
+ commit c1
+ commit c2
+ commit c3
+ git tag -d c1 c2 c3
+ cd ..
+ git clone server client && cd client
+ commit c4
+ commit c5
+ git checkout c4^^
+ commit c2side
+ cd ..
+ (cd server
+ git checkout --orphan anotherbranch
+ commit to_fetch
+ )
+
+ trace_fetch_baseline origin HEAD main
+)
+
+
+(mkdir multi_round && cd multi_round
+ git init -q server && cd server
+ commit to_fetch
+ cd ..
+
+ git init -q client && cd client
+ for i in $(seq 8); do
+ git checkout --orphan b$i &&
+ commit b$i.c0
+ done
+
+ for j in $(seq 19); do
+ for i in $(seq 8); do
+ git checkout b$i &&
+ commit b$i.c$j
+ done
+ done
+ cd ..
+ (cd server
+ git fetch --no-tags "$PWD/../client" b1:refs/heads/b1
+ git checkout b1
+ commit commit-on-b1
+ )
+ trace_fetch_baseline file://$PWD/server $(ls client/.git/refs/heads | sort)
+)
+
+(mkdir clock_skew && cd clock_skew
+ git init -q server && cd server
+ commit to_fetch
+ cd ..
+
+ (git init -q client && cd client
+ tick=2000000000
+ commit c1
+ commit c2
+
+ tick=1000000000
+ git checkout c1
+ commit old1
+ commit old2
+ commit old3
+ commit old4
+ )
+
+ trace_fetch_baseline file://$PWD/server HEAD main
+)
diff --git a/vendor/gix-negotiate/tests/negotiate.rs b/vendor/gix-negotiate/tests/negotiate.rs
new file mode 100644
index 000000000..357b54d61
--- /dev/null
+++ b/vendor/gix-negotiate/tests/negotiate.rs
@@ -0,0 +1,46 @@
+use gix_testtools::Result;
+
+mod window_size {
+ use gix_negotiate::window_size;
+
+ #[test]
+ fn initial_value_without_previous_window_size() {
+ assert_eq!(window_size(false, None), 16);
+ assert_eq!(window_size(true, None), 16);
+ }
+
+ #[test]
+ fn transport_is_stateless() {
+ let mut ws = window_size(true, None);
+ for expected in [32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 18022, 19824] {
+ ws = window_size(true, ws);
+ assert_eq!(ws, expected);
+ }
+ }
+
+ #[test]
+ fn transport_is_not_stateless() {
+ let mut ws = window_size(false, None);
+ for expected in [32, 64, 96] {
+ ws = window_size(false, ws);
+ assert_eq!(ws, expected);
+ }
+
+ let mut ws = 4;
+ for expected in [8, 16, 32, 64, 96] {
+ ws = window_size(false, ws);
+ assert_eq!(ws, expected);
+ }
+ }
+}
+
+mod baseline;
+
+#[test]
+fn size_of_entry() {
+ assert_eq!(
+ std::mem::size_of::<gix_revision::graph::Commit<gix_negotiate::Metadata>>(),
+ 56,
+ "we may keep a lot of these, so let's not let them grow unnoticed"
+ );
+}