summaryrefslogtreecommitdiffstats
path: root/src/utils/process.rs
diff options
context:
space:
mode:
authorThomas Otto <th1000s@posteo.net>2021-11-27 17:04:47 +0100
committerDan Davison <dandavison7@gmail.com>2021-11-29 08:39:56 -0500
commitb34a0311222ccd9b864e733606a0088a501089fc (patch)
tree32f5623929364e74aef43f6694a496cf81981d08 /src/utils/process.rs
parent14272816193484bdf1c1228c2c23fcc16d5c9d69 (diff)
Clean up FakeParentArgs
Can fake once, for_scope, or with a given list.
Diffstat (limited to 'src/utils/process.rs')
-rw-r--r--src/utils/process.rs276
1 files changed, 196 insertions, 80 deletions
diff --git a/src/utils/process.rs b/src/utils/process.rs
index 124defa7..ef59d318 100644
--- a/src/utils/process.rs
+++ b/src/utils/process.rs
@@ -46,31 +46,27 @@ pub enum ProcessArgs<T> {
}
pub fn git_blame_filename_extension() -> Option<String> {
- calling_process_cmdline(ProcInfo::new(), blame::guess_git_blame_filename_extension)
+ calling_process_cmdline(ProcInfo::new(), guess_git_blame_filename_extension)
}
-mod blame {
- use super::*;
-
- pub fn guess_git_blame_filename_extension(args: &[String]) -> ProcessArgs<String> {
- let all_args = args.iter().map(|s| s.as_str());
+pub fn guess_git_blame_filename_extension(args: &[String]) -> ProcessArgs<String> {
+ let all_args = args.iter().map(|s| s.as_str());
- // See git(1) and git-blame(1). Some arguments separate their parameter with space or '=', e.g.
- // --date 2015 or --date=2015.
- let git_blame_options_with_parameter =
- "-C -c -L --since --ignore-rev --ignore-revs-file --contents --reverse --date";
+ // See git(1) and git-blame(1). Some arguments separate their parameter with space or '=', e.g.
+ // --date 2015 or --date=2015.
+ let git_blame_options_with_parameter =
+ "-C -c -L --since --ignore-rev --ignore-revs-file --contents --reverse --date";
- let selected_args =
- skip_uninteresting_args(all_args, git_blame_options_with_parameter.split(' '));
+ let selected_args =
+ skip_uninteresting_args(all_args, git_blame_options_with_parameter.split(' '));
- match selected_args.as_slice() {
- [_git, "blame", .., last_arg] => match last_arg.split('.').last() {
- Some(arg) => ProcessArgs::Args(arg.to_string()),
- None => ProcessArgs::ArgError,
- },
- [_git, "blame"] => ProcessArgs::ArgError,
- _ => ProcessArgs::OtherProcess,
- }
+ match selected_args.as_slice() {
+ [_git, "blame", .., last_arg] => match last_arg.split('.').last() {
+ Some(arg) => ProcessArgs::Args(arg.to_string()),
+ None => ProcessArgs::ArgError,
+ },
+ [_git, "blame"] => ProcessArgs::ArgError,
+ _ => ProcessArgs::OtherProcess,
}
}
@@ -339,13 +335,14 @@ where
{
#[cfg(test)]
{
- if let Some(args) = tests::cfg::WithArgs::get() {
+ if let Some(args) = tests::FakeParentArgs::get() {
match extract_args(&args) {
ProcessArgs::Args(result) => return Some(result),
_ => return None,
}
}
}
+
let my_pid = info.my_pid();
// 1) Try the parent process. If delta is set as the pager in git, then git is the parent process.
@@ -409,6 +406,10 @@ where
P: ProcessInterface,
F: FnMut(Pid, usize),
{
+ // Probably bad input, not a tree:
+ if distance > 2000 {
+ return;
+ }
if let Some(proc) = info.process(pid) {
if let Some(pid) = proc.parent() {
f(pid, distance);
@@ -421,58 +422,105 @@ where
#[cfg(test)]
pub mod tests {
- use super::blame::*;
use super::*;
use itertools::Itertools;
-
- pub mod cfg {
- use std::cell::RefCell;
-
- #[derive(Debug, PartialEq)]
- enum TlsState<T> {
- Some(T),
- None,
- Invalid,
+ use std::cell::RefCell;
+ use std::rc::Rc;
+
+ thread_local! {
+ static FAKE_ARGS: RefCell<TlsState<Vec<String>>> = RefCell::new(TlsState::None);
+ }
+
+ #[derive(Debug, PartialEq)]
+ enum TlsState<T> {
+ Once(T),
+ Scope(T),
+ With(usize, Rc<Vec<T>>),
+ None,
+ Invalid,
+ }
+
+ // When calling `FakeParentArgs::get()`, it can return `Some(values)` which were set earlier
+ // during in the #[test]. Otherwise returns None.
+ // This value can be valid once: `FakeParentArgs::once(val)`, for the entire scope:
+ // `FakeParentArgs::for_scope(val)`, or can be different values everytime `get()` is called:
+ // `FakeParentArgs::with([val1, val2, val3])`.
+ // It is an error if `once` or `with` values remain unused, or are overused.
+ // Note: The values are stored per-thread, so the expectation is that no thread boundaries are
+ // crossed.
+ pub struct FakeParentArgs {}
+ impl FakeParentArgs {
+ pub fn once(args: &str) -> Self {
+ Self::new(args, |v| TlsState::Once(v), "once")
}
-
- thread_local! {
- static FAKE_ARGS: RefCell<TlsState<Vec<String>>> = RefCell::new(TlsState::None);
+ pub fn for_scope(args: &str) -> Self {
+ Self::new(args, |v| TlsState::Scope(v), "for_scope")
}
-
- pub struct WithArgs {}
- impl WithArgs {
- pub fn new(args: &str) -> Self {
- let string_vec = args.split(' ').map(str::to_owned).collect();
- assert!(
- FAKE_ARGS.with(|a| a.replace(TlsState::Some(string_vec))) != TlsState::Invalid,
- "test logic error (in new): wrong WithArgs scope?"
- );
- WithArgs {}
- }
- pub fn get() -> Option<Vec<String>> {
- FAKE_ARGS.with(|a| {
- let old_value = a.replace_with(|old_value| match old_value {
- TlsState::Some(_) => TlsState::Invalid,
- TlsState::None => TlsState::None,
- TlsState::Invalid => TlsState::Invalid,
- });
-
- match old_value {
- TlsState::Some(args) => Some(args),
- TlsState::None => None,
- TlsState::Invalid => {
- panic!("test logic error (in get): wrong WithArgs scope?")
- }
- }
- })
+ fn new<F>(args: &str, initial: F, from_: &str) -> Self
+ where
+ F: Fn(Vec<String>) -> TlsState<Vec<String>>,
+ {
+ let string_vec = args.split(' ').map(str::to_owned).collect();
+ if FAKE_ARGS.with(|a| a.replace(initial(string_vec))) != TlsState::None {
+ Self::error(from_);
}
+ FakeParentArgs {}
}
- impl Drop for WithArgs {
- fn drop(&mut self) {
- // clears an invalid state
- FAKE_ARGS.with(|a| a.replace(TlsState::None));
+ pub fn with(args: &[&str]) -> Self {
+ let with = TlsState::With(
+ 0,
+ Rc::new(
+ args.iter()
+ .map(|a| a.split(' ').map(str::to_owned).collect())
+ .collect(),
+ ),
+ );
+ if FAKE_ARGS.with(|a| a.replace(with)) != TlsState::None || args.is_empty() {
+ Self::error("with creation");
}
+ FakeParentArgs {}
+ }
+ pub fn get() -> Option<Vec<String>> {
+ FAKE_ARGS.with(|a| {
+ let old_value = a.replace_with(|old_value| match old_value {
+ TlsState::Once(_) => TlsState::Invalid,
+ TlsState::Scope(args) => TlsState::Scope(args.clone()),
+ TlsState::With(n, args) => TlsState::With(*n + 1, Rc::clone(args)),
+ TlsState::None => TlsState::None,
+ TlsState::Invalid => TlsState::Invalid,
+ });
+
+ match old_value {
+ TlsState::Once(args) | TlsState::Scope(args) => Some(args),
+ TlsState::With(n, args) if n < args.len() => Some(args[n].clone()),
+ TlsState::None => None,
+ TlsState::Invalid | TlsState::With(_, _) => Self::error("get"),
+ }
+ })
+ }
+ fn error(where_: &str) -> ! {
+ panic!(
+ "test logic error (in {}): wrong FakeParentArgs scope?",
+ where_
+ );
+ }
+ }
+ impl Drop for FakeParentArgs {
+ fn drop(&mut self) {
+ // Clears an Invalid state and tests if a Once or With value has been used.
+ FAKE_ARGS.with(|a| {
+ let old_value = a.replace(TlsState::None);
+ match old_value {
+ TlsState::With(n, args) => {
+ if n != args.len() {
+ Self::error("drop with")
+ }
+ }
+ TlsState::Once(_) | TlsState::None => Self::error("drop"),
+ TlsState::Scope(_) | TlsState::Invalid => {}
+ }
+ });
}
}
@@ -612,16 +660,28 @@ pub mod tests {
#[test]
fn test_process_testing() {
{
- let _args = cfg::WithArgs::new(&"git blame hello");
+ let _args = FakeParentArgs::once(&"git blame hello");
assert_eq!(
- calling_process_cmdline(ProcInfo::new(), blame::guess_git_blame_filename_extension),
+ calling_process_cmdline(ProcInfo::new(), guess_git_blame_filename_extension),
Some("hello".into())
);
}
{
- let _args = cfg::WithArgs::new(&"git blame world.txt");
+ let _args = FakeParentArgs::once(&"git blame world.txt");
+ assert_eq!(
+ calling_process_cmdline(ProcInfo::new(), guess_git_blame_filename_extension),
+ Some("txt".into())
+ );
+ }
+ {
+ let _args = FakeParentArgs::for_scope(&"git blame hello world.txt");
assert_eq!(
- calling_process_cmdline(ProcInfo::new(), blame::guess_git_blame_filename_extension),
+ calling_process_cmdline(ProcInfo::new(), guess_git_blame_filename_extension),
+ Some("txt".into())
+ );
+
+ assert_eq!(
+ calling_process_cmdline(ProcInfo::new(), guess_git_blame_filename_extension),
Some("txt".into())
);
}
@@ -630,22 +690,78 @@ pub mod tests {
#[test]
#[should_panic]
fn test_process_testing_assert() {
- {
- let _args = cfg::WithArgs::new(&"git blame do.not.panic");
- assert_eq!(
- calling_process_cmdline(ProcInfo::new(), blame::guess_git_blame_filename_extension),
- Some("panic".into())
- );
+ let _args = FakeParentArgs::once(&"git blame do.not.panic");
+ assert_eq!(
+ calling_process_cmdline(ProcInfo::new(), guess_git_blame_filename_extension),
+ Some("panic".into())
+ );
- calling_process_cmdline(ProcInfo::new(), blame::guess_git_blame_filename_extension);
- }
+ calling_process_cmdline(ProcInfo::new(), guess_git_blame_filename_extension);
+ }
+
+ #[test]
+ #[should_panic]
+ fn test_process_testing_assert_never_used() {
+ let _args = FakeParentArgs::once(&"never used");
+
+ // causes a panic while panicing, so can't test:
+ // let _args = FakeParentArgs::for_scope(&"never used");
+ // let _args = FakeParentArgs::once(&"never used");
+ }
+
+ #[test]
+ fn test_process_testing_scope_can_remain_unused() {
+ let _args = FakeParentArgs::for_scope(&"never used");
+ }
+
+ #[test]
+ fn test_process_testing_n_times_panic() {
+ let _args = FakeParentArgs::with(&["git blame once", "git blame twice"]);
+ assert_eq!(
+ calling_process_cmdline(ProcInfo::new(), guess_git_blame_filename_extension),
+ Some("once".into())
+ );
+
+ assert_eq!(
+ calling_process_cmdline(ProcInfo::new(), guess_git_blame_filename_extension),
+ Some("twice".into())
+ );
+ }
+
+ #[test]
+ #[should_panic]
+ fn test_process_testing_n_times_unused() {
+ let _args = FakeParentArgs::with(&["git blame once", "git blame twice"]);
+ }
+
+ #[test]
+ #[should_panic]
+ fn test_process_testing_n_times_underused() {
+ let _args = FakeParentArgs::with(&["git blame once", "git blame twice"]);
+ assert_eq!(
+ calling_process_cmdline(ProcInfo::new(), guess_git_blame_filename_extension),
+ Some("once".into())
+ );
+ }
+
+ #[test]
+ #[should_panic]
+ #[ignore]
+ fn test_process_testing_n_times_overused() {
+ let _args = FakeParentArgs::with(&["git blame once"]);
+ assert_eq!(
+ calling_process_cmdline(ProcInfo::new(), guess_git_blame_filename_extension),
+ Some("once".into())
+ );
+ // ignored: dropping causes a panic while panicing, so can't test
+ calling_process_cmdline(ProcInfo::new(), guess_git_blame_filename_extension);
}
#[test]
fn test_process_blame_info_with_parent() {
let no_processes = MockProcInfo::with(&[]);
assert_eq!(
- calling_process_cmdline(no_processes, blame::guess_git_blame_filename_extension),
+ calling_process_cmdline(no_processes, guess_git_blame_filename_extension),
None
);
@@ -655,7 +771,7 @@ pub mod tests {
(4, 100, "delta", Some(3)),
]);
assert_eq!(
- calling_process_cmdline(parent, blame::guess_git_blame_filename_extension),
+ calling_process_cmdline(parent, guess_git_blame_filename_extension),
Some("txt".into())
);
@@ -666,7 +782,7 @@ pub mod tests {
(5, 100, "delta", Some(4)),
]);
assert_eq!(
- calling_process_cmdline(grandparent, blame::guess_git_blame_filename_extension),
+ calling_process_cmdline(grandparent, guess_git_blame_filename_extension),
Some("rs".into())
);
}