History

Add last remaining test to validate entire packs can be fetched in async mode without issues.

Sebastian Thiel committed 4 years ago

Commit 5cc30871 parent 01e99b43 files · +167 −6Browse files

Unified · Split

3 files
  1. git-repository/tests/remote/fetch.rs +159 −3
  2. git-repository/tests/remote/mod.rs +7 −2
  3. git-repository/tests/remote/ref_map.rs +1 −1
git-repository/tests/remote/fetch.rs+159−3
@@ -26,7 +26,9 @@ mod blocking_and_async_io { · 25 unmodified lines
2626
2727 use super::{repo_path, repo_rw};
2828 use crate::remote::{into_daemon_remote_if_async, spawn_git_daemon_if_async};
29+ use git_features::progress;
2930 use git_protocol::maybe_async;
31+ use git_repository::remote::fetch;
3032 use git_testtools::hex_to_id;
3133
3234 #[maybe_async::test(
@@ -36,8 +38,11 @@ mod blocking_and_async_io { · 3 unmodified lines
3638 async fn fetch_empty_pack() -> crate::Result {
3739 let (repo, _tmp) = repo_rw("two-origins");
3840 let daemon = spawn_git_daemon_if_async(repo_path("base"))?;
39− let mut remote =
40− into_daemon_remote_if_async(repo.head()?.into_remote(Fetch).expect("present")?, daemon.as_ref());
41+ let mut remote = into_daemon_remote_if_async(
42+ repo.head()?.into_remote(Fetch).expect("present")?,
43+ daemon.as_ref(),
44+ None,
45+ );
4146 remote.replace_refspecs(Some("HEAD:refs/remotes/origin/does-not-yet-exist"), Fetch)?;
4247
4348 let res = remote
@@ -69,7 +74,8 @@ mod blocking_and_async_io { · 25 unmodified lines
6974 async fn fetch_pack_without_local_destination() -> crate::Result {
7075 let (repo, _tmp) = repo_rw("two-origins");
7176 let daemon = spawn_git_daemon_if_async(repo_path("clone-as-base-with-changes"))?;
72− let mut remote = into_daemon_remote_if_async(repo.find_remote("changes-on-top-of-origin")?, daemon.as_ref());
77+ let mut remote =
78+ into_daemon_remote_if_async(repo.find_remote("changes-on-top-of-origin")?, daemon.as_ref(), None);
7379 remote.replace_refspecs(Some("HEAD"), Fetch)?;
7480
7581 let res: git::remote::fetch::Outcome = remote
@@ -93,6 +99,156 @@ mod blocking_and_async_io { · 17 unmodified lines
9399 }
94100 Ok(())
95101 }
102+
103+ #[maybe_async::test(
104+ feature = "blocking-network-client",
105+ async(feature = "async-network-client-async-std", async_std::test)
106+ )]
107+ async fn fetch_pack() -> crate::Result {
108+ let daemon = spawn_git_daemon_if_async({
109+ let mut p = repo_path("base");
110+ p.pop();
111+ p
112+ })?;
113+ for (version, expected_objects, expected_hash) in [
114+ (None, 4, "d07c527cf14e524a8494ce6d5d08e28079f5c6ea"),
115+ (
116+ Some(git::protocol::transport::Protocol::V2),
117+ 4,
118+ "d07c527cf14e524a8494ce6d5d08e28079f5c6ea",
119+ ),
120+ (
121+ Some(git::protocol::transport::Protocol::V1),
122+ 4,
123+ "d07c527cf14e524a8494ce6d5d08e28079f5c6ea", // TODO: if these are the same, remove them
124+ ),
125+ ] {
126+ let (mut repo, _tmp) = repo_rw("two-origins");
127+ if let Some(version) = version {
128+ repo.config_snapshot_mut().set_raw_value(
129+ "protocol",
130+ None,
131+ "version",
132+ (version as u8).to_string().as_str(),
133+ )?;
134+ }
135+
136+ // No updates
137+ {
138+ let remote = into_daemon_remote_if_async(repo.find_remote("origin")?, daemon.as_ref(), "base");
139+ {
140+ remote
141+ .connect(Fetch, progress::Discard)
142+ .await?
143+ .prepare_fetch(Default::default())
144+ .await?;
145+ // early drops are fine and won't block.
146+ }
147+ let outcome = remote
148+ .connect(Fetch, progress::Discard)
149+ .await?
150+ .prepare_fetch(Default::default())
151+ .await?
152+ .receive(&AtomicBool::default())
153+ .await?;
154+ assert!(matches!(outcome.status, git::remote::fetch::Status::NoChange));
155+ }
156+
157+ // Some updates to be fetched
158+ for dry_run in [true, false] {
159+ let remote = into_daemon_remote_if_async(
160+ repo.find_remote("changes-on-top-of-origin")?,
161+ daemon.as_ref(),
162+ "clone-as-base-with-changes",
163+ );
164+ let outcome: git::remote::fetch::Outcome = remote
165+ .connect(Fetch, progress::Discard)
166+ .await?
167+ .prepare_fetch(Default::default())
168+ .await?
169+ .with_dry_run(dry_run)
170+ .receive(&AtomicBool::default())
171+ .await?;
172+ let refs = match outcome.status {
173+ fetch::Status::Change {
174+ write_pack_bundle,
175+ update_refs,
176+ } => {
177+ assert_eq!(write_pack_bundle.pack_version, git::odb::pack::data::Version::V2);
178+ assert_eq!(write_pack_bundle.object_hash, repo.object_hash());
179+ assert_eq!(write_pack_bundle.index.num_objects, expected_objects, "{dry_run}: this value is 4 when git does it with 'consecutive' negotiation style, but could be 33 if completely naive.");
180+ assert_eq!(
181+ write_pack_bundle.index.index_version,
182+ git::odb::pack::index::Version::V2
183+ );
184+ assert_eq!(write_pack_bundle.index.index_hash, hex_to_id(expected_hash));
185+ assert!(write_pack_bundle.data_path.map_or(false, |f| f.is_file()));
186+ assert!(write_pack_bundle.index_path.map_or(false, |f| f.is_file()));
187+ assert_eq!(update_refs.edits.len(), 2);
188+
189+ let edit = &update_refs.edits[0];
190+ assert_eq!(edit.name.as_bstr(), "refs/remotes/changes-on-top-of-origin/main");
191+ assert!(
192+ edit.change.new_value().expect("no deletion").try_id().is_some(),
193+ "a simple peeled ref"
194+ );
195+ let edit = &update_refs.edits[1];
196+ assert_eq!(edit.name.as_bstr(), "refs/remotes/changes-on-top-of-origin/symbolic");
197+ assert!(
198+ edit.change.new_value().expect("no deletion").try_id().is_some(),
199+ "on the remote this is a symbolic ref, we just write its destination object id though"
200+ );
201+
202+ assert!(
203+ !write_pack_bundle.keep_path.map_or(false, |f| f.is_file()),
204+ ".keep files are deleted if there is one edit"
205+ );
206+
207+ update_refs
208+ }
209+ fetch::Status::DryRun { update_refs } => update_refs,
210+ fetch::Status::NoChange => unreachable!("we firmly expect changes here"),
211+ };
212+
213+ assert_eq!(
214+ refs.updates,
215+ vec![
216+ fetch::refs::Update {
217+ mode: fetch::refs::update::Mode::New,
218+ edit_index: Some(0),
219+ },
220+ fetch::refs::Update {
221+ mode: fetch::refs::update::Mode::New,
222+ edit_index: Some(1),
223+ }
224+ ]
225+ );
226+ for (_update, mapping, _spec, edit) in
227+ refs.iter_mapping_updates(&outcome.ref_map.mappings, remote.refspecs(Fetch))
228+ {
229+ let edit = edit.expect("refedit present even if it's a no-op");
230+ if dry_run {
231+ assert_eq!(
232+ edit.change.new_value().expect("no deletions").id(),
233+ mapping.remote.as_id().expect("no unborn")
234+ );
235+ assert!(
236+ repo.try_find_reference(edit.name.as_ref())?.is_none(),
237+ "no ref created in dry-run mode"
238+ );
239+ } else {
240+ let r = repo.find_reference(edit.name.as_ref()).unwrap();
241+ assert_eq!(
242+ r.id(),
243+ *mapping.remote.as_id().expect("no unborn"),
244+ "local reference should point to remote id"
245+ );
246+ }
247+ }
248+ }
249+ }
250+ Ok(())
251+ }
96252 }
97253
98254 #[cfg(feature = "blocking-network-client")]
git-repository/tests/remote/mod.rs+7−2
@@ -44,9 +44,10 @@ pub(crate) fn spawn_git_daemon_if_async( · 43 unmodified lines
4444
4545 /// Turn `remote` into a remote that interacts with the git `daemon`, all else being the same, by creating a new stand-in remote.
4646 #[cfg(any(feature = "blocking-network-client", feature = "async-network-client-async-std"))]
47−pub(crate) fn into_daemon_remote_if_async<'repo>(
47+pub(crate) fn into_daemon_remote_if_async<'repo, 'a>(
4848 remote: git::Remote<'repo>,
4949 _daemon: Option<&git_testtools::GitDaemon>,
50+ _repo_name: impl Into<Option<&'a str>>,
5051 ) -> git::Remote<'repo> {
5152 #[cfg(feature = "blocking-network-client")]
5253 {
@@ -56,7 +57,11 @@ pub(crate) fn into_daemon_remote_if_async<'repo>( · 3 unmodified lines
5657 {
5758 let mut new_remote = remote
5859 .repo()
59− .remote_at(format!("{}/", _daemon.expect("daemon is available in async mode").url))
60+ .remote_at(format!(
61+ "{}/{}",
62+ _daemon.expect("daemon is available in async mode").url,
63+ _repo_name.into().unwrap_or_default()
64+ ))
6065 .expect("valid url to create remote at");
6166 for direction in [git::remote::Direction::Fetch, git::remote::Direction::Push] {
6267 new_remote
git-repository/tests/remote/ref_map.rs+1−1
@@ -29,7 +29,7 @@ mod blocking_and_async_io { · 28 unmodified lines
2929 )?;
3030 }
3131
32− let remote = into_daemon_remote_if_async(repo.find_remote("origin")?, daemon.as_ref());
32+ let remote = into_daemon_remote_if_async(repo.find_remote("origin")?, daemon.as_ref(), None);
3333 let map = remote
3434 .connect(Fetch, progress::Discard)
3535 .await?