Repository: uutils/procps
Pull request #222 (2024-09-16 09:53:25)

free: use no empty line as delimiter with `--line`
When using something like `free --line -s1` or `free --line -c2`, the original `free` doesn't output an empty line between the entries whereas our implementation does. This PR removes this empty line from our output. It also does some refactorings to get rid of the variables `count_flag` and `seconds_flag` which are misleading as they don't represent boolean values.

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/uutils/procps/pull/222?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=uutils) Report
Attention: Patch coverage is `83.33333%` with `7 lines` in your changes missing coverage. Please review.
> Project coverage is 66.55%. Comparing base [(`a8b52ad`)](https://app.codecov.io/gh/uutils/procps/commit/a8b52add08bc0920778c8846af6a345b8cbbe589?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=uutils) to head [(`f2abfaa`)](https://app.codecov.io/gh/uutils/procps/commit/f2abfaafb81ce04c6ef0ecce4ed93c620bda9726?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=uutils).
> Report is 5 commits behind head on main.

| [Files with missing lines](https://app.codecov.io/gh/uutils/procps/pull/222?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=uutils) | Patch % | Lines |
|---|---|---|
| [src/uu/free/src/free.rs](https://app.codecov.io/gh/uutils/procps/pull/222?src=pr&el=tree&filepath=src%2Fuu%2Ffree%2Fsrc%2Ffree.rs&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=uutils#diff-c3JjL3V1L2ZyZWUvc3JjL2ZyZWUucnM=) | 78.78% | [7 Missing :warning: ](https://app.codecov.io/gh/uutils/procps/pull/222?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=uutils) |

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main     #222      +/-   ##
==========================================
+ Coverage   65.16%   66.55%   +1.39%     
==========================================
  Files          42       41       -1     
  Lines        4415     4288     -127     
  Branches      702      665      -37     
==========================================
- Hits         2877     2854      -23     
+ Misses       1535     1433     -102     
+ Partials        3        1       -2     
```

| [Flag](https://app.codecov.io/gh/uutils/procps/pull/222/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=uutils) | Coverage Δ | |
|---|---|---|
| [macos_latest](https://app.codecov.io/gh/uutils/procps/pull/222/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=uutils) | `?` | |
| [ubuntu_latest](https://app.codecov.io/gh/uutils/procps/pull/222/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=uutils) | `66.55% <83.33%> (+<0.01%)` | :arrow_up: |
| [windows_latest](https://app.codecov.io/gh/uutils/procps/pull/222/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=uutils) | `?` | |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=uutils#carryforward-flags-in-the-pull-request-comment) to find out more.

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/uutils/procps/pull/222?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=uutils).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=uutils).


Patch:
diff --git a/src/uu/free/src/free.rs b/src/uu/free/src/free.rs
index 74b16a6..490e5b9 100644
--- a/src/uu/free/src/free.rs
+++ b/src/uu/free/src/free.rs
@@ -150,55 +150,60 @@ where
 pub fn uumain(args: impl uucore::Args) -> UResult<()> {
     let matches = uu_app().try_get_matches_from(args)?;
 
-    let count_flag = matches.get_one("count");
-    let mut count: u64 = match count_flag {
-        Some(0) => {
-            return Err(USimpleError::new(
-                1,
-                "count argument must be greater than 0",
-            ))
-        }
-        Some(c) => *c,
-        None => 1,
-    };
-    let seconds_flag = matches.get_one("seconds");
-    let seconds: f64 = match seconds_flag {
-        Some(0.0) => {
-            return Err(USimpleError::new(
-                1,
-                "seconds argument must be greater than 0",
-            ))
-        }
-        Some(s) => *s,
-        None => 1.0,
-    };
+    let count: Option<u64> = matches.get_one("count").copied();
+    let seconds: Option<f64> = matches.get_one("seconds").copied();
+
+    if count == Some(0) {
+        return Err(USimpleError::new(
+            1,
+            "count argument must be greater than 0",
+        ));
+    }
 
-    let dur = Duration::from_nanos(seconds.mul(1_000_000_000.0).round() as u64);
+    if seconds == Some(0.0) {
+        return Err(USimpleError::new(
+            1,
+            "seconds argument must be greater than 0",
+        ));
+    }
 
-    let infinite: bool = count_flag.is_none() && seconds_flag.is_some();
+    let (count, seconds) = match (count, seconds) {
+        (None, None) => (Some(1), 1.0),
+        (None, Some(s)) => (None, s),
+        (Some(c), None) => (Some(c), 1.0),
+        (Some(c), Some(s)) => (Some(c), s),
+    };
 
-    let construct_str = parse_output_format(matches);
+    let duration = Duration::from_nanos(seconds.mul(1_000_000_000.0).round() as u64);
+    let construct_str = parse_output_format(&matches);
 
-    while count > 0 || infinite {
-        // prevent underflow
-        if !infinite {
-            count -= 1;
+    let output_meminfo = || match parse_meminfo() {
+        Ok(mem_info) => {
+            print!("{}", construct_str(&mem_info));
         }
-
-        match parse_meminfo() {
-            Ok(mem_info) => {
-                print!("{}", construct_str(&mem_info));
-            }
-            Err(e) => {
-                eprintln!("free: failed to read memory info: {}", e);
-                process::exit(1);
-            }
+        Err(e) => {
+            eprintln!("free: failed to read memory info: {}", e);
+            process::exit(1);
         }
+    };
 
-        if count > 0 || infinite {
-            // the original free prints a newline everytime before waiting for the next round
+    let do_sleep = || {
+        if !matches.get_flag("line") {
             println!();
-            sleep(dur);
+        }
+        sleep(duration);
+    };
+
+    if let Some(c) = count {
+        for _ in 0..c - 1 {
+            output_meminfo();
+            do_sleep();
+        }
+        output_meminfo();
+    } else {
+        loop {
+            output_meminfo();
+            do_sleep();
         }
     }
 
@@ -273,7 +278,7 @@ fn parse_meminfo_value(value: &str) -> Result<u64, std::io::Error> {
         })
 }
 
-fn parse_output_format(matches: ArgMatches) -> impl Fn(&MemInfo) -> String {
+fn parse_output_format(matches: &ArgMatches) -> impl Fn(&MemInfo) -> String {
     let wide = matches.get_flag("wide");
     let human = matches.get_flag("human");
     let si = matches.get_flag("si");
@@ -282,7 +287,7 @@ fn parse_output_format(matches: ArgMatches) -> impl Fn(&MemInfo) -> String {
     let committed = matches.get_flag("committed");
     let one_line = matches.get_flag("line");
 
-    let convert = detect_unit(&matches);
+    let convert = detect_unit(matches);
 
     // function that converts the number to the correct string
     let n2s = move |x| {


Test patch:
diff --git a/tests/by-util/test_free.rs b/tests/by-util/test_free.rs
index 79ba1a6..5263835 100644
--- a/tests/by-util/test_free.rs
+++ b/tests/by-util/test_free.rs
@@ -101,6 +101,23 @@ fn test_count() {
     }
 }
 
+#[test]
+fn test_count_line() {
+    let re = Regex::new(r"^SwapUse +\d+ CachUse +\d+ {2}MemUse +\d+ MemFree +\d+$").unwrap();
+
+    let output = new_ucmd!()
+        .args(&["--count", "2", "--line", "-s", "0.00001"])
+        .succeeds()
+        .stdout_move_str();
+
+    let lines: Vec<&str> = output.lines().collect();
+
+    assert_eq!(2, lines.len());
+
+    assert!(re.is_match(lines[0]));
+    assert!(re.is_match(lines[1]));
+}
+
 #[test]
 fn test_count_zero() {
     new_ucmd!()