History

[git-protocol] move everything into `blocking_io` for later translation…

…into async-io, as usual.

Sebastian Thiel committed 5 years ago

Commit fa033741 parent 0d39b5d12 files · +295 −254Browse files

Showing the changes to git-protocol/src/fetch/blocking_io/tests (4 of 12 files). Show all

Unified · Split

4 files
  1. git-protocol/src/fetch/blocking_io/tests/arguments.rs +236 −0
  2. git-protocol/src/fetch/blocking_io/tests/command.rs +165 −0
  3. git-protocol/src/fetch/blocking_io/tests/mod.rs +3 −0
  4. git-protocol/src/fetch/blocking_io/tests/refs.rs +108 −0
git-protocol/src/fetch/blocking_io/tests/arguments.rs+236−0
@@ -0,0 +1,236 @@
1+use crate::fetch;
2+use bstr::ByteSlice;
3+use git_transport::{
4+ client,
5+ client::{Error, Identity, MessageKind, RequestWriter, SetServiceResponse, WriteMode},
6+ Protocol, Service,
7+};
8+use std::io;
9+
10+fn arguments_v1(features: impl IntoIterator<Item = &'static str>) -> fetch::Arguments {
11+ fetch::Arguments::new(Protocol::V1, features.into_iter().map(|n| (n, None)).collect())
12+}
13+
14+fn arguments_v2(features: impl IntoIterator<Item = &'static str>) -> fetch::Arguments {
15+ fetch::Arguments::new(Protocol::V2, features.into_iter().map(|n| (n, None)).collect())
16+}
17+
18+struct Transport<T: client::Transport> {
19+ inner: T,
20+ stateful: bool,
21+}
22+
23+impl<T: client::Transport> client::Transport for Transport<T> {
24+ fn handshake(&mut self, service: Service) -> Result<SetServiceResponse<'_>, Error> {
25+ self.inner.handshake(service)
26+ }
27+
28+ fn set_identity(&mut self, identity: Identity) -> Result<(), Error> {
29+ self.inner.set_identity(identity)
30+ }
31+
32+ fn request(&mut self, write_mode: WriteMode, on_into_read: MessageKind) -> Result<RequestWriter<'_>, Error> {
33+ self.inner.request(write_mode, on_into_read)
34+ }
35+
36+ fn close(&mut self) -> Result<(), Error> {
37+ self.inner.close()
38+ }
39+
40+ fn to_url(&self) -> String {
41+ self.inner.to_url()
42+ }
43+
44+ fn desired_protocol_version(&self) -> Protocol {
45+ self.inner.desired_protocol_version()
46+ }
47+
48+ fn is_stateful(&self) -> bool {
49+ self.stateful
50+ }
51+}
52+
53+fn transport(
54+ out: &mut Vec<u8>,
55+ stateful: bool,
56+) -> Transport<git_transport::client::git::Connection<io::Cursor<Vec<u8>>, &mut Vec<u8>>> {
57+ Transport {
58+ inner: git_transport::client::git::Connection::new(
59+ io::Cursor::new(Vec::new()),
60+ out,
61+ Protocol::V1, // does not matter
62+ b"does/not/matter".as_bstr().to_owned(),
63+ None::<(&str, _)>,
64+ git_transport::client::git::ConnectMode::Process, // avoid header to be sent
65+ ),
66+ stateful,
67+ }
68+}
69+
70+fn id(hex: &str) -> git_hash::ObjectId {
71+ git_hash::ObjectId::from_hex(hex.as_bytes()).expect("expect valid hex id")
72+}
73+
74+mod v1 {
75+ use crate::fetch::blocking_io::tests::arguments::{arguments_v1, id, transport};
76+ use bstr::ByteSlice;
77+
78+ #[test]
79+ fn haves_and_wants_for_clone() {
80+ let mut out = Vec::new();
81+ let mut t = transport(&mut out, true);
82+ let mut arguments = arguments_v1(["feature-a", "feature-b"].iter().cloned());
83+
84+ arguments.want(id("7b333369de1221f9bfbbe03a3a13e9a09bc1c907"));
85+ arguments.want(id("ff333369de1221f9bfbbe03a3a13e9a09bc1ffff"));
86+ arguments.send(&mut t, true).expect("sending to buffer to work");
87+ assert_eq!(
88+ out.as_bstr(),
89+ b"0046want 7b333369de1221f9bfbbe03a3a13e9a09bc1c907 feature-a feature-b
90+0032want ff333369de1221f9bfbbe03a3a13e9a09bc1ffff
91+00000009done
92+"
93+ .as_bstr()
94+ );
95+ }
96+
97+ #[test]
98+ fn haves_and_wants_for_fetch_stateless() {
99+ let mut out = Vec::new();
100+ let mut t = transport(&mut out, false);
101+ let mut arguments = arguments_v1(["feature-a", "shallow", "deepen-since", "deepen-not"].iter().copied());
102+
103+ arguments.deepen(1);
104+ arguments.shallow(id("7b333369de1221f9bfbbe03a3a13e9a09bc1c9ff"));
105+ arguments.want(id("7b333369de1221f9bfbbe03a3a13e9a09bc1c907"));
106+ arguments.deepen_since(12345);
107+ arguments.deepen_not("refs/heads/main".into());
108+ arguments.have(id("0000000000000000000000000000000000000000"));
109+ arguments.send(&mut t, false).expect("sending to buffer to work");
110+
111+ arguments.have(id("1111111111111111111111111111111111111111"));
112+ arguments.send(&mut t, true).expect("sending to buffer to work");
113+ assert_eq!(
114+ out.as_bstr(),
115+ b"005cwant 7b333369de1221f9bfbbe03a3a13e9a09bc1c907 feature-a shallow deepen-since deepen-not
116+0035shallow 7b333369de1221f9bfbbe03a3a13e9a09bc1c9ff
117+000ddeepen 1
118+0017deepen-since 12345
119+001fdeepen-not refs/heads/main
120+00000032have 0000000000000000000000000000000000000000
121+0000005cwant 7b333369de1221f9bfbbe03a3a13e9a09bc1c907 feature-a shallow deepen-since deepen-not
122+0035shallow 7b333369de1221f9bfbbe03a3a13e9a09bc1c9ff
123+000ddeepen 1
124+0017deepen-since 12345
125+001fdeepen-not refs/heads/main
126+00000032have 1111111111111111111111111111111111111111
127+0009done
128+"
129+ .as_bstr()
130+ );
131+ }
132+
133+ #[test]
134+ fn haves_and_wants_for_fetch_stateful() {
135+ let mut out = Vec::new();
136+ let mut t = transport(&mut out, true);
137+ let mut arguments = arguments_v1(["feature-a", "shallow"].iter().copied());
138+
139+ arguments.deepen(1);
140+ arguments.want(id("7b333369de1221f9bfbbe03a3a13e9a09bc1c907"));
141+ arguments.have(id("0000000000000000000000000000000000000000"));
142+ arguments.send(&mut t, false).expect("sending to buffer to work");
143+
144+ arguments.have(id("1111111111111111111111111111111111111111"));
145+ arguments.send(&mut t, true).expect("sending to buffer to work");
146+ assert_eq!(
147+ out.as_bstr(),
148+ b"0044want 7b333369de1221f9bfbbe03a3a13e9a09bc1c907 feature-a shallow
149+000ddeepen 1
150+00000032have 0000000000000000000000000000000000000000
151+00000032have 1111111111111111111111111111111111111111
152+0009done
153+"
154+ .as_bstr()
155+ );
156+ }
157+}
158+
159+mod v2 {
160+ use crate::fetch::blocking_io::tests::arguments::{arguments_v2, id, transport};
161+ use bstr::ByteSlice;
162+
163+ #[test]
164+ fn haves_and_wants_for_clone_stateful() {
165+ let mut out = Vec::new();
166+ let mut t = transport(&mut out, true);
167+ let mut arguments = arguments_v2(["feature-a", "shallow"].iter().copied());
168+
169+ arguments.deepen(1);
170+ arguments.deepen_relative();
171+ arguments.want(id("7b333369de1221f9bfbbe03a3a13e9a09bc1c907"));
172+ arguments.want(id("ff333369de1221f9bfbbe03a3a13e9a09bc1ffff"));
173+ arguments.send(&mut t, true).expect("sending to buffer to work");
174+ assert_eq!(
175+ out.as_bstr(),
176+ b"0012command=fetch
177+0001000ethin-pack
178+0010include-tag
179+000eofs-delta
180+000ddeepen 1
181+0014deepen-relative
182+0032want 7b333369de1221f9bfbbe03a3a13e9a09bc1c907
183+0032want ff333369de1221f9bfbbe03a3a13e9a09bc1ffff
184+0009done
185+0000"
186+ .as_bstr(), "we filter features/capabilities without value as these apparently sholdn't be listed (remote dies otherwise)"
187+ );
188+ }
189+
190+ #[test]
191+ fn haves_and_wants_for_fetch_stateless_and_stateful() {
192+ for is_stateful in &[false, true] {
193+ let mut out = Vec::new();
194+ let mut t = transport(&mut out, *is_stateful);
195+ let mut arguments = arguments_v2(Some("shallow"));
196+
197+ arguments.deepen(1);
198+ arguments.deepen_since(12345);
199+ arguments.shallow(id("7b333369de1221f9bfbbe03a3a13e9a09bc1c9ff"));
200+ arguments.want(id("7b333369de1221f9bfbbe03a3a13e9a09bc1c907"));
201+ arguments.deepen_not("refs/heads/main".into());
202+ arguments.have(id("0000000000000000000000000000000000000000"));
203+ arguments.send(&mut t, false).expect("sending to buffer to work");
204+
205+ arguments.have(id("1111111111111111111111111111111111111111"));
206+ arguments.send(&mut t, true).expect("sending to buffer to work");
207+ assert_eq!(
208+ out.as_bstr(),
209+ b"0012command=fetch
210+0001000ethin-pack
211+0010include-tag
212+000eofs-delta
213+000ddeepen 1
214+0017deepen-since 12345
215+0035shallow 7b333369de1221f9bfbbe03a3a13e9a09bc1c9ff
216+0032want 7b333369de1221f9bfbbe03a3a13e9a09bc1c907
217+001fdeepen-not refs/heads/main
218+0032have 0000000000000000000000000000000000000000
219+00000012command=fetch
220+0001000ethin-pack
221+0010include-tag
222+000eofs-delta
223+000ddeepen 1
224+0017deepen-since 12345
225+0035shallow 7b333369de1221f9bfbbe03a3a13e9a09bc1c9ff
226+0032want 7b333369de1221f9bfbbe03a3a13e9a09bc1c907
227+001fdeepen-not refs/heads/main
228+0032have 1111111111111111111111111111111111111111
229+0009done
230+0000"
231+ .as_bstr(),
232+ "V2 is stateless by default, so it repeats all but 'haves' in each request"
233+ );
234+ }
235+ }
236+}
git-protocol/src/fetch/blocking_io/tests/command.rs+165−0
@@ -0,0 +1,165 @@
1+mod v1 {
2+ fn capabilities(input: &str) -> git_transport::client::Capabilities {
3+ git_transport::client::Capabilities::from_bytes(format!("\0{}", input).as_bytes())
4+ .expect("valid input capabilities")
5+ .0
6+ }
7+
8+ const GITHUB_CAPABILITIES: &str = "multi_ack thin-pack side-band ofs-delta shallow deepen-since deepen-not deepen-relative no-progress include-tag allow-tip-sha1-in-want allow-reachable-sha1-in-want no-done symref=HEAD:refs/heads/main filter agent=git/github-gdf51a71f0236";
9+ mod fetch {
10+ mod default_features {
11+ use crate::fetch::blocking_io::tests::command::v1::{capabilities, GITHUB_CAPABILITIES};
12+ use crate::fetch::{self, Command};
13+
14+ #[test]
15+ fn it_chooses_the_best_multi_ack_and_sideband() {
16+ assert_eq!(
17+ Command::Fetch.default_features(
18+ git_transport::Protocol::V1,
19+ &capabilities("multi_ack side-band side-band-64k multi_ack_detailed")
20+ ),
21+ &[("side-band-64k", None), ("multi_ack_detailed", None), fetch::agent()]
22+ );
23+ }
24+
25+ #[test]
26+ fn it_chooses_all_supported_non_stacking_capabilities_and_leaves_no_progress() {
27+ assert_eq!(
28+ Command::Fetch.default_features(git_transport::Protocol::V1, &capabilities(GITHUB_CAPABILITIES)),
29+ &[
30+ ("multi_ack", None),
31+ ("thin-pack", None),
32+ ("side-band", None),
33+ ("ofs-delta", None),
34+ ("shallow", None),
35+ ("deepen-since", None),
36+ ("deepen-not", None),
37+ ("deepen-relative", None),
38+ ("allow-tip-sha1-in-want", None),
39+ ("allow-reachable-sha1-in-want", None),
40+ ("no-done", None),
41+ ("filter", None),
42+ fetch::agent()
43+ ],
44+ "we don't enforce include-tag or no-progress"
45+ );
46+ }
47+ }
48+ }
49+}
50+
51+mod v2 {
52+ use git_transport::client::Capabilities;
53+
54+ fn capabilities(command: &str, input: &str) -> Capabilities {
55+ Capabilities::from_lines(Some(Ok("version 2".into())), format!("{}={}", command, input))
56+ .expect("valid input for V2 capabilities")
57+ }
58+
59+ mod fetch {
60+ mod default_features {
61+ use crate::fetch::blocking_io::tests::command::v2::capabilities;
62+ use crate::fetch::{self, Command};
63+
64+ #[test]
65+ fn all_features() {
66+ assert_eq!(
67+ Command::Fetch.default_features(
68+ git_transport::Protocol::V2,
69+ &capabilities("fetch", "shallow filter ref-in-want sideband-all packfile-uris")
70+ ),
71+ ["shallow", "filter", "ref-in-want", "sideband-all", "packfile-uris"]
72+ .iter()
73+ .map(|s| (*s, None))
74+ .chain(Some(fetch::agent()))
75+ .collect::<Vec<_>>()
76+ )
77+ }
78+ }
79+
80+ mod initial_arguments {
81+ use bstr::ByteSlice;
82+
83+ use crate::fetch::blocking_io::tests::command::v2::capabilities;
84+ use crate::fetch::Command;
85+
86+ #[test]
87+ fn for_all_features() {
88+ assert_eq!(
89+ Command::Fetch.initial_arguments(&Command::Fetch.default_features(
90+ git_transport::Protocol::V2,
91+ &capabilities("fetch", "shallow filter ref-in-want sideband-all packfile-uris")
92+ )),
93+ [
94+ "thin-pack",
95+ "include-tag",
96+ "ofs-delta",
97+ "sideband-all",
98+ "ref-in-want",
99+ "packfile-uris"
100+ ]
101+ .iter()
102+ .map(|s| s.as_bytes().as_bstr().to_owned())
103+ .collect::<Vec<_>>()
104+ )
105+ }
106+ }
107+ }
108+
109+ mod ls_refs {
110+ mod default_features {
111+ use crate::fetch::blocking_io::tests::command::v2::capabilities;
112+ use crate::fetch::{self, Command};
113+
114+ #[test]
115+ fn default_as_there_are_no_features() {
116+ assert_eq!(
117+ Command::LsRefs.default_features(
118+ git_transport::Protocol::V2,
119+ &capabilities("something-else", "does not matter as there are none")
120+ ),
121+ &[fetch::agent()]
122+ );
123+ }
124+ }
125+
126+ mod validate {
127+ use bstr::ByteSlice;
128+
129+ use crate::fetch::blocking_io::tests::command::v2::capabilities;
130+ use crate::fetch::Command;
131+
132+ #[test]
133+ fn ref_prefixes_can_always_be_used() {
134+ Command::LsRefs.validate_argument_prefixes_or_panic(
135+ git_transport::Protocol::V2,
136+ &capabilities("something else", "do-not-matter"),
137+ &[b"ref-prefix hello/".as_bstr().into()],
138+ &[],
139+ );
140+ }
141+
142+ #[test]
143+ #[should_panic]
144+ fn unknown_argument() {
145+ Command::LsRefs.validate_argument_prefixes_or_panic(
146+ git_transport::Protocol::V2,
147+ &capabilities("other", "do-not-matter"),
148+ &[b"definitely-nothing-we-know".as_bstr().into()],
149+ &[],
150+ );
151+ }
152+
153+ #[test]
154+ #[should_panic]
155+ fn unknown_feature() {
156+ Command::LsRefs.validate_argument_prefixes_or_panic(
157+ git_transport::Protocol::V2,
158+ &capabilities("other", "do-not-matter"),
159+ &[],
160+ &[("some-feature-that-does-not-exist", None)],
161+ );
162+ }
163+ }
164+ }
165+}
git-protocol/src/fetch/blocking_io/tests/mod.rs+3−0
@@ -0,0 +1,3 @@
1+mod arguments;
2+mod command;
3+mod refs;
git-protocol/src/fetch/blocking_io/tests/refs.rs+108−0
@@ -0,0 +1,108 @@
1+use std::io;
2+
3+use git_transport::client;
4+
5+use crate::fetch::{blocking_io::refs::InternalRef, refs, Ref};
6+
7+fn oid(hex_sha: &str) -> git_hash::ObjectId {
8+ git_hash::ObjectId::from_hex(hex_sha.as_bytes()).expect("valid input")
9+}
10+
11+#[test]
12+fn extract_references_from_v2_refs() {
13+ let input: &mut dyn io::BufRead = &mut "808e50d724f604f69ab93c6da2919c014667bedb HEAD symref-target:refs/heads/main
14+808e50d724f604f69ab93c6da2919c014667bedb refs/heads/main
15+7fe1b98b39423b71e14217aa299a03b7c937d656 refs/tags/foo peeled:808e50d724f604f69ab93c6da2919c014667bedb
16+7fe1b98b39423b71e14217aa299a03b7c937d6ff refs/tags/blaz
17+"
18+ .as_bytes();
19+ let mut out = Vec::new();
20+ refs::from_v2_refs(&mut out, input).expect("no failure on valid input");
21+
22+ assert_eq!(
23+ out,
24+ vec![
25+ Ref::Symbolic {
26+ path: "HEAD".into(),
27+ target: "refs/heads/main".into(),
28+ object: oid("808e50d724f604f69ab93c6da2919c014667bedb")
29+ },
30+ Ref::Direct {
31+ path: "refs/heads/main".into(),
32+ object: oid("808e50d724f604f69ab93c6da2919c014667bedb")
33+ },
34+ Ref::Peeled {
35+ path: "refs/tags/foo".into(),
36+ tag: oid("7fe1b98b39423b71e14217aa299a03b7c937d656"),
37+ object: oid("808e50d724f604f69ab93c6da2919c014667bedb")
38+ },
39+ Ref::Direct {
40+ path: "refs/tags/blaz".into(),
41+ object: oid("7fe1b98b39423b71e14217aa299a03b7c937d6ff")
42+ },
43+ ]
44+ )
45+}
46+
47+#[test]
48+fn extract_references_from_v1_refs() {
49+ let input: &mut dyn io::BufRead = &mut "73a6868963993a3328e7d8fe94e5a6ac5078a944 HEAD
50+73a6868963993a3328e7d8fe94e5a6ac5078a944 refs/heads/main
51+8e472f9ccc7d745927426cbb2d9d077de545aa4e refs/pull/13/head
52+dce0ea858eef7ff61ad345cc5cdac62203fb3c10 refs/tags/git-commitgraph-v0.0.0
53+21c9b7500cb144b3169a6537961ec2b9e865be81 refs/tags/git-commitgraph-v0.0.0^{}"
54+ .as_bytes();
55+ let mut out = vec![InternalRef::SymbolicForLookup {
56+ path: "HEAD".into(),
57+ target: "refs/heads/main".into(),
58+ }];
59+ refs::from_v1_refs_received_as_part_of_handshake(&mut out, input).expect("no failure from valid input");
60+ assert_eq!(
61+ out,
62+ vec![
63+ InternalRef::Symbolic {
64+ path: "HEAD".into(),
65+ target: "refs/heads/main".into(),
66+ object: oid("73a6868963993a3328e7d8fe94e5a6ac5078a944")
67+ },
68+ InternalRef::Direct {
69+ path: "refs/heads/main".into(),
70+ object: oid("73a6868963993a3328e7d8fe94e5a6ac5078a944")
71+ },
72+ InternalRef::Direct {
73+ path: "refs/pull/13/head".into(),
74+ object: oid("8e472f9ccc7d745927426cbb2d9d077de545aa4e")
75+ },
76+ InternalRef::Peeled {
77+ path: "refs/tags/git-commitgraph-v0.0.0".into(),
78+ tag: oid("dce0ea858eef7ff61ad345cc5cdac62203fb3c10"),
79+ object: oid("21c9b7500cb144b3169a6537961ec2b9e865be81")
80+ }
81+ ]
82+ )
83+}
84+
85+#[test]
86+fn extract_symbolic_references_from_capabilities() -> Result<(), client::Error> {
87+ let caps = client::Capabilities::from_bytes(
88+ b"\0unrelated symref=HEAD:refs/heads/main symref=ANOTHER:refs/heads/foo agent=git/2.28.0",
89+ )?
90+ .0;
91+ let mut out = Vec::new();
92+ refs::from_capabilities(&mut out, caps.iter()).expect("a working example");
93+
94+ assert_eq!(
95+ out,
96+ vec![
97+ InternalRef::SymbolicForLookup {
98+ path: "HEAD".into(),
99+ target: "refs/heads/main".into()
100+ },
101+ InternalRef::SymbolicForLookup {
102+ path: "ANOTHER".into(),
103+ target: "refs/heads/foo".into()
104+ }
105+ ]
106+ );
107+ Ok(())
108+}