From b47a4fe51470a36116b5c941c6e07ac5730585ea Mon Sep 17 00:00:00 2001 From: David Knaack Date: Tue, 15 Nov 2022 11:14:52 +0100 Subject: [PATCH] fix(container): avoid detecting WSL as a systemd-container (#4593) --- src/modules/container.rs | 100 +++++++++++++++++++++++++++++++------ src/modules/git_metrics.rs | 20 ++------ src/modules/git_state.rs | 20 ++------ src/utils.rs | 34 +++++++++++++ 4 files changed, 129 insertions(+), 45 deletions(-) diff --git a/src/modules/container.rs b/src/modules/container.rs index 50f76956..7c3637cc 100644 --- a/src/modules/container.rs +++ b/src/modules/container.rs @@ -10,7 +10,7 @@ pub fn module<'a>(context: &'a Context) -> Option> { use super::ModuleConfig; use crate::configs::container::ContainerConfig; use crate::formatter::StringFormatter; - use crate::utils::read_file; + use crate::utils::{self, read_file}; pub fn container_name(context: &Context) -> Option { use crate::utils::context_path; @@ -26,7 +26,14 @@ pub fn module<'a>(context: &'a Context) -> Option> { return Some("OCI".into()); } - if context_path(context, "/run/systemd/container").exists() { + // WSL with systemd will set the contents of this file to "wsl" + // Avoid showing the container module in that case + let systemd_path = context_path(context, "/run/systemd/container"); + if utils::read_file(systemd_path) + .ok() + .filter(|s| s.trim() != "wsl") + .is_some() + { // systemd return Some("Systemd".into()); } @@ -101,8 +108,9 @@ pub fn module<'a>(context: &'a Context) -> Option> { #[cfg(test)] mod tests { use crate::test::ModuleRenderer; + use crate::utils; use nu_ansi_term::Color; - use std::path::PathBuf; + use std::fs; #[test] fn test_none_if_disabled() { @@ -120,8 +128,6 @@ mod tests { } fn containerenv(name: Option<&str>) -> std::io::Result<(Option, Option)> { - use std::io::Write; - let renderer = ModuleRenderer::new("container") // For a custom config .config(toml::toml! { @@ -131,18 +137,15 @@ mod tests { let root_path = renderer.root_path(); - let mut containerenv = PathBuf::from(root_path); + let containerenv = root_path.join("run/.containerenv"); - containerenv.push("run"); - std::fs::DirBuilder::new() - .recursive(true) - .create(&containerenv)?; + fs::create_dir_all(containerenv.parent().unwrap())?; - containerenv.push(".containerenv"); - let mut file = std::fs::File::create(&containerenv)?; - if let Some(name) = name { - file.write_all(format!("image=\"{name}\"\n").as_bytes())?; - } + let contents = match name { + Some(name) => format!("image=\"{name}\"\n"), + None => String::new(), + }; + utils::write_file(&containerenv, contents)?; // The output of the module let actual = renderer @@ -182,6 +185,73 @@ mod tests { Ok(()) } + #[test] + #[cfg(target_os = "linux")] + fn test_containerenv_systemd() -> std::io::Result<()> { + let renderer = ModuleRenderer::new("container") + // For a custom config + .config(toml::toml! { + [container] + disabled = false + }); + + let root_path = renderer.root_path(); + + let systemd_path = root_path.join("run/systemd/container"); + + fs::create_dir_all(systemd_path.parent().unwrap())?; + utils::write_file(&systemd_path, "systemd-nspawn\n")?; + + // The output of the module + let actual = renderer + // Run the module and collect the output + .collect(); + + // The value that should be rendered by the module. + let expected = Some(format!( + "{} ", + Color::Red + .bold() + .dimmed() + .paint(format!("⬢ [{}]", "Systemd")) + )); + + // Assert that the actual and expected values are the same + assert_eq!(actual, expected); + + Ok(()) + } + + #[test] + #[cfg(target_os = "linux")] + fn test_containerenv_wsl() -> std::io::Result<()> { + let renderer = ModuleRenderer::new("container") + // For a custom config + .config(toml::toml! { + [container] + disabled = false + }); + + let root_path = renderer.root_path(); + + let systemd_path = root_path.join("run/systemd/container"); + + fs::create_dir_all(systemd_path.parent().unwrap())?; + utils::write_file(&systemd_path, "wsl\n")?; + + // The output of the module + let actual = renderer + // Run the module and collect the output + .collect(); + + // The value that should be rendered by the module. + let expected = None; + + // Assert that the actual and expected values are the same + assert_eq!(actual, expected); + + Ok(()) + } #[test] #[cfg(not(target_os = "linux"))] diff --git a/src/modules/git_metrics.rs b/src/modules/git_metrics.rs index dc32660f..b3d96dcb 100644 --- a/src/modules/git_metrics.rs +++ b/src/modules/git_metrics.rs @@ -109,11 +109,11 @@ impl<'a> GitDiff<'a> { #[cfg(test)] mod tests { - use crate::utils::create_command; + use crate::utils::{create_command, write_file}; use std::ffi::OsStr; use std::fs::OpenOptions; use std::io::{self, Error, ErrorKind, Write}; - use std::path::{Path, PathBuf}; + use std::path::Path; use std::process::Stdio; use nu_ansi_term::Color; @@ -157,7 +157,7 @@ mod tests { let path = repo_dir.path(); let file_path = path.join("the_file"); - write_file(file_path, "First Line\nSecond Line")?; + write_file(file_path, "First Line\nSecond Line\n")?; let actual = render_metrics(path); @@ -173,7 +173,7 @@ mod tests { let path = repo_dir.path(); let file_path = path.join("the_file"); - write_file(file_path, "\nSecond Line\n\nModified\nAdded")?; + write_file(file_path, "\nSecond Line\n\nModified\nAdded\n")?; let actual = render_metrics(path); @@ -263,16 +263,6 @@ mod tests { } } - fn write_file(file: PathBuf, text: &str) -> io::Result<()> { - let mut file = OpenOptions::new() - .write(true) - .create(true) - .truncate(true) - .open(file)?; - writeln!(file, "{text}")?; - file.sync_all() - } - fn create_repo_with_commit() -> io::Result { let repo_dir = tempfile::tempdir()?; let path = repo_dir.path(); @@ -312,7 +302,7 @@ mod tests { )?; // Write a file on master and commit it - write_file(file, "First Line\nSecond Line\nThird Line")?; + write_file(file, "First Line\nSecond Line\nThird Line\n")?; run_git_cmd(["add", "the_file"], Some(path), true)?; run_git_cmd( ["commit", "--message", "Commit A", "--no-gpg-sign"], diff --git a/src/modules/git_state.rs b/src/modules/git_state.rs index 04fd4cfc..138f8ae1 100644 --- a/src/modules/git_state.rs +++ b/src/modules/git_state.rs @@ -159,13 +159,12 @@ struct StateDescription<'a> { mod tests { use nu_ansi_term::Color; use std::ffi::OsStr; - use std::fs::OpenOptions; - use std::io::{self, Error, ErrorKind, Write}; + use std::io::{self, Error, ErrorKind}; use std::path::Path; use std::process::Stdio; use crate::test::ModuleRenderer; - use crate::utils::create_command; + use crate::utils::{create_command, write_file}; #[test] fn show_nothing_on_empty_dir() -> io::Result<()> { @@ -289,15 +288,6 @@ mod tests { let path = repo_dir.path(); let conflicted_file = repo_dir.path().join("the_file"); - let write_file = |text: &str| { - let mut file = OpenOptions::new() - .write(true) - .create(true) - .truncate(true) - .open(&conflicted_file)?; - write!(file, "{text}") - }; - // Initialize a new git repo run_git_cmd( [ @@ -332,7 +322,7 @@ mod tests { )?; // Write a file on master and commit it - write_file("Version A")?; + write_file(&conflicted_file, "Version A")?; run_git_cmd(["add", "the_file"], Some(path), true)?; run_git_cmd( ["commit", "--message", "Commit A", "--no-gpg-sign"], @@ -342,7 +332,7 @@ mod tests { // Switch to another branch, and commit a change to the file run_git_cmd(["checkout", "-b", "other-branch"], Some(path), true)?; - write_file("Version B")?; + write_file(&conflicted_file, "Version B")?; run_git_cmd( ["commit", "--all", "--message", "Commit B", "--no-gpg-sign"], Some(path), @@ -351,7 +341,7 @@ mod tests { // Switch back to master, and commit a third change to the file run_git_cmd(["checkout", "master"], Some(path), true)?; - write_file("Version C")?; + write_file(conflicted_file, "Version C")?; run_git_cmd( ["commit", "--all", "--message", "Commit C", "--no-gpg-sign"], Some(path), diff --git a/src/utils.rs b/src/utils.rs index 4427cddf..6994290e 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -48,6 +48,40 @@ pub fn read_file + Debug>(file_name: P) -> Result { result } +/// Write a string to a file +#[cfg(test)] +pub fn write_file, S: AsRef>(file_name: P, text: S) -> Result<()> { + use std::io::Write; + + let file_name = file_name.as_ref(); + let text = text.as_ref(); + + log::trace!("Trying to write {text:?} to {file_name:?}"); + let mut file = match std::fs::OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open(file_name) + { + Ok(file) => file, + Err(err) => { + log::warn!("Error creating file: {:?}", err); + return Err(err); + } + }; + + match file.write_all(text.as_bytes()) { + Ok(_) => { + log::trace!("File {file_name:?} written successfully"); + } + Err(err) => { + log::warn!("Error writing to file: {err:?}"); + return Err(err); + } + } + file.sync_all() +} + /// Reads command output from stderr or stdout depending on to which stream program streamed it's output pub fn get_command_string_output(command: CommandOutput) -> String { if command.stdout.is_empty() {