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 @@
1use crate::file::transaction::prepare_and_commit::{committer, create_at, delete_at, empty_store};
2use git_lock::acquire::Fail;
3use git_ref::file::transaction::PackedRefs;
4use git_ref::transaction::{Change, LogChange, PreviousValue, RefEdit};
5use git_ref::Target;
6use std::convert::TryInto;
7
8fn 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]
14fn 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]
39fn 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]
59fn 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]
77fn 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
21 transaction::prepare_and_commit::{committer, empty_store, log_line, reflog_lines},21 transaction::prepare_and_commit::{committer, empty_store, log_line, reflog_lines},
22};22};
23 23
24mod collisions {24mod 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}
235 25
236#[test]26#[test]
237fn intermediate_directories_are_removed_on_rollback() -> crate::Result {27fn 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