blob: 4969fa4312edecf59b2b5462f7f6884bfbd4a59c [file] [log] [blame]
fn submodule(bytes: &str) -> gix_submodule::File {
gix_submodule::File::from_bytes(bytes.as_bytes(), None, &Default::default()).expect("valid module")
}
mod is_active_platform {
use std::str::FromStr;
use bstr::{BStr, ByteSlice};
fn multi_modules() -> crate::Result<gix_submodule::File> {
let modules = gix_testtools::scripted_fixture_read_only("basic.sh")?
.join("multiple")
.join(".gitmodules");
Ok(gix_submodule::File::from_bytes(
std::fs::read(&modules)?.as_slice(),
modules,
&Default::default(),
)?)
}
fn assume_valid_active_state<'a>(
module: &'a gix_submodule::File,
config: &'a gix_config::File<'static>,
defaults: gix_pathspec::Defaults,
) -> crate::Result<Vec<(&'a str, bool)>> {
assume_valid_active_state_with_attrs(module, config, defaults, |_, _, _, _| {
unreachable!("shouldn't be called")
})
}
fn assume_valid_active_state_with_attrs<'a>(
module: &'a gix_submodule::File,
config: &'a gix_config::File<'static>,
defaults: gix_pathspec::Defaults,
mut attributes: impl FnMut(
&BStr,
gix_pathspec::attributes::glob::pattern::Case,
bool,
&mut gix_pathspec::attributes::search::Outcome,
) -> bool
+ 'a,
) -> crate::Result<Vec<(&'a str, bool)>> {
let mut platform = module.is_active_platform(config, defaults)?;
Ok(module
.names()
.map(|name| {
(
name.to_str().expect("valid"),
platform
.is_active(module, config, name, &mut attributes)
.expect("valid"),
)
})
.collect())
}
#[test]
fn without_any_additional_settings_all_are_active_if_they_have_a_url() -> crate::Result {
let module = multi_modules()?;
assert_eq!(
assume_valid_active_state(&module, &Default::default(), Default::default())?,
&[
("submodule", true),
("a/b", true),
(".a/..c", true),
("a/d\\", true),
("a\\e", true)
]
);
Ok(())
}
#[test]
fn submodules_with_active_config_are_considered_active_or_inactive() -> crate::Result {
let module = multi_modules()?;
assert_eq!(
assume_valid_active_state(
&module,
&gix_config::File::from_str(
"[submodule.submodule]\n active = 0\n[submodule \"a/b\"]\n active = false"
)?,
Default::default()
)?,
&[
("submodule", false),
("a/b", false),
(".a/..c", true),
("a/d\\", true),
("a\\e", true)
]
);
Ok(())
}
#[test]
fn submodules_with_active_config_override_pathspecs() -> crate::Result {
let module = multi_modules()?;
assert_eq!(
assume_valid_active_state(
&module,
&gix_config::File::from_str(
"[submodule.submodule]\n active = 0\n[submodule]\n active = *\n[submodule]\n active = :!a*"
)?,
Default::default()
)?,
&[
("submodule", false),
("a/b", false),
(".a/..c", true),
("a/d\\", false),
("a\\e", false)
]
);
Ok(())
}
#[test]
fn pathspecs_matter_even_if_they_do_not_match() -> crate::Result {
let module = multi_modules()?;
assert_eq!(
assume_valid_active_state(
&module,
&gix_config::File::from_str("[submodule]\n active = submodule ")?,
Default::default()
)?,
&[
("submodule", true),
("a/b", false),
(".a/..c", false),
("a/d\\", false),
("a\\e", false)
]
);
assert_eq!(
assume_valid_active_state(
&module,
&gix_config::File::from_str("[submodule]\n active = :!submodule ")?,
Default::default()
)?,
&[
("submodule", false),
("a/b", true),
(".a/..c", true),
("a/d\\", true),
("a\\e", true)
]
);
Ok(())
}
}
mod path {
use gix_submodule::config::path::Error;
use crate::file::submodule;
fn submodule_path(value: &str) -> Error {
let module = submodule(&format!("[submodule.a]\npath = {value}"));
module.path("a".into()).unwrap_err()
}
#[test]
fn valid() -> crate::Result {
let module = submodule("[submodule.a]\n path = relative/path/submodule");
assert_eq!(module.path("a".into())?.as_ref(), "relative/path/submodule");
Ok(())
}
#[test]
fn validate_upon_retrieval() {
assert!(matches!(
submodule_path(if cfg!(windows) {
"c:\\\\hello"
} else {
"/definitely/absolute\\\\"
}),
Error::Absolute { .. }
));
assert!(matches!(submodule_path(""), Error::Missing { .. }));
assert!(matches!(submodule_path("../attack"), Error::OutsideOfWorktree { .. }));
{
let module = submodule("[submodule.a]\n path");
assert!(matches!(module.path("a".into()).unwrap_err(), Error::Missing { .. }));
}
{
let module = submodule("[submodule.a]\n");
assert!(matches!(module.path("a".into()).unwrap_err(), Error::Missing { .. }));
}
}
}
mod url {
use gix_submodule::config::url::Error;
use crate::file::submodule;
fn submodule_url(value: &str) -> Error {
let module = submodule(&format!("[submodule.a]\nurl = {value}"));
module.url("a".into()).unwrap_err()
}
#[test]
fn valid() -> crate::Result {
let module = submodule("[submodule.a]\n url = path-to-repo");
assert_eq!(module.url("a".into())?.to_bstring(), "path-to-repo");
Ok(())
}
#[test]
fn validate_upon_retrieval() {
assert!(matches!(submodule_url(""), Error::Missing { .. }));
{
let module = submodule("[submodule.a]\n url");
assert!(matches!(module.url("a".into()).unwrap_err(), Error::Missing { .. }));
}
{
let module = submodule("[submodule.a]\n");
assert!(matches!(module.url("a".into()).unwrap_err(), Error::Missing { .. }));
}
assert!(matches!(submodule_url("file://"), Error::Parse { .. }));
}
}
mod update {
use std::str::FromStr;
use gix_submodule::config::{update::Error, Update};
use crate::file::submodule;
fn submodule_update(value: &str) -> Error {
let module = submodule(&format!("[submodule.a]\nupdate = {value}"));
module.update("a".into()).unwrap_err()
}
#[test]
fn default() {
assert_eq!(Update::default(), Update::Checkout, "as defined in the docs");
}
#[test]
fn valid() -> crate::Result {
for (valid, expected) in [
("checkout", Update::Checkout),
("rebase", Update::Rebase),
("merge", Update::Merge),
("none", Update::None),
] {
let module = submodule(&format!("[submodule.a]\n update = {valid}"));
assert_eq!(module.update("a".into())?.expect("present"), expected);
}
Ok(())
}
#[test]
fn valid_in_overrides() -> crate::Result {
let mut module = submodule("[submodule.a]\n update = merge");
let repo_config = gix_config::File::from_str("[submodule.a]\n update = !dangerous")?;
let prev_names = module.names().map(ToOwned::to_owned).collect::<Vec<_>>();
module.append_submodule_overrides(&repo_config);
assert_eq!(
module.update("a".into())?.expect("present"),
Update::Command("dangerous".into()),
"overridden values are picked up and make commands possible - these are local"
);
assert_eq!(
module.names().map(ToOwned::to_owned).collect::<Vec<_>>(),
prev_names,
"Appending more configuration sections doesn't affect name listing"
);
Ok(())
}
#[test]
fn validate_upon_retrieval() {
assert!(matches!(submodule_update(""), Error::Invalid { .. }));
assert!(matches!(submodule_update("bogus"), Error::Invalid { .. }));
assert!(
matches!(
submodule_update("!dangerous"),
Error::CommandForbiddenInModulesConfiguration { .. }
),
"forbidden unless it's an override"
);
}
}
mod fetch_recurse {
use gix_submodule::config::FetchRecurse;
use crate::file::submodule;
#[test]
fn default() {
assert_eq!(
FetchRecurse::default(),
FetchRecurse::OnDemand,
"as defined in git codebase actually"
);
}
#[test]
fn valid() -> crate::Result {
for (valid, expected) in [
("yes", FetchRecurse::Always),
("true", FetchRecurse::Always),
("", FetchRecurse::Never),
("no", FetchRecurse::Never),
("false", FetchRecurse::Never),
("on-demand", FetchRecurse::OnDemand),
] {
let module = submodule(&format!("[submodule.a]\n fetchRecurseSubmodules = {valid}"));
assert_eq!(module.fetch_recurse("a".into())?.expect("present"), expected);
}
let module = submodule("[submodule.a]\n fetchRecurseSubmodules");
assert_eq!(
module.fetch_recurse("a".into())?.expect("present"),
FetchRecurse::Always,
"no value means true, which means to always recurse"
);
Ok(())
}
#[test]
fn validate_upon_retrieval() -> crate::Result {
for invalid in ["foo", "ney", "On-demand"] {
let module = submodule(&format!("[submodule.a]\n fetchRecurseSubmodules = \"{invalid}\""));
assert!(module.fetch_recurse("a".into()).is_err());
}
Ok(())
}
}
mod ignore {
use gix_submodule::config::Ignore;
use crate::file::submodule;
#[test]
fn default() {
assert_eq!(Ignore::default(), Ignore::None, "as defined in the docs");
}
#[test]
fn valid() -> crate::Result {
for (valid, expected) in [
("all", Ignore::All),
("dirty", Ignore::Dirty),
("untracked", Ignore::Untracked),
("none", Ignore::None),
] {
let module = submodule(&format!("[submodule.a]\n ignore = {valid}"));
assert_eq!(module.ignore("a".into())?.expect("present"), expected);
}
let module = submodule("[submodule.a]\n ignore");
assert!(
module.ignore("a".into())?.is_none(),
"no value is interpreted as non-existing string, hence the caller will see None"
);
Ok(())
}
#[test]
fn validate_upon_retrieval() -> crate::Result {
for invalid in ["All", ""] {
let module = submodule(&format!("[submodule.a]\n ignore = \"{invalid}\""));
assert!(module.ignore("a".into()).is_err());
}
Ok(())
}
}
mod branch {
use gix_submodule::config::Branch;
use crate::file::submodule;
#[test]
fn valid() -> crate::Result {
for (valid, expected) in [
(".", Branch::CurrentInSuperproject),
("", Branch::Name("HEAD".into())),
("master", Branch::Name("master".into())),
("feature/a", Branch::Name("feature/a".into())),
(
"abcde12345abcde12345abcde12345abcde12345",
Branch::Name("abcde12345abcde12345abcde12345abcde12345".into()),
),
] {
let module = submodule(&format!("[submodule.a]\n branch = {valid}"));
assert_eq!(module.branch("a".into())?.expect("present"), expected);
}
let module = submodule("[submodule.a]\n branch");
assert!(
module.branch("a".into())?.is_none(),
"no value implies it's not set, but the caller will then default"
);
Ok(())
}
#[test]
fn validate_upon_retrieval() -> crate::Result {
let module = submodule("[submodule.a]\n branch = /invalid");
assert!(module.branch("a".into()).is_err());
Ok(())
}
}
#[test]
fn shallow() -> crate::Result {
let module = submodule("[submodule.a]\n shallow");
assert_eq!(
module.shallow("a".into())?,
Some(true),
"shallow is a simple boolean without anything special (yet)"
);
Ok(())
}
mod append_submodule_overrides {
use std::str::FromStr;
use crate::file::submodule;
#[test]
fn last_of_multiple_values_wins() -> crate::Result {
let mut module = submodule("[submodule.a] url = from-module");
let repo_config =
gix_config::File::from_str("[submodule.a]\n url = a\n url = b\n ignore = x\n [submodule.a]\n url = c\n[submodule.b] url = not-relevant")?;
module.append_submodule_overrides(&repo_config);
Ok(())
}
}
mod baseline;