summaryrefslogtreecommitdiffstats
path: root/intl/l10n/rust/l10nregistry-rs/tests
diff options
context:
space:
mode:
Diffstat (limited to 'intl/l10n/rust/l10nregistry-rs/tests')
-rw-r--r--intl/l10n/rust/l10nregistry-rs/tests/localization.rs201
-rw-r--r--intl/l10n/rust/l10nregistry-rs/tests/registry.rs304
-rw-r--r--intl/l10n/rust/l10nregistry-rs/tests/scenarios_async.rs109
-rw-r--r--intl/l10n/rust/l10nregistry-rs/tests/scenarios_sync.rs107
-rw-r--r--intl/l10n/rust/l10nregistry-rs/tests/source.rs305
5 files changed, 1026 insertions, 0 deletions
diff --git a/intl/l10n/rust/l10nregistry-rs/tests/localization.rs b/intl/l10n/rust/l10nregistry-rs/tests/localization.rs
new file mode 100644
index 0000000000..dd6d7d9c9f
--- /dev/null
+++ b/intl/l10n/rust/l10nregistry-rs/tests/localization.rs
@@ -0,0 +1,201 @@
+use std::borrow::Cow;
+
+use fluent_fallback::{
+ env::LocalesProvider,
+ types::{L10nKey, ResourceId},
+ Localization,
+};
+use l10nregistry::testing::{
+ FileSource, MockBundleAdapter, RegistrySetup, TestEnvironment, TestFileFetcher,
+};
+use serial_test::serial;
+use unic_langid::{langid, LanguageIdentifier};
+
+type L10nRegistry = l10nregistry::registry::L10nRegistry<TestEnvironment, MockBundleAdapter>;
+
+static LOCALES: &[LanguageIdentifier] = &[langid!("pl"), langid!("en-US")];
+static mut FILE_FETCHER: Option<TestFileFetcher> = None;
+static mut L10N_REGISTRY: Option<L10nRegistry> = None;
+
+const FTL_RESOURCE: &str = "toolkit/updates/history.ftl";
+const L10N_ID_PL_EN: (&str, Option<&str>) = ("history-title", Some("Historia aktualizacji"));
+const L10N_ID_MISSING: (&str, Option<&str>) = ("missing-id", None);
+const L10N_ID_ONLY_EN: (&str, Option<&str>) = (
+ "history-intro",
+ Some("The following updates have been installed"),
+);
+
+fn get_file_fetcher() -> &'static TestFileFetcher {
+ let fetcher: &mut Option<TestFileFetcher> = unsafe { &mut FILE_FETCHER };
+
+ fetcher.get_or_insert_with(|| TestFileFetcher::new())
+}
+
+fn get_l10n_registry() -> &'static L10nRegistry {
+ let reg: &mut Option<L10nRegistry> = unsafe { &mut L10N_REGISTRY };
+
+ reg.get_or_insert_with(|| {
+ let fetcher = get_file_fetcher();
+ let setup = RegistrySetup::new(
+ "test",
+ vec![
+ FileSource::new(
+ "toolkit",
+ None,
+ get_app_locales().to_vec(),
+ "toolkit/{locale}/",
+ ),
+ FileSource::new(
+ "browser",
+ None,
+ get_app_locales().to_vec(),
+ "browser/{locale}/",
+ ),
+ ],
+ get_app_locales().to_vec(),
+ );
+ fetcher.get_registry_and_environment(setup).1
+ })
+}
+
+fn get_app_locales() -> &'static [LanguageIdentifier] {
+ LOCALES
+}
+
+struct LocalesService;
+
+impl LocalesProvider for LocalesService {
+ type Iter = std::vec::IntoIter<LanguageIdentifier>;
+
+ fn locales(&self) -> Self::Iter {
+ get_app_locales().to_vec().into_iter()
+ }
+}
+
+fn sync_localization(
+ reg: &'static L10nRegistry,
+ res_ids: Vec<ResourceId>,
+) -> Localization<L10nRegistry, LocalesService> {
+ Localization::with_env(res_ids, true, LocalesService, reg.clone())
+}
+
+fn async_localization(
+ reg: &'static L10nRegistry,
+ res_ids: Vec<ResourceId>,
+) -> Localization<L10nRegistry, LocalesService> {
+ Localization::with_env(res_ids, false, LocalesService, reg.clone())
+}
+
+fn setup_sync_test() -> Localization<L10nRegistry, LocalesService> {
+ sync_localization(get_l10n_registry(), vec![FTL_RESOURCE.into()])
+}
+
+fn setup_async_test() -> Localization<L10nRegistry, LocalesService> {
+ async_localization(get_l10n_registry(), vec![FTL_RESOURCE.into()])
+}
+
+#[test]
+#[serial]
+fn localization_format_value_sync() {
+ let loc = setup_sync_test();
+ let bundles = loc.bundles();
+ let mut errors = vec![];
+
+ for query in &[L10N_ID_PL_EN, L10N_ID_MISSING, L10N_ID_ONLY_EN] {
+ let value = bundles
+ .format_value_sync(query.0, None, &mut errors)
+ .unwrap();
+ assert_eq!(value, query.1.map(|s| Cow::Borrowed(s)));
+ }
+
+ assert_eq!(errors.len(), 4);
+}
+
+#[test]
+#[serial]
+fn localization_format_values_sync() {
+ let loc = setup_sync_test();
+ let bundles = loc.bundles();
+ let mut errors = vec![];
+
+ let ids = &[L10N_ID_PL_EN, L10N_ID_MISSING, L10N_ID_ONLY_EN];
+ let keys = ids
+ .iter()
+ .map(|query| L10nKey {
+ id: query.0.into(),
+ args: None,
+ })
+ .collect::<Vec<_>>();
+
+ let values = bundles.format_values_sync(&keys, &mut errors).unwrap();
+
+ assert_eq!(values.len(), ids.len());
+
+ for (value, query) in values.iter().zip(ids) {
+ if let Some(expected) = query.1 {
+ assert_eq!(*value, Some(Cow::Borrowed(expected)));
+ }
+ }
+ assert_eq!(errors.len(), 4);
+}
+
+#[tokio::test]
+#[serial]
+async fn localization_format_value_async() {
+ let loc = setup_async_test();
+ let bundles = loc.bundles();
+ let mut errors = vec![];
+
+ for query in &[L10N_ID_PL_EN, L10N_ID_MISSING, L10N_ID_ONLY_EN] {
+ let value = bundles.format_value(query.0, None, &mut errors).await;
+ if let Some(expected) = query.1 {
+ assert_eq!(value, Some(Cow::Borrowed(expected)));
+ }
+ }
+}
+
+#[tokio::test]
+#[serial]
+async fn localization_format_values_async() {
+ let loc = setup_async_test();
+ let bundles = loc.bundles();
+ let mut errors = vec![];
+
+ let ids = &[L10N_ID_PL_EN, L10N_ID_MISSING, L10N_ID_ONLY_EN];
+ let keys = ids
+ .iter()
+ .map(|query| L10nKey {
+ id: query.0.into(),
+ args: None,
+ })
+ .collect::<Vec<_>>();
+
+ let values = bundles.format_values(&keys, &mut errors).await;
+
+ assert_eq!(values.len(), ids.len());
+
+ for (value, query) in values.iter().zip(ids) {
+ if let Some(expected) = query.1 {
+ assert_eq!(*value, Some(Cow::Borrowed(expected)));
+ }
+ }
+}
+
+#[tokio::test]
+#[serial]
+async fn localization_upgrade() {
+ let mut loc = setup_sync_test();
+ let bundles = loc.bundles();
+ let mut errors = vec![];
+ let value = bundles
+ .format_value_sync(L10N_ID_PL_EN.0, None, &mut errors)
+ .unwrap();
+ assert_eq!(value, L10N_ID_PL_EN.1.map(|s| Cow::Borrowed(s)));
+
+ loc.set_async();
+ let bundles = loc.bundles();
+ let value = bundles
+ .format_value(L10N_ID_PL_EN.0, None, &mut errors)
+ .await;
+ assert_eq!(value, L10N_ID_PL_EN.1.map(|s| Cow::Borrowed(s)));
+}
diff --git a/intl/l10n/rust/l10nregistry-rs/tests/registry.rs b/intl/l10n/rust/l10nregistry-rs/tests/registry.rs
new file mode 100644
index 0000000000..2a7569b7aa
--- /dev/null
+++ b/intl/l10n/rust/l10nregistry-rs/tests/registry.rs
@@ -0,0 +1,304 @@
+use l10nregistry::testing::{FileSource, RegistrySetup, TestFileFetcher};
+use unic_langid::LanguageIdentifier;
+
+static FTL_RESOURCE_TOOLKIT: &str = "toolkit/global/textActions.ftl";
+static FTL_RESOURCE_BROWSER: &str = "branding/brand.ftl";
+
+#[test]
+fn test_get_sources_for_resource() {
+ let en_us: LanguageIdentifier = "en-US".parse().unwrap();
+ let setup = RegistrySetup::new(
+ "test",
+ vec![
+ FileSource::new("toolkit", None, vec![en_us.clone()], "toolkit/{locale}/"),
+ FileSource::new("browser", None, vec![en_us.clone()], "browser/{locale}/"),
+ ],
+ vec![en_us.clone()],
+ );
+ let fetcher = TestFileFetcher::new();
+ let (_, reg) = fetcher.get_registry_and_environment(setup);
+
+ {
+ let metasources = reg
+ .try_borrow_metasources()
+ .expect("Unable to borrow metasources.");
+
+ let toolkit = metasources.file_source_by_name(0, "toolkit").unwrap();
+ let browser = metasources.file_source_by_name(0, "browser").unwrap();
+ let toolkit_resource_id = FTL_RESOURCE_TOOLKIT.into();
+
+ let mut i = metasources.get_sources_for_resource(0, &en_us, &toolkit_resource_id);
+
+ assert_eq!(i.next(), Some(toolkit));
+ assert_eq!(i.next(), Some(browser));
+ assert_eq!(i.next(), None);
+
+ assert!(browser
+ .fetch_file_sync(&en_us, &FTL_RESOURCE_TOOLKIT.into(), false)
+ .is_none());
+
+ let mut i = metasources.get_sources_for_resource(0, &en_us, &toolkit_resource_id);
+ assert_eq!(i.next(), Some(toolkit));
+ assert_eq!(i.next(), None);
+
+ assert!(toolkit
+ .fetch_file_sync(&en_us, &FTL_RESOURCE_TOOLKIT.into(), false)
+ .is_some());
+
+ let mut i = metasources.get_sources_for_resource(0, &en_us, &toolkit_resource_id);
+ assert_eq!(i.next(), Some(toolkit));
+ assert_eq!(i.next(), None);
+ }
+}
+
+#[test]
+fn test_generate_bundles_for_lang_sync() {
+ let en_us: LanguageIdentifier = "en-US".parse().unwrap();
+ let setup = RegistrySetup::new(
+ "test",
+ vec![
+ FileSource::new("toolkit", None, vec![en_us.clone()], "toolkit/{locale}/"),
+ FileSource::new("browser", None, vec![en_us.clone()], "browser/{locale}/"),
+ ],
+ vec![en_us.clone()],
+ );
+ let fetcher = TestFileFetcher::new();
+ let (_, reg) = fetcher.get_registry_and_environment(setup);
+
+ let paths = vec![FTL_RESOURCE_TOOLKIT.into(), FTL_RESOURCE_BROWSER.into()];
+ let mut i = reg.generate_bundles_for_lang_sync(en_us.clone(), paths);
+
+ assert!(i.next().is_some());
+ assert!(i.next().is_none());
+}
+
+#[test]
+fn test_generate_bundles_sync() {
+ let en_us: LanguageIdentifier = "en-US".parse().unwrap();
+ let setup = RegistrySetup::new(
+ "test",
+ vec![
+ FileSource::new("toolkit", None, vec![en_us.clone()], "toolkit/{locale}/"),
+ FileSource::new("browser", None, vec![en_us.clone()], "browser/{locale}/"),
+ ],
+ vec![en_us.clone()],
+ );
+ let fetcher = TestFileFetcher::new();
+ let (_, reg) = fetcher.get_registry_and_environment(setup);
+
+ let paths = vec![FTL_RESOURCE_TOOLKIT.into(), FTL_RESOURCE_BROWSER.into()];
+ let lang_ids = vec![en_us];
+ let mut i = reg.generate_bundles_sync(lang_ids.into_iter(), paths);
+
+ assert!(i.next().is_some());
+ assert!(i.next().is_none());
+}
+
+#[tokio::test]
+async fn test_generate_bundles_for_lang() {
+ use futures::stream::StreamExt;
+
+ let en_us: LanguageIdentifier = "en-US".parse().unwrap();
+ let setup = RegistrySetup::new(
+ "test",
+ vec![
+ FileSource::new("toolkit", None, vec![en_us.clone()], "toolkit/{locale}/"),
+ FileSource::new("browser", None, vec![en_us.clone()], "browser/{locale}/"),
+ ],
+ vec![en_us.clone()],
+ );
+ let fetcher = TestFileFetcher::new();
+ let (_, reg) = fetcher.get_registry_and_environment(setup);
+
+ let paths = vec![FTL_RESOURCE_TOOLKIT.into(), FTL_RESOURCE_BROWSER.into()];
+ let mut i = reg
+ .generate_bundles_for_lang(en_us, paths)
+ .expect("Failed to get GenerateBundles.");
+
+ assert!(i.next().await.is_some());
+ assert!(i.next().await.is_none());
+}
+
+#[tokio::test]
+async fn test_generate_bundles() {
+ use futures::stream::StreamExt;
+
+ let en_us: LanguageIdentifier = "en-US".parse().unwrap();
+ let setup = RegistrySetup::new(
+ "test",
+ vec![
+ FileSource::new("toolkit", None, vec![en_us.clone()], "toolkit/{locale}/"),
+ FileSource::new("browser", None, vec![en_us.clone()], "browser/{locale}/"),
+ ],
+ vec![en_us.clone()],
+ );
+ let fetcher = TestFileFetcher::new();
+ let (_, reg) = fetcher.get_registry_and_environment(setup);
+
+ let paths = vec![FTL_RESOURCE_TOOLKIT.into(), FTL_RESOURCE_BROWSER.into()];
+ let langs = vec![en_us];
+ let mut i = reg
+ .generate_bundles(langs.into_iter(), paths)
+ .expect("Failed to get GenerateBundles.");
+
+ assert!(i.next().await.is_some());
+ assert!(i.next().await.is_none());
+}
+
+#[test]
+fn test_manage_sources() {
+ let en_us: LanguageIdentifier = "en-US".parse().unwrap();
+ let setup = RegistrySetup::new(
+ "test",
+ vec![
+ FileSource::new("toolkit", None, vec![en_us.clone()], "toolkit/{locale}/"),
+ FileSource::new("browser", None, vec![en_us.clone()], "browser/{locale}/"),
+ ],
+ vec![en_us.clone()],
+ );
+ let fetcher = TestFileFetcher::new();
+ let (_, reg) = fetcher.get_registry_and_environment(setup);
+
+ let lang_ids = vec![en_us];
+
+ let paths = vec![FTL_RESOURCE_TOOLKIT.into(), FTL_RESOURCE_BROWSER.into()];
+
+ let mut i = reg.generate_bundles_sync(lang_ids.clone().into_iter(), paths);
+
+ assert!(i.next().is_some());
+ assert!(i.next().is_none());
+
+ reg.clone()
+ .remove_sources(vec!["toolkit"])
+ .expect("Failed to remove a source.");
+
+ let paths = vec![FTL_RESOURCE_TOOLKIT.into(), FTL_RESOURCE_BROWSER.into()];
+ let mut i = reg.generate_bundles_sync(lang_ids.clone().into_iter(), paths);
+ assert!(i.next().is_none());
+
+ let paths = vec![FTL_RESOURCE_BROWSER.into()];
+ let mut i = reg.generate_bundles_sync(lang_ids.clone().into_iter(), paths);
+ assert!(i.next().is_some());
+ assert!(i.next().is_none());
+
+ reg.register_sources(vec![fetcher.get_test_file_source(
+ "toolkit",
+ None,
+ lang_ids.clone(),
+ "browser/{locale}/",
+ )])
+ .expect("Failed to register a source.");
+
+ let paths = vec![FTL_RESOURCE_TOOLKIT.into(), FTL_RESOURCE_BROWSER.into()];
+ let mut i = reg.generate_bundles_sync(lang_ids.clone().into_iter(), paths);
+ assert!(i.next().is_none());
+
+ reg.update_sources(vec![fetcher.get_test_file_source(
+ "toolkit",
+ None,
+ lang_ids.clone(),
+ "toolkit/{locale}/",
+ )])
+ .expect("Failed to update a source.");
+
+ let paths = vec![FTL_RESOURCE_TOOLKIT.into(), FTL_RESOURCE_BROWSER.into()];
+ let mut i = reg.generate_bundles_sync(lang_ids.clone().into_iter(), paths);
+ assert!(i.next().is_some());
+ assert!(i.next().is_none());
+}
+
+#[test]
+fn test_generate_bundles_with_metasources_sync() {
+ let en_us: LanguageIdentifier = "en-US".parse().unwrap();
+ let setup = RegistrySetup::new(
+ "test",
+ vec![
+ FileSource::new(
+ "toolkit",
+ Some("app"),
+ vec![en_us.clone()],
+ "toolkit/{locale}/",
+ ),
+ FileSource::new(
+ "browser",
+ Some("app"),
+ vec![en_us.clone()],
+ "browser/{locale}/",
+ ),
+ FileSource::new(
+ "toolkit",
+ Some("langpack"),
+ vec![en_us.clone()],
+ "toolkit/{locale}/",
+ ),
+ FileSource::new(
+ "browser",
+ Some("langpack"),
+ vec![en_us.clone()],
+ "browser/{locale}/",
+ ),
+ ],
+ vec![en_us.clone()],
+ );
+ let fetcher = TestFileFetcher::new();
+ let (_, reg) = fetcher.get_registry_and_environment(setup);
+
+ let paths = vec![FTL_RESOURCE_TOOLKIT.into(), FTL_RESOURCE_BROWSER.into()];
+ let lang_ids = vec![en_us];
+ let mut i = reg.generate_bundles_sync(lang_ids.into_iter(), paths);
+
+ assert!(i.next().is_some());
+ assert!(i.next().is_some());
+ assert!(i.next().is_none());
+}
+
+#[tokio::test]
+async fn test_generate_bundles_with_metasources() {
+ use futures::stream::StreamExt;
+
+ let en_us: LanguageIdentifier = "en-US".parse().unwrap();
+
+ let setup = RegistrySetup::new(
+ "test",
+ vec![
+ FileSource::new(
+ "toolkit",
+ Some("app"),
+ vec![en_us.clone()],
+ "toolkit/{locale}/",
+ ),
+ FileSource::new(
+ "browser",
+ Some("app"),
+ vec![en_us.clone()],
+ "browser/{locale}/",
+ ),
+ FileSource::new(
+ "toolkit",
+ Some("langpack"),
+ vec![en_us.clone()],
+ "toolkit/{locale}/",
+ ),
+ FileSource::new(
+ "browser",
+ Some("langpack"),
+ vec![en_us.clone()],
+ "browser/{locale}/",
+ ),
+ ],
+ vec![en_us.clone()],
+ );
+
+ let fetcher = TestFileFetcher::new();
+ let (_, reg) = fetcher.get_registry_and_environment(setup);
+
+ let paths = vec![FTL_RESOURCE_TOOLKIT.into(), FTL_RESOURCE_BROWSER.into()];
+ let langs = vec![en_us];
+ let mut i = reg
+ .generate_bundles(langs.into_iter(), paths)
+ .expect("Failed to get GenerateBundles.");
+
+ assert!(i.next().await.is_some());
+ assert!(i.next().await.is_some());
+ assert!(i.next().await.is_none());
+}
diff --git a/intl/l10n/rust/l10nregistry-rs/tests/scenarios_async.rs b/intl/l10n/rust/l10nregistry-rs/tests/scenarios_async.rs
new file mode 100644
index 0000000000..839cd4b3d8
--- /dev/null
+++ b/intl/l10n/rust/l10nregistry-rs/tests/scenarios_async.rs
@@ -0,0 +1,109 @@
+use fluent_bundle::FluentArgs;
+use fluent_fallback::Localization;
+use fluent_testing::get_scenarios;
+use l10nregistry::fluent::FluentBundle;
+use l10nregistry::registry::BundleAdapter;
+use l10nregistry::testing::{RegistrySetup, TestFileFetcher};
+
+#[derive(Clone)]
+struct ScenarioBundleAdapter {}
+
+impl BundleAdapter for ScenarioBundleAdapter {
+ fn adapt_bundle(&self, bundle: &mut FluentBundle) {
+ bundle.set_use_isolating(false);
+ bundle
+ .add_function("PLATFORM", |_positional, _named| "linux".into())
+ .expect("Failed to add a function to the bundle.");
+ }
+}
+
+#[tokio::test]
+async fn scenarios_async() {
+ use fluent_testing::scenarios::structs::Scenario;
+ let fetcher = TestFileFetcher::new();
+
+ let scenarios = get_scenarios();
+
+ let adapter = ScenarioBundleAdapter {};
+
+ let cannot_produce_bundle = |scenario: &Scenario| {
+ scenario
+ .queries
+ .iter()
+ .any(|query| query.exceptional_context.blocks_bundle_generation())
+ };
+
+ for scenario in scenarios {
+ println!("scenario: {}", scenario.name);
+ let setup: RegistrySetup = (&scenario).into();
+ let (env, reg) = fetcher.get_registry_and_environment_with_adapter(setup, adapter.clone());
+
+ let loc = Localization::with_env(scenario.res_ids.clone(), false, env.clone(), reg);
+ let bundles = loc.bundles();
+ let no_bundles = cannot_produce_bundle(&scenario);
+
+ let mut errors = vec![];
+
+ for query in scenario.queries.iter() {
+ let errors_start_len = errors.len();
+
+ let args = query.input.args.as_ref().map(|args| {
+ let mut result = FluentArgs::new();
+ for arg in args.as_slice() {
+ result.set(arg.id.clone(), arg.value.clone());
+ }
+ result
+ });
+
+ if let Some(output) = &query.output {
+ if let Some(value) = &output.value {
+ let v = bundles
+ .format_value(&query.input.id, args.as_ref(), &mut errors)
+ .await;
+ if no_bundles || query.exceptional_context.causes_failed_value_lookup() {
+ assert!(v.is_none());
+ if no_bundles {
+ continue;
+ }
+ } else {
+ assert_eq!(v.unwrap(), value.as_str())
+ }
+ }
+ }
+
+ if query.exceptional_context.causes_reported_format_error() {
+ assert!(
+ errors.len() > errors_start_len,
+ "expected reported errors for query {:#?}",
+ query
+ );
+ } else {
+ assert_eq!(
+ errors.len(),
+ errors_start_len,
+ "expected no reported errors for query {:#?}",
+ query
+ );
+ }
+ }
+
+ if scenario
+ .queries
+ .iter()
+ .any(|query| query.exceptional_context.missing_required_resource())
+ {
+ assert!(
+ !env.errors().is_empty(),
+ "expected errors for scenario {{ {} }}, but found none",
+ scenario.name
+ );
+ } else {
+ assert!(
+ env.errors().is_empty(),
+ "expected no errors for scenario {{ {} }}, but found {:#?}",
+ scenario.name,
+ env.errors()
+ );
+ }
+ }
+}
diff --git a/intl/l10n/rust/l10nregistry-rs/tests/scenarios_sync.rs b/intl/l10n/rust/l10nregistry-rs/tests/scenarios_sync.rs
new file mode 100644
index 0000000000..280b32e927
--- /dev/null
+++ b/intl/l10n/rust/l10nregistry-rs/tests/scenarios_sync.rs
@@ -0,0 +1,107 @@
+use fluent_bundle::FluentArgs;
+use fluent_fallback::Localization;
+use fluent_testing::get_scenarios;
+use l10nregistry::fluent::FluentBundle;
+use l10nregistry::registry::BundleAdapter;
+use l10nregistry::testing::{RegistrySetup, TestFileFetcher};
+
+#[derive(Clone)]
+struct ScenarioBundleAdapter {}
+
+impl BundleAdapter for ScenarioBundleAdapter {
+ fn adapt_bundle(&self, bundle: &mut FluentBundle) {
+ bundle.set_use_isolating(false);
+ bundle
+ .add_function("PLATFORM", |_positional, _named| "linux".into())
+ .expect("Failed to add a function to the bundle.");
+ }
+}
+
+#[test]
+fn scenarios_sync() {
+ use fluent_testing::scenarios::structs::Scenario;
+ let fetcher = TestFileFetcher::new();
+
+ let scenarios = get_scenarios();
+
+ let adapter = ScenarioBundleAdapter {};
+
+ let cannot_produce_bundle = |scenario: &Scenario| {
+ scenario
+ .queries
+ .iter()
+ .any(|query| query.exceptional_context.blocks_bundle_generation())
+ };
+
+ for scenario in scenarios {
+ println!("scenario: {}", scenario.name);
+ let setup: RegistrySetup = (&scenario).into();
+ let (env, reg) = fetcher.get_registry_and_environment_with_adapter(setup, adapter.clone());
+
+ let loc = Localization::with_env(scenario.res_ids.clone(), true, env.clone(), reg);
+ let bundles = loc.bundles();
+ let no_bundles = cannot_produce_bundle(&scenario);
+
+ let mut errors = vec![];
+
+ for query in scenario.queries.iter() {
+ let errors_start_len = errors.len();
+
+ let args = query.input.args.as_ref().map(|args| {
+ let mut result = FluentArgs::new();
+ for arg in args.as_slice() {
+ result.set(arg.id.clone(), arg.value.clone());
+ }
+ result
+ });
+
+ if let Some(output) = &query.output {
+ if let Some(value) = &output.value {
+ let v = bundles.format_value_sync(&query.input.id, args.as_ref(), &mut errors);
+ if no_bundles || query.exceptional_context.causes_failed_value_lookup() {
+ assert!(v.is_err() || v.unwrap().is_none());
+ if no_bundles {
+ continue;
+ }
+ } else {
+ assert_eq!(v.unwrap().unwrap(), value.as_str())
+ }
+ }
+ }
+
+ if query.exceptional_context.causes_reported_format_error() {
+ assert!(
+ errors.len() > errors_start_len,
+ "expected reported errors for query {:#?}",
+ query
+ );
+ } else {
+ assert_eq!(
+ errors.len(),
+ errors_start_len,
+ "expected no reported errors for query {:#?}",
+ query
+ );
+ }
+ }
+
+ if scenario
+ .queries
+ .iter()
+ .any(|query| query.exceptional_context.missing_required_resource())
+ {
+ assert!(
+ !env.errors().is_empty(),
+ "expected errors for scenario {{ {} }}, but found none",
+ scenario.name
+ );
+ } else {
+ assert!(
+ env.errors().is_empty(),
+ "expected no errors for scenario {{ {} }}, but found {:#?}",
+ scenario.name,
+ env.errors()
+ );
+ }
+ }
+}
diff --git a/intl/l10n/rust/l10nregistry-rs/tests/source.rs b/intl/l10n/rust/l10nregistry-rs/tests/source.rs
new file mode 100644
index 0000000000..a54ff23438
--- /dev/null
+++ b/intl/l10n/rust/l10nregistry-rs/tests/source.rs
@@ -0,0 +1,305 @@
+use fluent_fallback::types::{ResourceType, ToResourceId};
+use futures::future::join_all;
+use l10nregistry::testing::TestFileFetcher;
+use unic_langid::LanguageIdentifier;
+
+static FTL_RESOURCE_PRESENT: &str = "toolkit/global/textActions.ftl";
+static FTL_RESOURCE_MISSING: &str = "missing.ftl";
+
+#[test]
+fn test_fetch_sync() {
+ let fetcher = TestFileFetcher::new();
+ let en_us: LanguageIdentifier = "en-US".parse().unwrap();
+
+ let fs1 =
+ fetcher.get_test_file_source("toolkit", None, vec![en_us.clone()], "toolkit/{locale}/");
+
+ let file = fs1.fetch_file_sync(&en_us, &FTL_RESOURCE_PRESENT.into(), false);
+ assert!(file.is_some());
+ assert!(!file.is_none());
+ assert!(!file.is_required_and_missing());
+
+ let file = fs1.fetch_file_sync(
+ &en_us,
+ &FTL_RESOURCE_PRESENT.to_resource_id(ResourceType::Required),
+ false,
+ );
+ assert!(file.is_some());
+ assert!(!file.is_none());
+ assert!(!file.is_required_and_missing());
+
+ let file = fs1.fetch_file_sync(
+ &en_us,
+ &FTL_RESOURCE_PRESENT.to_resource_id(ResourceType::Optional),
+ false,
+ );
+ assert!(file.is_some());
+ assert!(!file.is_none());
+ assert!(!file.is_required_and_missing());
+
+ let file = fs1.fetch_file_sync(&en_us, &FTL_RESOURCE_MISSING.into(), false);
+ assert!(!file.is_some());
+ assert!(file.is_none());
+ assert!(file.is_required_and_missing());
+
+ let file = fs1.fetch_file_sync(
+ &en_us,
+ &FTL_RESOURCE_MISSING.to_resource_id(ResourceType::Required),
+ false,
+ );
+ assert!(!file.is_some());
+ assert!(file.is_none());
+ assert!(file.is_required_and_missing());
+
+ let file = fs1.fetch_file_sync(
+ &en_us,
+ &FTL_RESOURCE_MISSING.to_resource_id(ResourceType::Optional),
+ false,
+ );
+ assert!(!file.is_some());
+ assert!(file.is_none());
+ assert!(!file.is_required_and_missing());
+}
+
+#[tokio::test]
+async fn test_fetch_async() {
+ let fetcher = TestFileFetcher::new();
+ let en_us: LanguageIdentifier = "en-US".parse().unwrap();
+
+ let fs1 =
+ fetcher.get_test_file_source("toolkit", None, vec![en_us.clone()], "toolkit/{locale}/");
+
+ let file = fs1.fetch_file(&en_us, &FTL_RESOURCE_PRESENT.into()).await;
+ assert!(file.is_some());
+ assert!(!file.is_none());
+ assert!(!file.is_required_and_missing());
+
+ let file = fs1
+ .fetch_file(
+ &en_us,
+ &FTL_RESOURCE_PRESENT.to_resource_id(ResourceType::Required),
+ )
+ .await;
+ assert!(file.is_some());
+ assert!(!file.is_none());
+ assert!(!file.is_required_and_missing());
+
+ let file = fs1
+ .fetch_file(
+ &en_us,
+ &FTL_RESOURCE_PRESENT.to_resource_id(ResourceType::Optional),
+ )
+ .await;
+ assert!(file.is_some());
+ assert!(!file.is_none());
+ assert!(!file.is_required_and_missing());
+
+ let file = fs1.fetch_file(&en_us, &FTL_RESOURCE_MISSING.into()).await;
+ assert!(!file.is_some());
+ assert!(file.is_none());
+ assert!(file.is_required_and_missing());
+
+ let file = fs1
+ .fetch_file(
+ &en_us,
+ &FTL_RESOURCE_MISSING.to_resource_id(ResourceType::Required),
+ )
+ .await;
+ assert!(!file.is_some());
+ assert!(file.is_none());
+ assert!(file.is_required_and_missing());
+
+ let file = fs1
+ .fetch_file(
+ &en_us,
+ &FTL_RESOURCE_MISSING.to_resource_id(ResourceType::Optional),
+ )
+ .await;
+ assert!(!file.is_some());
+ assert!(file.is_none());
+ assert!(!file.is_required_and_missing());
+}
+
+#[tokio::test]
+async fn test_fetch_sync_2_async() {
+ let fetcher = TestFileFetcher::new();
+ let en_us: LanguageIdentifier = "en-US".parse().unwrap();
+
+ let fs1 =
+ fetcher.get_test_file_source("toolkit", None, vec![en_us.clone()], "toolkit/{locale}/");
+
+ assert!(fs1
+ .fetch_file_sync(&en_us, &FTL_RESOURCE_PRESENT.into(), false)
+ .is_some());
+ assert!(fs1
+ .fetch_file(&en_us, &FTL_RESOURCE_PRESENT.into())
+ .await
+ .is_some());
+ assert!(fs1
+ .fetch_file_sync(&en_us, &FTL_RESOURCE_PRESENT.into(), false)
+ .is_some());
+}
+
+#[tokio::test]
+async fn test_fetch_async_2_sync() {
+ let fetcher = TestFileFetcher::new();
+ let en_us: LanguageIdentifier = "en-US".parse().unwrap();
+
+ let fs1 =
+ fetcher.get_test_file_source("toolkit", None, vec![en_us.clone()], "toolkit/{locale}/");
+
+ assert!(fs1
+ .fetch_file(&en_us, &FTL_RESOURCE_PRESENT.into())
+ .await
+ .is_some());
+ assert!(fs1
+ .fetch_file_sync(&en_us, &FTL_RESOURCE_PRESENT.into(), false)
+ .is_some());
+}
+
+#[test]
+fn test_fetch_has_value_required_sync() {
+ let fetcher = TestFileFetcher::new();
+ let en_us: LanguageIdentifier = "en-US".parse().unwrap();
+ let path = FTL_RESOURCE_PRESENT.into();
+ let path_missing = FTL_RESOURCE_MISSING.into();
+
+ let fs1 =
+ fetcher.get_test_file_source("toolkit", None, vec![en_us.clone()], "toolkit/{locale}/");
+
+ assert_eq!(fs1.has_file(&en_us, &path), None);
+ assert!(fs1.fetch_file_sync(&en_us, &path, false).is_some());
+ assert_eq!(fs1.has_file(&en_us, &path), Some(true));
+
+ assert_eq!(fs1.has_file(&en_us, &path_missing), None);
+ assert!(fs1.fetch_file_sync(&en_us, &path_missing, false).is_none());
+ assert_eq!(fs1.has_file(&en_us, &path_missing), Some(false));
+}
+
+#[test]
+fn test_fetch_has_value_optional_sync() {
+ let fetcher = TestFileFetcher::new();
+ let en_us: LanguageIdentifier = "en-US".parse().unwrap();
+ let path = FTL_RESOURCE_PRESENT.to_resource_id(ResourceType::Optional);
+ let path_missing = FTL_RESOURCE_MISSING.to_resource_id(ResourceType::Optional);
+
+ let fs1 =
+ fetcher.get_test_file_source("toolkit", None, vec![en_us.clone()], "toolkit/{locale}/");
+
+ assert_eq!(fs1.has_file(&en_us, &path), None);
+ assert!(fs1.fetch_file_sync(&en_us, &path, false).is_some());
+ assert_eq!(fs1.has_file(&en_us, &path), Some(true));
+
+ assert_eq!(fs1.has_file(&en_us, &path_missing), None);
+ assert!(fs1.fetch_file_sync(&en_us, &path_missing, false).is_none());
+ assert_eq!(fs1.has_file(&en_us, &path_missing), Some(false));
+}
+
+#[tokio::test]
+async fn test_fetch_has_value_required_async() {
+ let fetcher = TestFileFetcher::new();
+ let en_us: LanguageIdentifier = "en-US".parse().unwrap();
+ let path = FTL_RESOURCE_PRESENT.into();
+ let path_missing = FTL_RESOURCE_MISSING.into();
+
+ let fs1 =
+ fetcher.get_test_file_source("toolkit", None, vec![en_us.clone()], "toolkit/{locale}/");
+
+ assert_eq!(fs1.has_file(&en_us, &path), None);
+ assert!(fs1.fetch_file(&en_us, &path).await.is_some());
+ println!("Completed");
+ assert_eq!(fs1.has_file(&en_us, &path), Some(true));
+
+ assert_eq!(fs1.has_file(&en_us, &path_missing), None);
+
+ assert!(fs1.fetch_file(&en_us, &path_missing).await.is_none());
+ assert!(fs1
+ .fetch_file(&en_us, &path_missing)
+ .await
+ .is_required_and_missing());
+
+ assert_eq!(fs1.has_file(&en_us, &path_missing), Some(false));
+
+ assert!(fs1.fetch_file_sync(&en_us, &path_missing, false).is_none());
+ assert!(fs1
+ .fetch_file_sync(&en_us, &path_missing, false)
+ .is_required_and_missing());
+}
+
+#[tokio::test]
+async fn test_fetch_has_value_optional_async() {
+ let fetcher = TestFileFetcher::new();
+ let en_us: LanguageIdentifier = "en-US".parse().unwrap();
+ let path = FTL_RESOURCE_PRESENT.to_resource_id(ResourceType::Optional);
+ let path_missing = FTL_RESOURCE_MISSING.to_resource_id(ResourceType::Optional);
+
+ let fs1 =
+ fetcher.get_test_file_source("toolkit", None, vec![en_us.clone()], "toolkit/{locale}/");
+
+ assert_eq!(fs1.has_file(&en_us, &path), None);
+ assert!(fs1.fetch_file(&en_us, &path).await.is_some());
+ println!("Completed");
+ assert_eq!(fs1.has_file(&en_us, &path), Some(true));
+
+ assert_eq!(fs1.has_file(&en_us, &path_missing), None);
+ assert!(fs1.fetch_file(&en_us, &path_missing).await.is_none());
+ assert!(!fs1
+ .fetch_file(&en_us, &path_missing)
+ .await
+ .is_required_and_missing());
+
+ assert_eq!(fs1.has_file(&en_us, &path_missing), Some(false));
+
+ assert!(fs1.fetch_file_sync(&en_us, &path_missing, false).is_none());
+ assert!(!fs1
+ .fetch_file_sync(&en_us, &path_missing, false)
+ .is_required_and_missing());
+}
+
+#[tokio::test]
+async fn test_fetch_async_consecutive() {
+ let fetcher = TestFileFetcher::new();
+ let en_us: LanguageIdentifier = "en-US".parse().unwrap();
+
+ let fs1 =
+ fetcher.get_test_file_source("toolkit", None, vec![en_us.clone()], "toolkit/{locale}/");
+
+ let results = join_all(vec![
+ fs1.fetch_file(&en_us, &FTL_RESOURCE_PRESENT.into()),
+ fs1.fetch_file(&en_us, &FTL_RESOURCE_PRESENT.into()),
+ ])
+ .await;
+ assert!(results[0].is_some());
+ assert!(results[1].is_some());
+
+ assert!(fs1
+ .fetch_file(&en_us, &FTL_RESOURCE_PRESENT.into())
+ .await
+ .is_some());
+}
+
+#[test]
+fn test_indexed() {
+ let fetcher = TestFileFetcher::new();
+ let en_us: LanguageIdentifier = "en-US".parse().unwrap();
+ let path = FTL_RESOURCE_PRESENT;
+ let path_missing = FTL_RESOURCE_MISSING;
+
+ let fs1 = fetcher.get_test_file_source_with_index(
+ "toolkit",
+ None,
+ vec![en_us.clone()],
+ "toolkit/{locale}/",
+ vec!["toolkit/en-US/toolkit/global/textActions.ftl"],
+ );
+
+ assert_eq!(fs1.has_file(&en_us, &path.into()), Some(true));
+ assert!(fs1.fetch_file_sync(&en_us, &path.into(), false).is_some());
+ assert_eq!(fs1.has_file(&en_us, &path.into()), Some(true));
+
+ assert_eq!(fs1.has_file(&en_us, &path_missing.into()), Some(false));
+ assert!(fs1
+ .fetch_file_sync(&en_us, &path_missing.into(), false)
+ .is_none());
+ assert_eq!(fs1.has_file(&en_us, &path_missing.into()), Some(false));
+}