History

refactor

Sebastian Thiel committed 3 years ago

Commit f17c6b61 parent c1d2aea2 files · +209 −211Browse files

Unified · Split

2 files
  1. git-ref/tests/file/transaction/prepare_and_commit/create_or_update/collisions.rs +208 −0
  2. git-ref/tests/file/transaction/prepare_and_commit/create_or_update/mod.rs +1 −211
git-ref/tests/file/transaction/prepare_and_commit/create_or_update/collisions.rs+208−0
@@ -0,0 +1,208 @@
1+use crate::file::transaction::prepare_and_commit::{committer, create_at, delete_at, empty_store};
2+use git_lock::acquire::Fail;
3+use git_ref::file::transaction::PackedRefs;
4+use git_ref::transaction::{Change, LogChange, PreviousValue, RefEdit};
5+use git_ref::Target;
6+use std::convert::TryInto;
7+
8+fn case_sensitive(tmp_dir: &std::path::Path) -> bool {
9+ std::fs::write(tmp_dir.join("config"), "").expect("can create file once");
10+ !git_worktree::fs::Capabilities::probe(tmp_dir).ignore_case
11+}
12+
13+#[test]
14+fn conflicting_creation_without_packed_refs() -> crate::Result {
15+ let (dir, store) = empty_store()?;
16+ let res = store.transaction().prepare(
17+ [create_at("refs/a"), create_at("refs/A")],
18+ Fail::Immediately,
19+ Fail::Immediately,
20+ );
21+
22+ let case_sensitive = case_sensitive(dir.path());
23+ match res {
24+ Ok(_) if case_sensitive => {}
25+ Ok(_) if !case_sensitive => panic!("should fail as 'a' and 'A' clash"),
26+ Err(err) if case_sensitive => panic!(
27+ "should work as case sensitivity allows 'a' and 'A' to coexist: {:?}",
28+ err
29+ ),
30+ Err(err) if !case_sensitive => {
31+ assert_eq!(err.to_string(), "A lock could not be obtained for reference \"refs/A\"")
32+ }
33+ _ => unreachable!("actually everything is covered"),
34+ }
35+ Ok(())
36+}
37+
38+#[test]
39+fn non_conflicting_creation_without_packed_refs_work() -> crate::Result {
40+ let (_dir, store) = empty_store()?;
41+ let ongoing = store
42+ .transaction()
43+ .prepare([create_at("refs/new")], Fail::Immediately, Fail::Immediately)
44+ .unwrap();
45+
46+ let t2 = store.transaction().prepare(
47+ [create_at("refs/non-conflicting")],
48+ Fail::Immediately,
49+ Fail::Immediately,
50+ )?;
51+
52+ t2.commit(committer().to_ref())?;
53+ ongoing.commit(committer().to_ref())?;
54+
55+ Ok(())
56+}
57+
58+#[test]
59+fn packed_refs_lock_is_mandatory_for_multiple_ongoing_transactions_even_if_one_does_not_need_it() -> crate::Result {
60+ let (_dir, store) = empty_store()?;
61+ let ref_name = "refs/a";
62+ let _t1 = store
63+ .transaction()
64+ .packed_refs(PackedRefs::DeletionsAndNonSymbolicUpdatesRemoveLooseSourceReference(
65+ Box::new(|_, _| Ok(Some(git_object::Kind::Commit))),
66+ ))
67+ .prepare([create_at(ref_name)], Fail::Immediately, Fail::Immediately)?;
68+
69+ let t2res = store
70+ .transaction()
71+ .prepare([delete_at(ref_name)], Fail::Immediately, Fail::Immediately);
72+ assert_eq!(&t2res.unwrap_err().to_string()[..51], "The lock for the packed-ref file could not be obtai", "if packed-refs are about to be created, other transactions always acquire a packed-refs lock as to not miss anything");
73+ Ok(())
74+}
75+
76+#[test]
77+fn conflicting_creation_into_packed_refs() -> crate::Result {
78+ let (_dir, store) = empty_store()?;
79+ store
80+ .transaction()
81+ .packed_refs(PackedRefs::DeletionsAndNonSymbolicUpdatesRemoveLooseSourceReference(
82+ Box::new(|_, _| Ok(Some(git_object::Kind::Commit))),
83+ ))
84+ .prepare(
85+ [create_at("refs/a"), create_at("refs/A")],
86+ Fail::Immediately,
87+ Fail::Immediately,
88+ )?
89+ .commit(committer().to_ref())?;
90+
91+ assert_eq!(
92+ store.cached_packed_buffer()?.expect("created").iter()?.count(),
93+ 2,
94+ "packed-refs can store everything in case-insensitive manner"
95+ );
96+ assert_eq!(
97+ store.loose_iter()?.count(),
98+ 0,
99+ "refs/ directory isn't present as there is no loose ref - it removed every up to the base dir"
100+ );
101+
102+ // The following works because locks aren't actually obtained if there would be no change.
103+ store
104+ .transaction()
105+ .packed_refs(PackedRefs::DeletionsAndNonSymbolicUpdatesRemoveLooseSourceReference(
106+ Box::new(|_, _| Ok(Some(git_object::Kind::Commit))),
107+ ))
108+ .prepare(
109+ [
110+ RefEdit {
111+ change: Change::Update {
112+ log: LogChange::default(),
113+ expected: PreviousValue::Any,
114+ new: Target::Peeled(git_hash::Kind::Sha1.null()),
115+ },
116+ name: "refs/a".try_into().expect("valid"),
117+ deref: false,
118+ },
119+ RefEdit {
120+ change: Change::Update {
121+ log: LogChange::default(),
122+ expected: PreviousValue::MustExistAndMatch(Target::Peeled(git_hash::Kind::Sha1.null())),
123+ new: Target::Peeled(git_hash::Kind::Sha1.null()),
124+ },
125+ name: "refs/A".try_into().expect("valid"),
126+ deref: false,
127+ },
128+ ],
129+ Fail::Immediately,
130+ Fail::Immediately,
131+ )?
132+ .commit(committer().to_ref())?;
133+ assert_eq!(store.iter()?.all()?.count(), 2);
134+
135+ {
136+ let _ongoing = store
137+ .transaction()
138+ .prepare([create_at("refs/new")], Fail::Immediately, Fail::Immediately)?;
139+
140+ let t2res = store.transaction().prepare(
141+ [create_at("refs/non-conflicting")],
142+ Fail::Immediately,
143+ Fail::Immediately,
144+ );
145+
146+ assert_eq!(
147+ &t2res.unwrap_err().to_string()[..40],
148+ "The lock for the packed-ref file could n",
149+ "packed-refs files will always be locked if they are present as we have to look up their content"
150+ );
151+ }
152+
153+ {
154+ let _ongoing = store
155+ .transaction()
156+ .prepare([delete_at("refs/a")], Fail::Immediately, Fail::Immediately)?;
157+
158+ let t2res = store
159+ .transaction()
160+ .prepare([delete_at("refs/A")], Fail::Immediately, Fail::Immediately);
161+
162+ assert_eq!(
163+ &t2res.unwrap_err().to_string()[..40],
164+ "The lock for the packed-ref file could n",
165+ "once again, packed-refs save the day"
166+ );
167+ }
168+
169+ // Create a loose ref at a path
170+ assert_eq!(store.loose_iter()?.count(), 0);
171+ store
172+ .transaction()
173+ .prepare(
174+ [RefEdit {
175+ change: Change::Update {
176+ log: LogChange::default(),
177+ expected: PreviousValue::Any,
178+ new: Target::Symbolic("refs/heads/does-not-matter".try_into().expect("valid")),
179+ },
180+ name: "refs/a".try_into().expect("valid"),
181+ deref: false,
182+ }],
183+ Fail::Immediately,
184+ Fail::Immediately,
185+ )?
186+ .commit(committer().to_ref())?;
187+ assert_eq!(
188+ store.loose_iter()?.count(),
189+ 1,
190+ "we created a loose ref, overlaying the packed one"
191+ );
192+
193+ store
194+ .transaction()
195+ .prepare(
196+ [delete_at("refs/a"), delete_at("refs/A")],
197+ Fail::Immediately,
198+ Fail::Immediately,
199+ )?
200+ .commit(committer().to_ref())?;
201+
202+ assert_eq!(
203+ store.iter()?.all()?.count(),
204+ 0,
205+ "we deleted our only two packed refs and one loose ref with the same name"
206+ );
207+ Ok(())
208+}
git-ref/tests/file/transaction/prepare_and_commit/create_or_update/mod.rsrenamed from git-ref/tests/file/transaction/prepare_and_commit/create_or_update.rs+1−211
@@ -21,217 +21,7 @@ use crate::file::{ · 20 unmodified lines
2121 transaction::prepare_and_commit::{committer, empty_store, log_line, reflog_lines},
2222 };
2323
24−mod collisions {
25− use crate::file::transaction::prepare_and_commit::{committer, create_at, delete_at, empty_store};
26− use git_lock::acquire::Fail;
27− use git_ref::file::transaction::PackedRefs;
28− use git_ref::transaction::{Change, LogChange, PreviousValue, RefEdit};
29− use git_ref::Target;
30− use std::convert::TryInto;
31−
32− fn case_sensitive(tmp_dir: &std::path::Path) -> bool {
33− std::fs::write(tmp_dir.join("config"), "").expect("can create file once");
34− !git_worktree::fs::Capabilities::probe(tmp_dir).ignore_case
35− }
36−
37− #[test]
38− fn conflicting_creation_without_packed_refs() -> crate::Result {
39− let (dir, store) = empty_store()?;
40− let res = store.transaction().prepare(
41− [create_at("refs/a"), create_at("refs/A")],
42− Fail::Immediately,
43− Fail::Immediately,
44− );
45−
46− let case_sensitive = case_sensitive(dir.path());
47− match res {
48− Ok(_) if case_sensitive => {}
49− Ok(_) if !case_sensitive => panic!("should fail as 'a' and 'A' clash"),
50− Err(err) if case_sensitive => panic!(
51− "should work as case sensitivity allows 'a' and 'A' to coexist: {:?}",
52− err
53− ),
54− Err(err) if !case_sensitive => {
55− assert_eq!(err.to_string(), "A lock could not be obtained for reference \"refs/A\"")
56− }
57− _ => unreachable!("actually everything is covered"),
58− }
59− Ok(())
60− }
61−
62− #[test]
63− fn non_conflicting_creation_without_packed_refs_work() -> crate::Result {
64− let (_dir, store) = empty_store()?;
65− let ongoing = store
66− .transaction()
67− .prepare([create_at("refs/new")], Fail::Immediately, Fail::Immediately)
68− .unwrap();
69−
70− let t2 = store.transaction().prepare(
71− [create_at("refs/non-conflicting")],
72− Fail::Immediately,
73− Fail::Immediately,
74− )?;
75−
76− t2.commit(committer().to_ref())?;
77− ongoing.commit(committer().to_ref())?;
78−
79− Ok(())
80− }
81−
82− #[test]
83− fn packed_refs_lock_is_mandatory_for_multiple_ongoing_transactions_even_if_one_does_not_need_it() -> crate::Result {
84− let (_dir, store) = empty_store()?;
85− let ref_name = "refs/a";
86− let _t1 = store
87− .transaction()
88− .packed_refs(PackedRefs::DeletionsAndNonSymbolicUpdatesRemoveLooseSourceReference(
89− Box::new(|_, _| Ok(Some(git_object::Kind::Commit))),
90− ))
91− .prepare([create_at(ref_name)], Fail::Immediately, Fail::Immediately)?;
92−
93− let t2res = store
94− .transaction()
95− .prepare([delete_at(ref_name)], Fail::Immediately, Fail::Immediately);
96− assert_eq!(&t2res.unwrap_err().to_string()[..51], "The lock for the packed-ref file could not be obtai", "if packed-refs are about to be created, other transactions always acquire a packed-refs lock as to not miss anything");
97− Ok(())
98− }
99−
100− #[test]
101− fn conflicting_creation_into_packed_refs() -> crate::Result {
102− let (_dir, store) = empty_store()?;
103− store
104− .transaction()
105− .packed_refs(PackedRefs::DeletionsAndNonSymbolicUpdatesRemoveLooseSourceReference(
106− Box::new(|_, _| Ok(Some(git_object::Kind::Commit))),
107− ))
108− .prepare(
109− [create_at("refs/a"), create_at("refs/A")],
110− Fail::Immediately,
111− Fail::Immediately,
112− )?
113− .commit(committer().to_ref())?;
114−
115− assert_eq!(
116− store.cached_packed_buffer()?.expect("created").iter()?.count(),
117− 2,
118− "packed-refs can store everything in case-insensitive manner"
119− );
120− assert_eq!(
121− store.loose_iter()?.count(),
122− 0,
123− "refs/ directory isn't present as there is no loose ref - it removed every up to the base dir"
124− );
125−
126− // The following works because locks aren't actually obtained if there would be no change.
127− store
128− .transaction()
129− .packed_refs(PackedRefs::DeletionsAndNonSymbolicUpdatesRemoveLooseSourceReference(
130− Box::new(|_, _| Ok(Some(git_object::Kind::Commit))),
131− ))
132− .prepare(
133− [
134− RefEdit {
135− change: Change::Update {
136− log: LogChange::default(),
137− expected: PreviousValue::Any,
138− new: Target::Peeled(git_hash::Kind::Sha1.null()),
139− },
140− name: "refs/a".try_into().expect("valid"),
141− deref: false,
142− },
143− RefEdit {
144− change: Change::Update {
145− log: LogChange::default(),
146− expected: PreviousValue::MustExistAndMatch(Target::Peeled(git_hash::Kind::Sha1.null())),
147− new: Target::Peeled(git_hash::Kind::Sha1.null()),
148− },
149− name: "refs/A".try_into().expect("valid"),
150− deref: false,
151− },
152− ],
153− Fail::Immediately,
154− Fail::Immediately,
155− )?
156− .commit(committer().to_ref())?;
157− assert_eq!(store.iter()?.all()?.count(), 2);
158−
159− {
160− let _ongoing =
161− store
162− .transaction()
163− .prepare([create_at("refs/new")], Fail::Immediately, Fail::Immediately)?;
164−
165− let t2res = store.transaction().prepare(
166− [create_at("refs/non-conflicting")],
167− Fail::Immediately,
168− Fail::Immediately,
169− );
170−
171− assert_eq!(
172− &t2res.unwrap_err().to_string()[..40],
173− "The lock for the packed-ref file could n",
174− "packed-refs files will always be locked if they are present as we have to look up their content"
175− );
176− }
177−
178− {
179− let _ongoing = store
180− .transaction()
181− .prepare([delete_at("refs/a")], Fail::Immediately, Fail::Immediately)?;
182−
183− let t2res = store
184− .transaction()
185− .prepare([delete_at("refs/A")], Fail::Immediately, Fail::Immediately);
186−
187− assert_eq!(
188− &t2res.unwrap_err().to_string()[..40],
189− "The lock for the packed-ref file could n",
190− "once again, packed-refs save the day"
191− );
192− }
193−
194− // Create a loose ref at a path
195− assert_eq!(store.loose_iter()?.count(), 0);
196− store
197− .transaction()
198− .prepare(
199− [RefEdit {
200− change: Change::Update {
201− log: LogChange::default(),
202− expected: PreviousValue::Any,
203− new: Target::Symbolic("refs/heads/does-not-matter".try_into().expect("valid")),
204− },
205− name: "refs/a".try_into().expect("valid"),
206− deref: false,
207− }],
208− Fail::Immediately,
209− Fail::Immediately,
210− )?
211− .commit(committer().to_ref())?;
212− assert_eq!(
213− store.loose_iter()?.count(),
214− 1,
215− "we created a loose ref, overlaying the packed one"
216− );
217−
218− store
219− .transaction()
220− .prepare(
221− [delete_at("refs/a"), delete_at("refs/A")],
222− Fail::Immediately,
223− Fail::Immediately,
224− )?
225− .commit(committer().to_ref())?;
226−
227− assert_eq!(
228− store.iter()?.all()?.count(),
229− 0,
230− "we deleted our only two packed refs and one loose ref with the same name"
231− );
232− Ok(())
233− }
234−}
24+mod collisions;
23525
23626 #[test]
23727 fn intermediate_directories_are_removed_on_rollback() -> crate::Result {

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t