about summary refs log tree commit diff
path: root/pkgs/test
diff options
context:
space:
mode:
authorSilvan Mosberger <silvan.mosberger@tweag.io>2023-12-14 02:47:44 +0100
committerSilvan Mosberger <silvan.mosberger@tweag.io>2023-12-14 03:40:49 +0100
commita6ba4cae311698ea907ee239785f75d76bd01e4b (patch)
treeea580fc2e2a7a86fac5cf47473cb74d0711e60e3 /pkgs/test
parente98d22851b67a6125683f80735e4bc1042252aef (diff)
tests.nixpkgs-check-by-name: Intermediate refactor
This prepares the code base for the removal of the `--version` flag, to
be replaced with a flag that can specify a base version to compare the
main Nixpkgs against, in order to have gradual transitions to stricter
checks.

This refactoring does:
- Introduce the `version` module that can house the logic to increase
  strictness, with a `version::Nixpkgs` struct that contains the
  strictness conformity of a single Nixpkgs version
- Make the check return `version::Nixpkgs`
- Handle the behavior of the still-existing `--version` flag with `version::Nixpkgs`
- Introduce an intermediate `process` function to handle the top-level
  logic, especially useful in the next commit
Diffstat (limited to 'pkgs/test')
-rw-r--r--pkgs/test/nixpkgs-check-by-name/src/eval.rs44
-rw-r--r--pkgs/test/nixpkgs-check-by-name/src/main.rs64
-rw-r--r--pkgs/test/nixpkgs-check-by-name/src/version.rs71
3 files changed, 139 insertions, 40 deletions
diff --git a/pkgs/test/nixpkgs-check-by-name/src/eval.rs b/pkgs/test/nixpkgs-check-by-name/src/eval.rs
index 08dc243359d52..face1117f6435 100644
--- a/pkgs/test/nixpkgs-check-by-name/src/eval.rs
+++ b/pkgs/test/nixpkgs-check-by-name/src/eval.rs
@@ -1,7 +1,7 @@
 use crate::nixpkgs_problem::NixpkgsProblem;
 use crate::structure;
 use crate::validation::{self, Validation::Success};
-use crate::Version;
+use crate::version;
 use std::path::Path;
 
 use anyhow::Context;
