Skip to content

Commit 55a32c8

Browse files
committed
add all relevant tests for the merge processing pipeline
1 parent 21bca6c commit 55a32c8

File tree

4 files changed

+216
-24
lines changed

4 files changed

+216
-24
lines changed

gix-merge/src/blob/pipeline.rs

+25-22
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use super::{BuiltinDriver, Pipeline, ResourceKind};
2-
use bstr::{BStr, ByteSlice};
2+
use bstr::{BStr, BString, ByteSlice};
33
use gix_filter::attributes;
44
use gix_filter::driver::apply::{Delay, MaybeDelayed};
55
use gix_filter::pipeline::convert::{ToGitOutcome, ToWorktreeOutcome};
@@ -8,7 +8,7 @@ use std::io::Read;
88
use std::path::{Path, PathBuf};
99

1010
/// Options for use in a [`Pipeline`].
11-
#[derive(Default, Clone, Copy, PartialEq, Eq, Debug, Hash, Ord, PartialOrd)]
11+
#[derive(Default, Clone, PartialEq, Eq, Debug, Hash, Ord, PartialOrd)]
1212
pub struct Options {
1313
/// The amount of bytes that an object has to reach before being treated as binary.
1414
/// These objects will not be queried, nor will their data be processed in any way.
@@ -22,10 +22,10 @@ pub struct Options {
2222
pub large_file_threshold_bytes: u64,
2323
/// Capabilities of the file system which affect how we read worktree files.
2424
pub fs: gix_fs::Capabilities,
25-
/// Define which driver to use if the `merge` attribute for a resource is unspecified.
25+
/// Define which driver to use by name if the `merge` attribute for a resource is unspecified.
2626
///
2727
/// This is the value of the `merge.default` git configuration.
28-
pub default_driver: Option<BuiltinDriver>,
28+
pub default_driver: Option<BString>,
2929
}
3030

3131
/// The specific way to convert a resource.
@@ -242,25 +242,28 @@ impl Pipeline {
242242
let driver = match attr.assignment.state {
243243
attributes::StateRef::Set => DriverChoice::BuiltIn(BuiltinDriver::Text),
244244
attributes::StateRef::Unset => DriverChoice::BuiltIn(BuiltinDriver::Binary),
245-
attributes::StateRef::Value(name) => {
246-
let name = name.as_bstr();
247-
self.drivers
248-
.binary_search_by(|d| d.name.as_bstr().cmp(name))
249-
.ok()
250-
.map(DriverChoice::Index)
251-
.or_else(|| {
252-
name.to_str()
253-
.ok()
254-
.and_then(BuiltinDriver::by_name)
255-
.map(DriverChoice::BuiltIn)
256-
})
257-
.unwrap_or_default()
245+
attributes::StateRef::Value(_) | attributes::StateRef::Unspecified => {
246+
let name = match attr.assignment.state {
247+
attributes::StateRef::Value(name) => Some(name.as_bstr()),
248+
attributes::StateRef::Unspecified => {
249+
self.options.default_driver.as_ref().map(|name| name.as_bstr())
250+
}
251+
_ => unreachable!("only value and unspecified are possible here"),
252+
};
253+
name.and_then(|name| {
254+
self.drivers
255+
.binary_search_by(|d| d.name.as_bstr().cmp(name))
256+
.ok()
257+
.map(DriverChoice::Index)
258+
.or_else(|| {
259+
name.to_str()
260+
.ok()
261+
.and_then(BuiltinDriver::by_name)
262+
.map(DriverChoice::BuiltIn)
263+
})
264+
})
265+
.unwrap_or_default()
258266
}
259-
attributes::StateRef::Unspecified => self
260-
.options
261-
.default_driver
262-
.map(DriverChoice::BuiltIn)
263-
.unwrap_or_default(),
264267
};
265268
match self.roots.by_kind(kind) {
266269
Some(root) => {

gix-merge/tests/merge/blob/builtin_driver.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,8 @@ mod text {
123123
"Number of expected diverging cases must match the actual one - probably the implementation improved"
124124
);
125125
assert_eq!(
126-
(num_diverging as f32 / num_cases as f32) * 100.0,
127-
12.053572,
126+
((num_diverging as f32 / num_cases as f32) * 100.0) as usize,
127+
12,
128128
"Just to show the percentage of skipped tests - this should get better"
129129
);
130130
Ok(())

gix-merge/tests/merge/blob/mod.rs

+50
Original file line numberDiff line numberDiff line change
@@ -1 +1,51 @@
11
mod builtin_driver;
2+
mod pipeline;
3+
4+
mod util {
5+
use std::collections::HashMap;
6+
7+
use gix_hash::oid;
8+
use gix_object::{bstr::BString, find::Error};
9+
10+
#[derive(Default)]
11+
pub struct ObjectDb {
12+
data_by_id: HashMap<gix_hash::ObjectId, BString>,
13+
}
14+
15+
impl gix_object::FindHeader for ObjectDb {
16+
fn try_header(&self, id: &oid) -> Result<Option<gix_object::Header>, Error> {
17+
match self.data_by_id.get(&id.to_owned()) {
18+
Some(data) => Ok(Some(gix_object::Header {
19+
kind: gix_object::Kind::Blob,
20+
size: data.len() as u64,
21+
})),
22+
None => Ok(None),
23+
}
24+
}
25+
}
26+
27+
impl gix_object::Find for ObjectDb {
28+
fn try_find<'a>(&self, id: &oid, buffer: &'a mut Vec<u8>) -> Result<Option<gix_object::Data<'a>>, Error> {
29+
match self.data_by_id.get(&id.to_owned()) {
30+
Some(data) => {
31+
buffer.clear();
32+
buffer.extend_from_slice(data);
33+
Ok(Some(gix_object::Data {
34+
kind: gix_object::Kind::Blob,
35+
data: buffer.as_slice(),
36+
}))
37+
}
38+
None => Ok(None),
39+
}
40+
}
41+
}
42+
43+
impl ObjectDb {
44+
/// Insert `data` and return its hash. That can be used to find it again.
45+
pub fn insert(&mut self, data: &str) -> gix_hash::ObjectId {
46+
let id = gix_object::compute_hash(gix_hash::Kind::Sha1, gix_object::Kind::Blob, data.as_bytes());
47+
self.data_by_id.insert(id, data.into());
48+
id
49+
}
50+
}
51+
}
+139
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
use crate::blob::util::ObjectDb;
2+
use bstr::ByteSlice;
3+
use gix_merge::blob::pipeline::{self, DriverChoice, WorktreeRoots};
4+
use gix_merge::blob::{BuiltinDriver, ResourceKind};
5+
use gix_object::tree::EntryKind;
6+
7+
#[test]
8+
fn without_transformation() -> crate::Result {
9+
for mode in [pipeline::Mode::ToGit, pipeline::Mode::Renormalize] {
10+
let tmp = gix_testtools::tempfile::TempDir::new()?;
11+
let mut filter = gix_merge::blob::Pipeline::new(
12+
WorktreeRoots {
13+
common_ancestor_root: Some(tmp.path().to_owned()),
14+
current_root: None,
15+
other_root: None,
16+
},
17+
gix_filter::Pipeline::default(),
18+
vec![],
19+
default_options(),
20+
);
21+
22+
let does_not_matter = gix_hash::Kind::Sha1.null();
23+
let mut buf = Vec::new();
24+
let a_name = "a";
25+
let a_content = "a-content";
26+
std::fs::write(tmp.path().join(a_name), a_content.as_bytes())?;
27+
let out = filter.convert_to_mergeable(
28+
&does_not_matter,
29+
EntryKind::Blob,
30+
a_name.into(),
31+
ResourceKind::CommonAncestorOrBase,
32+
&mut |_, _| {},
33+
&gix_object::find::Never,
34+
mode,
35+
&mut buf,
36+
)?;
37+
assert_eq!(
38+
out.driver,
39+
default_driver(),
40+
"default driver is text, nothing else was configured"
41+
);
42+
assert_eq!(out.data, Some(pipeline::Data::Buffer));
43+
assert_eq!(buf.as_bstr(), a_content, "there is no transformations configured");
44+
45+
let link_name = "link";
46+
gix_fs::symlink::create(a_name.as_ref(), &tmp.path().join(link_name))?;
47+
let err = filter
48+
.convert_to_mergeable(
49+
&does_not_matter,
50+
EntryKind::Link,
51+
link_name.into(),
52+
ResourceKind::CommonAncestorOrBase,
53+
&mut |_, _| {},
54+
&gix_object::find::Never,
55+
mode,
56+
&mut buf,
57+
)
58+
.unwrap_err();
59+
60+
assert!(
61+
matches!(err, pipeline::convert_to_mergeable::Error::InvalidEntryKind {rela_path,actual}
62+
if rela_path == link_name && actual == EntryKind::Link)
63+
);
64+
assert_eq!(
65+
buf.len(),
66+
9,
67+
"input buffers are cleared only if we think they are going to be used"
68+
);
69+
drop(tmp);
70+
71+
let mut db = ObjectDb::default();
72+
let b_content = "b-content";
73+
let id = db.insert(b_content);
74+
75+
let out = filter.convert_to_mergeable(
76+
&id,
77+
EntryKind::Blob,
78+
a_name.into(),
79+
ResourceKind::CurrentOrOurs,
80+
&mut |_, _| {},
81+
&db,
82+
mode,
83+
&mut buf,
84+
)?;
85+
86+
assert_eq!(out.driver, default_driver());
87+
assert_eq!(out.data, Some(pipeline::Data::Buffer));
88+
assert_eq!(
89+
buf.as_bstr(),
90+
b_content,
91+
"there is no transformations configured, it fetched the data from the ODB"
92+
);
93+
94+
let out = filter.convert_to_mergeable(
95+
&does_not_matter,
96+
EntryKind::Blob,
97+
a_name.into(),
98+
ResourceKind::OtherOrTheirs,
99+
&mut |_, _| {},
100+
&gix_object::find::Never,
101+
mode,
102+
&mut buf,
103+
)?;
104+
assert_eq!(out.driver, default_driver());
105+
assert_eq!(out.data, None, "the lack of object in the database isn't a problem");
106+
107+
let out = filter.convert_to_mergeable(
108+
&does_not_matter,
109+
EntryKind::Blob,
110+
"does not exist on disk".into(),
111+
ResourceKind::CommonAncestorOrBase,
112+
&mut |_, _| {},
113+
&gix_object::find::Never,
114+
mode,
115+
&mut buf,
116+
)?;
117+
assert_eq!(out.driver, default_driver());
118+
assert_eq!(out.data, None, "the lack of file on disk is fine as well");
119+
}
120+
121+
Ok(())
122+
}
123+
124+
fn default_driver() -> DriverChoice {
125+
DriverChoice::BuiltIn(BuiltinDriver::Text)
126+
}
127+
128+
fn default_options() -> pipeline::Options {
129+
pipeline::Options {
130+
large_file_threshold_bytes: 0,
131+
fs: gix_fs::Capabilities {
132+
precompose_unicode: false,
133+
ignore_case: false,
134+
executable_bit: true,
135+
symlink: true,
136+
},
137+
default_driver: None,
138+
}
139+
}

0 commit comments

Comments
 (0)