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
26 26
27 use super::{repo_path, repo_rw};27 use super::{repo_path, repo_rw};
28 use crate::remote::{into_daemon_remote_if_async, spawn_git_daemon_if_async};28 use crate::remote::{into_daemon_remote_if_async, spawn_git_daemon_if_async};
29 use git_features::progress;
29 use git_protocol::maybe_async;30 use git_protocol::maybe_async;
31 use git_repository::remote::fetch;
30 use git_testtools::hex_to_id;32 use git_testtools::hex_to_id;
31 33
32 #[maybe_async::test(34 #[maybe_async::test(
@@ -36,8 +38,11 @@ mod blocking_and_async_io { · 3 unmodified lines
36 async fn fetch_empty_pack() -> crate::Result {38 async fn fetch_empty_pack() -> crate::Result {
37 let (repo, _tmp) = repo_rw("two-origins");39 let (repo, _tmp) = repo_rw("two-origins");
38 let daemon = spawn_git_daemon_if_async(repo_path("base"))?;40 let daemon = spawn_git_daemon_if_async(repo_path("base"))?;
39 let mut remote =41 let mut remote = into_daemon_remote_if_async(
40 into_daemon_remote_if_async(repo.head()?.into_remote(Fetch).expect("present")?, daemon.as_ref());42 repo.head()?.into_remote(Fetch).expect("present")?,
43 daemon.as_ref(),
44 None,
45 );
41 remote.replace_refspecs(Some("HEAD:refs/remotes/origin/does-not-yet-exist"), Fetch)?;46 remote.replace_refspecs(Some("HEAD:refs/remotes/origin/does-not-yet-exist"), Fetch)?;
42 47
43 let res = remote48 let res = remote
@@ -69,7 +74,8 @@ mod blocking_and_async_io { · 25 unmodified lines
69 async fn fetch_pack_without_local_destination() -> crate::Result {74 async fn fetch_pack_without_local_destination() -> crate::Result {
70 let (repo, _tmp) = repo_rw("two-origins");75 let (repo, _tmp) = repo_rw("two-origins");
71 let daemon = spawn_git_daemon_if_async(repo_path("clone-as-base-with-changes"))?;76 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);
73 remote.replace_refspecs(Some("HEAD"), Fetch)?;79 remote.replace_refspecs(Some("HEAD"), Fetch)?;
74 80
75 let res: git::remote::fetch::Outcome = remote81 let res: git::remote::fetch::Outcome = remote
@@ -93,6 +99,156 @@ mod blocking_and_async_io { · 17 unmodified lines
93 }99 }
94 Ok(())100 Ok(())
95 }101 }
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 }
96}252}
97 253
98#[cfg(feature = "blocking-network-client")]254#[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
44 44
45/// Turn `remote` into a remote that interacts with the git `daemon`, all else being the same, by creating a new stand-in remote.45/// Turn `remote` into a remote that interacts with the git `daemon`, all else being the same, by creating a new stand-in remote.
46#[cfg(any(feature = "blocking-network-client", feature = "async-network-client-async-std"))]46#[cfg(any(feature = "blocking-network-client", feature = "async-network-client-async-std"))]
47pub(crate) fn into_daemon_remote_if_async<'repo>(47pub(crate) fn into_daemon_remote_if_async<'repo, 'a>(
48 remote: git::Remote<'repo>,48 remote: git::Remote<'repo>,
49 _daemon: Option<&git_testtools::GitDaemon>,49 _daemon: Option<&git_testtools::GitDaemon>,
50 _repo_name: impl Into<Option<&'a str>>,
50) -> git::Remote<'repo> {51) -> git::Remote<'repo> {
51 #[cfg(feature = "blocking-network-client")]52 #[cfg(feature = "blocking-network-client")]
52 {53 {
@@ -56,7 +57,11 @@ pub(crate) fn into_daemon_remote_if_async<'repo>( · 3 unmodified lines
56 {57 {
57 let mut new_remote = remote58 let mut new_remote = remote
58 .repo()59 .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 ))
60 .expect("valid url to create remote at");65 .expect("valid url to create remote at");
61 for direction in [git::remote::Direction::Fetch, git::remote::Direction::Push] {66 for direction in [git::remote::Direction::Fetch, git::remote::Direction::Push] {
62 new_remote67 new_remote
git-repository/tests/remote/ref_map.rs+1−1
@@ -29,7 +29,7 @@ mod blocking_and_async_io { · 28 unmodified lines
29 )?;29 )?;
30 }30 }
31 31
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);
33 let map = remote33 let map = remote
34 .connect(Fetch, progress::Discard)34 .connect(Fetch, progress::Discard)
35 .await?35 .await?