@@ -39,11 +39,10 @@ enum AttributeVariant {
 /// of the form `callPackage <package_file> { ... }`.
 /// See the `eval.nix` file for how this is achieved on the Nix side
 pub fn check_values(
-    version: Version,
     nixpkgs_path: &Path,
     package_names: Vec<String>,
     eval_accessible_paths: Vec<&Path>,
-) -> validation::Result<()> {
+) -> validation::Result<version::Nixpkgs> {
     // Write the list of packages we need to check into a temporary JSON file.
     // This can then get read by the Nix evaluation.
     let attrs_file = NamedTempFile::new().context("Failed to create a temporary file")?;
@@ -110,8 +109,8 @@ pub fn check_values(
             String::from_utf8_lossy(&result.stdout)
         ))?;
 
-    Ok(validation::sequence_(package_names.iter().map(
-        |package_name| {
+    Ok(
+        validation::sequence(package_names.iter().map(|package_name| {
             let relative_package_file = structure::relative_file_for_package(package_name);
             let absolute_package_file = nixpkgs_path.join(&relative_package_file);
 
@@ -126,23 +125,25 @@ pub fn check_values(
                     Success(())
                 };
 
-                check_result.and(match &attribute_info.variant {
-                    AttributeVariant::AutoCalled => Success(()),
+                let check_result = check_result.and(match &attribute_info.variant {
+                    AttributeVariant::AutoCalled => Success(version::Attribute {
+                        empty_non_auto_called: version::EmptyNonAutoCalled::Valid,
+                    }),
                     AttributeVariant::CallPackage { path, empty_arg } => {
                         let correct_file = if let Some(call_package_path) = path {
                             absolute_package_file == *call_package_path
                         } else {
                             false
                         };
-                        // Only check for the argument to be non-empty if the version is V1 or
-                        // higher
-                        let non_empty = if version >= Version::V1 {
-                            !empty_arg
-                        } else {
-                            true
-                        };
-                        if correct_file && non_empty {
-                            Success(())
+
+                        if correct_file {
+                            Success(version::Attribute {
+                                empty_non_auto_called: if *empty_arg {
+                                    version::EmptyNonAutoCalled::Invalid
+                                } else {
+                                    version::EmptyNonAutoCalled::Valid
+                                },
+                            })
                         } else {
                             NixpkgsProblem::WrongCallPackage {
                                 relative_package_file: relative_package_file.clone(),
@@ -156,7 +157,9 @@ pub fn check_values(
                         package_name: package_name.clone(),
                     }
                     .into(),
-                })
+                });
+
+                check_result.map(|value| (package_name.clone(), value))
             } else {
                 NixpkgsProblem::UndefinedAttr {
                     relative_package_file: relative_package_file.clone(),
@@ -164,6 +167,9 @@ pub fn check_values(
                 }
                 .into()
             }
-        },
-    )))
+        }))
+        .map(|elems| version::Nixpkgs {
+            attributes: elems.into_iter().collect(),
+        }),
+    )
 }
diff --git a/pkgs/test/nixpkgs-check-by-name/src/main.rs b/pkgs/test/nixpkgs-check-by-name/src/main.rs
index 567da00333e6c..981d1134c85a7 100644
--- a/pkgs/test/nixpkgs-check-by-name/src/main.rs
+++ b/pkgs/test/nixpkgs-check-by-name/src/main.rs
@@ -4,6 +4,7 @@ mod references;
 mod structure;
 mod utils;
 mod validation;
+mod version;
 
 use crate::structure::check_structure;
 use crate::validation::Validation::Failure;
@@ -39,7 +40,7 @@ pub enum Version {
 
 fn main() -> ExitCode {
     let args = Args::parse();
-    match check_nixpkgs(&args.nixpkgs, args.version, vec![], &mut io::stderr()) {
+    match process(&args.nixpkgs, args.version, vec![], &mut io::stderr()) {
         Ok(true) => {
             eprintln!("{}", "Validated successfully".green());
             ExitCode::SUCCESS
@@ -55,7 +56,7 @@ fn main() -> ExitCode {
     }
 }
 
-/// Checks whether the pkgs/by-name structure in Nixpkgs is valid.
+/// Does the actual work. This is the abstraction used both by `main` and the tests.
 ///
 /// # Arguments
 /// - `nixpkgs_path`: The path to the Nixpkgs to check
@@ -68,28 +69,23 @@ fn main() -> ExitCode {
 /// - `Err(e)` if an I/O-related error `e` occurred.
 /// - `Ok(false)` if there are problems, all of which will be written to `error_writer`.
 /// - `Ok(true)` if there are no problems
-pub fn check_nixpkgs<W: io::Write>(
+pub fn process<W: io::Write>(
     nixpkgs_path: &Path,
     version: Version,
     eval_accessible_paths: Vec<&Path>,
     error_writer: &mut W,
 ) -> anyhow::Result<bool> {
-    let nixpkgs_path = nixpkgs_path.canonicalize().context(format!(
-        "Nixpkgs path {} could not be resolved",
-        nixpkgs_path.display()
-    ))?;
-
-    let check_result = if !nixpkgs_path.join(utils::BASE_SUBPATH).exists() {
-        eprintln!(
-            "Given Nixpkgs path does not contain a {} subdirectory, no check necessary.",
-            utils::BASE_SUBPATH
-        );
-        Success(())
-    } else {
-        check_structure(&nixpkgs_path)?.result_map(|package_names|
-            // Only if we could successfully parse the structure, we do the evaluation checks
-            eval::check_values(version, &nixpkgs_path, package_names, eval_accessible_paths))?
-    };
+    let nixpkgs_result = check_nixpkgs(nixpkgs_path, eval_accessible_paths)?;
+    let check_result = nixpkgs_result.result_map(|nixpkgs_version| {
+        let empty_non_auto_called_base = match version {
+            Version::V0 => version::EmptyNonAutoCalled::Invalid,
+            Version::V1 => version::EmptyNonAutoCalled::Valid,
+        };
+        Ok(version::Nixpkgs::compare(
+            &empty_non_auto_called_base,
+            nixpkgs_version,
+        ))
+    })?;
 
     match check_result {
         Failure(errors) => {
@@ -102,9 +98,35 @@ pub fn check_nixpkgs<W: io::Write>(
     }
 }
 
+/// Checks whether the pkgs/by-name structure in Nixpkgs is valid,
+/// and returns to which degree it's valid for checks with increased strictness.
+pub fn check_nixpkgs(
+    nixpkgs_path: &Path,
+    eval_accessible_paths: Vec<&Path>,
+) -> validation::Result<version::Nixpkgs> {
+    Ok({
+        let nixpkgs_path = nixpkgs_path.canonicalize().context(format!(
+            "Nixpkgs path {} could not be resolved",
+            nixpkgs_path.display()
+        ))?;
+
+        if !nixpkgs_path.join(utils::BASE_SUBPATH).exists() {
+            eprintln!(
+                "Given Nixpkgs path does not contain a {} subdirectory, no check necessary.",
+                utils::BASE_SUBPATH
+            );
+            Success(version::Nixpkgs::default())
+        } else {
+            check_structure(&nixpkgs_path)?.result_map(|package_names|
+                // Only if we could successfully parse the structure, we do the evaluation checks
+                eval::check_values(&nixpkgs_path, package_names, eval_accessible_paths))?
+        }
+    })
+}
+
 #[cfg(test)]
 mod tests {
-    use crate::check_nixpkgs;
+    use crate::process;
     use crate::utils;
     use crate::Version;
     use anyhow::Context;
@@ -195,7 +217,7 @@ mod tests {
         // We don't want coloring to mess up the tests
         let writer = temp_env::with_var("NO_COLOR", Some("1"), || -> anyhow::Result<_> {
             let mut writer = vec![];
-            check_nixpkgs(&path, Version::V1, vec![&extra_nix_path], &mut writer)
+            process(&path, Version::V1, vec![&extra_nix_path], &mut writer)
                 .context(format!("Failed test case {name}"))?;
             Ok(writer)
         })?;
diff --git a/pkgs/test/nixpkgs-check-by-name/src/version.rs b/pkgs/test/nixpkgs-check-by-name/src/version.rs
new file mode 100644
index 0000000000000..ab146270241ef
--- /dev/null
+++ b/pkgs/test/nixpkgs-check-by-name/src/version.rs
@@ -0,0 +1,71 @@
+use crate::nixpkgs_problem::NixpkgsProblem;
+use crate::structure;
+use crate::validation::{self, Validation, Validation::Success};
+use std::collections::HashMap;
+
+/// The check version conformity of a Nixpkgs path:
+/// When the strictness of the check increases, this structure should be extended to distinguish
+/// between parts that are still valid, and ones that aren't valid anymore.
+#[derive(Default)]
+pub struct Nixpkgs {
+    /// The package attributes tracked in `pkgs/by-name`
+    pub attributes: HashMap<String, Attribute>,
+}
+
+impl Nixpkgs {
+    /// Compares two Nixpkgs versions against each other, returning validation errors only if the
+    /// `from` version satisfied the stricter checks, while the `to` version doesn't satisfy them
+    /// anymore.
+    pub fn compare(empty_non_auto_called_from: &EmptyNonAutoCalled, to: Self) -> Validation<()> {
+        validation::sequence_(
+            // We only loop over the current attributes,
+            // we don't need to check ones that were removed
+            to.attributes.into_iter().map(|(name, attr_to)| {
+                Attribute::compare(&name, empty_non_auto_called_from, &attr_to)
+            }),
+        )
+    }
+}
+
+/// The check version conformity of an attribute defined by `pkgs/by-name`
+pub struct Attribute {
+    pub empty_non_auto_called: EmptyNonAutoCalled,
+}
+
+impl Attribute {
+    pub fn compare(
+        name: &str,
+        empty_non_auto_called_from: &EmptyNonAutoCalled,
+        to: &Self,
+    ) -> Validation<()> {
+        EmptyNonAutoCalled::compare(name, empty_non_auto_called_from, &to.empty_non_auto_called)
+    }
+}
+
+/// Whether an attribute conforms to the new strictness check that
+/// `callPackage ... {}` is not allowed anymore in `all-package.nix`
+#[derive(PartialEq, PartialOrd)]
+pub enum EmptyNonAutoCalled {
+    /// The attribute is not valid anymore with the new check
+    Invalid,
+    /// The attribute is still valid with the new check
+    Valid,
+}
+
+impl EmptyNonAutoCalled {
+    fn compare(
+        name: &str,
+        empty_non_auto_called_from: &EmptyNonAutoCalled,
+        to: &Self,
+    ) -> Validation<()> {
+        if to >= empty_non_auto_called_from {
+            Success(())
+        } else {
+            NixpkgsProblem::WrongCallPackage {
+                relative_package_file: structure::relative_file_for_package(name),
+                package_name: name.to_owned(),
+            }
+            .into()
+        }
+    }
+}