Skip to content

Commit 8a5fc1b

Browse files
committed
Add support for using is_terminal and environment variables to determine whether to output ANSI colour sequences.
This makes use of the trait [`IsTerminal`](https://doc.rust-lang.org/stable/std/io/trait.IsTerminal.html), stabilised in Rust 1.70. If the Rust version is at least 1.70, then the library uses this trait to detect whether stdout is a terminal and only outputs colours by default if that is the case. When used on prior Rust versions, the library does not detect whether the output is a terminal and defaults to not outputing colours. This also introduces support for two environment variables to override the default behaviour defined above: * `NO_COLOR`, described in <http://no-color.org>. * `FORCE_COLOR`, which is supported by a range of tools such as [Node.js](https://nodejs.org/api/cli.html#force_color1-2-3). The environment variable `GTEST_RUST_NO_COLOR` is hereby no longer supported. This also removes the ANSI colour sequences from nearly all tests. It creates two integration tests which specifically test the colourised output respectively the behaviour of the environment variable `NO_COLOR`. Otherwise, it globally sets `NO_COLOR` to suppress colourised output. This greatly reduces the noise in the existing test assertions and makes sure the tests continue to be compatible with all environments. Since the use of `IsTerminal` is optional and conditional on the Rust version, this does not change the minimum supported Rust version.
1 parent e45d943 commit 8a5fc1b

File tree

12 files changed

+229
-141
lines changed

12 files changed

+229
-141
lines changed

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -284,9 +284,10 @@ displayed, we recommend setting those variables in the personal
284284

285285
### Configuration variable list
286286

287-
| Variable name | Description |
288-
| ------------------- | -------------------------------------------------- |
289-
| GTEST_RUST_NO_COLOR | If set to any value, disables ANSI output from the failure message. This is useful when the failure description is piped to a file or another process. |
287+
| Variable name | Description |
288+
| ------------- | ------------------------------------------------------- |
289+
| NO_COLOR | Disables coloured output. See <https://no-color.org/>. |
290+
| FORCE_COLOR | Forces colours even when the output is piped to a file. |
290291

291292
## Contributing Changes
292293

googletest/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,9 @@ authors = [
3434
googletest_macro = { path = "../googletest_macro", version = "0.9.0" }
3535
anyhow = { version = "1", optional = true }
3636
num-traits = "0.2.15"
37-
regex = "1.6.0"
3837
proptest = { version = "1.2.0", optional = true }
38+
regex = "1.6.0"
39+
rustversion = "1.0.14"
3940

4041
[dev-dependencies]
4142
indoc = "2"

googletest/config.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[env]
2+
NO_COLOR = "1"

googletest/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#![doc = include_str!("../crate_docs.md")]
1616

1717
extern crate googletest_macro;
18+
extern crate rustversion;
1819

1920
#[cfg(test)]
2021
extern crate quickcheck;

googletest/src/matcher_support/summarize_diff.rs

Lines changed: 40 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,17 @@
1414

1515
#![doc(hidden)]
1616

17-
use std::borrow::Cow;
18-
use std::fmt::{Display, Write};
19-
2017
use crate::matcher_support::edit_distance;
21-
22-
/// Environment variable controlling the usage of ansi color in difference
23-
/// summary.
24-
const NO_COLOR_VAR: &str = "GTEST_RUST_NO_COLOR";
18+
#[rustversion::since(1.70)]
19+
use std::io::IsTerminal;
20+
use std::{
21+
borrow::Cow,
22+
fmt::{Display, Write},
23+
sync::{
24+
atomic::{AtomicBool, Ordering},
25+
Once,
26+
},
27+
};
2528

2629
/// Returns a string describing how the expected and actual lines differ.
2730
///
@@ -195,7 +198,7 @@ struct StyledLine<'a> {
195198

196199
impl<'a> Display for StyledLine<'a> {
197200
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
198-
if std::env::var(NO_COLOR_VAR).is_err() {
201+
if stdout_supports_colour() {
199202
write!(
200203
f,
201204
"{}{}{}{}",
@@ -207,36 +210,39 @@ impl<'a> Display for StyledLine<'a> {
207210
}
208211
}
209212

213+
static SUPPORTS_COLOUR_INIT: Once = Once::new();
214+
static SUPPORTS_COLOUR: AtomicBool = AtomicBool::new(false);
215+
216+
#[rustversion::since(1.70)]
217+
fn stdout_supports_colour() -> bool {
218+
SUPPORTS_COLOUR_INIT.call_once(|| {
219+
if !std::env::var("NO_COLOR").unwrap_or("".into()).is_empty() {
220+
SUPPORTS_COLOUR.store(false, Ordering::Relaxed);
221+
} else if !std::env::var("FORCE_COLOR").unwrap_or("".into()).is_empty() {
222+
SUPPORTS_COLOUR.store(true, Ordering::Relaxed);
223+
} else {
224+
SUPPORTS_COLOUR.store(std::io::stdout().is_terminal(), Ordering::Relaxed);
225+
}
226+
});
227+
SUPPORTS_COLOUR.load(Ordering::Relaxed)
228+
}
229+
230+
#[rustversion::not(since(1.70))]
231+
fn stdout_supports_colour() -> bool {
232+
SUPPORTS_COLOUR_INIT.call_once(|| {
233+
SUPPORTS_COLOUR.store(
234+
!std::env::var("FORCE_COLOR").unwrap_or("".into()).is_empty(),
235+
Ordering::Relaxed,
236+
);
237+
});
238+
SUPPORTS_COLOUR.load(Ordering::Relaxed)
239+
}
240+
210241
#[cfg(test)]
211242
mod tests {
212243
use super::*;
213244
use crate::{matcher_support::edit_distance::Mode, prelude::*};
214245
use indoc::indoc;
215-
use serial_test::serial;
216-
217-
#[must_use]
218-
fn remove_var() -> TempVar {
219-
let old_value = std::env::var(NO_COLOR_VAR);
220-
std::env::remove_var(NO_COLOR_VAR);
221-
TempVar(old_value.ok())
222-
}
223-
224-
#[must_use]
225-
fn set_var(var: &str) -> TempVar {
226-
let old_value = std::env::var(NO_COLOR_VAR);
227-
std::env::set_var(NO_COLOR_VAR, var);
228-
TempVar(old_value.ok())
229-
}
230-
struct TempVar(Option<String>);
231-
232-
impl Drop for TempVar {
233-
fn drop(&mut self) {
234-
match &self.0 {
235-
Some(old_var) => std::env::set_var(NO_COLOR_VAR, old_var),
236-
None => std::env::remove_var(NO_COLOR_VAR),
237-
}
238-
}
239-
}
240246

241247
// Make a long text with each element of the iterator on one line.
242248
// `collection` must contains at least one element.
@@ -277,30 +283,8 @@ mod tests {
277283
}
278284

279285
#[test]
280-
#[serial]
281-
fn create_diff_exact_small_difference() -> Result<()> {
282-
let _cleanup = remove_var();
283-
284-
verify_that!(
285-
create_diff(&build_text(1..50), &build_text(1..51), Mode::Exact),
286-
eq(indoc! {
287-
"
288-
289-
Difference(-\x1B[1;31mactual\x1B[0m / +\x1B[1;32mexpected\x1B[0m):
290-
1
291-
2
292-
\x1B[3m<---- 45 common lines omitted ---->\x1B[0m
293-
48
294-
49
295-
+\x1B[1;32m50\x1B[0m"
296-
})
297-
)
298-
}
299-
300-
#[test]
301-
#[serial]
302286
fn create_diff_exact_small_difference_no_color() -> Result<()> {
303-
let _cleanup = set_var("NO_COLOR");
287+
std::env::set_var("NO_COLOR", "1");
304288

305289
verify_that!(
306290
create_diff(&build_text(1..50), &build_text(1..51), Mode::Exact),

googletest/src/matchers/display_matcher.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,10 +108,10 @@ mod tests {
108108
err(displays_as(contains_substring(indoc!(
109109
"
110110
which displays as a string which isn't equal to \"123\\n345\"
111-
Difference(-\x1B[1;31mactual\x1B[0m / +\x1B[1;32mexpected\x1B[0m):
111+
Difference(-actual / +expected):
112112
123
113-
-\x1B[1;31m234\x1B[0m
114-
+\x1B[1;32m345\x1B[0m
113+
-234
114+
+345
115115
"
116116
))))
117117
)

googletest/src/matchers/eq_deref_of_matcher.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -138,12 +138,12 @@ mod tests {
138138
"
139139
Actual: Strukt { int: 123, string: \"something\" },
140140
which isn't equal to Strukt { int: 321, string: \"someone\" }
141-
Difference(-\x1B[1;31mactual\x1B[0m / +\x1B[1;32mexpected\x1B[0m):
141+
Difference(-actual / +expected):
142142
Strukt {
143-
-\x1B[1;31m int: 123,\x1B[0m
144-
+\x1B[1;32m int: 321,\x1B[0m
145-
-\x1B[1;31m string: \"something\",\x1B[0m
146-
+\x1B[1;32m string: \"someone\",\x1B[0m
143+
- int: 123,
144+
+ int: 321,
145+
- string: \"something\",
146+
+ string: \"someone\",
147147
}
148148
"})))
149149
)

googletest/src/matchers/eq_matcher.rs

Lines changed: 34 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -178,12 +178,12 @@ mod tests {
178178
"
179179
Actual: Strukt { int: 123, string: \"something\" },
180180
which isn't equal to Strukt { int: 321, string: \"someone\" }
181-
Difference(-\x1B[1;31mactual\x1B[0m / +\x1B[1;32mexpected\x1B[0m):
181+
Difference(-actual / +expected):
182182
Strukt {
183-
-\x1B[1;31m int: 123,\x1B[0m
184-
+\x1B[1;32m int: 321,\x1B[0m
185-
-\x1B[1;31m string: \"something\",\x1B[0m
186-
+\x1B[1;32m string: \"someone\",\x1B[0m
183+
- int: 123,
184+
+ int: 321,
185+
- string: \"something\",
186+
+ string: \"someone\",
187187
}
188188
"})))
189189
)
@@ -200,12 +200,12 @@ mod tests {
200200
Expected: is equal to [1, 3, 4]
201201
Actual: [1, 2, 3],
202202
which isn't equal to [1, 3, 4]
203-
Difference(-\x1B[1;31mactual\x1B[0m / +\x1B[1;32mexpected\x1B[0m):
203+
Difference(-actual / +expected):
204204
[
205205
1,
206-
-\x1B[1;31m 2,\x1B[0m
206+
- 2,
207207
3,
208-
+\x1B[1;32m 4,\x1B[0m
208+
+ 4,
209209
]
210210
"})))
211211
)
@@ -222,12 +222,12 @@ mod tests {
222222
Expected: is equal to [1, 3, 5]
223223
Actual: [1, 2, 3, 4, 5],
224224
which isn't equal to [1, 3, 5]
225-
Difference(-\x1B[1;31mactual\x1B[0m / +\x1B[1;32mexpected\x1B[0m):
225+
Difference(-actual / +expected):
226226
[
227227
1,
228-
-\x1B[1;31m 2,\x1B[0m
228+
- 2,
229229
3,
230-
-\x1B[1;31m 4,\x1B[0m
230+
- 4,
231231
5,
232232
]
233233
"})))
@@ -241,17 +241,17 @@ mod tests {
241241
result,
242242
err(displays_as(contains_substring(indoc! {
243243
"
244-
Difference(-\x1B[1;31mactual\x1B[0m / +\x1B[1;32mexpected\x1B[0m):
244+
Difference(-actual / +expected):
245245
[
246-
-\x1B[1;31m 1,\x1B[0m
247-
-\x1B[1;31m 2,\x1B[0m
246+
- 1,
247+
- 2,
248248
3,
249249
4,
250-
\x1B[3m<---- 43 common lines omitted ---->\x1B[0m
250+
<---- 43 common lines omitted ---->
251251
48,
252252
49,
253-
+\x1B[1;32m 50,\x1B[0m
254-
+\x1B[1;32m 51,\x1B[0m
253+
+ 50,
254+
+ 51,
255255
]"})))
256256
)
257257
}
@@ -263,17 +263,17 @@ mod tests {
263263
result,
264264
err(displays_as(contains_substring(indoc! {
265265
"
266-
Difference(-\x1B[1;31mactual\x1B[0m / +\x1B[1;32mexpected\x1B[0m):
266+
Difference(-actual / +expected):
267267
[
268-
-\x1B[1;31m 1,\x1B[0m
269-
-\x1B[1;31m 2,\x1B[0m
268+
- 1,
269+
- 2,
270270
3,
271271
4,
272272
5,
273273
6,
274274
7,
275-
+\x1B[1;32m 8,\x1B[0m
276-
+\x1B[1;32m 9,\x1B[0m
275+
+ 8,
276+
+ 9,
277277
]"})))
278278
)
279279
}
@@ -285,14 +285,14 @@ mod tests {
285285
result,
286286
err(displays_as(contains_substring(indoc! {
287287
"
288-
Difference(-\x1B[1;31mactual\x1B[0m / +\x1B[1;32mexpected\x1B[0m):
288+
Difference(-actual / +expected):
289289
[
290290
1,
291-
\x1B[3m<---- 46 common lines omitted ---->\x1B[0m
291+
<---- 46 common lines omitted ---->
292292
48,
293293
49,
294-
+\x1B[1;32m 50,\x1B[0m
295-
+\x1B[1;32m 51,\x1B[0m
294+
+ 50,
295+
+ 51,
296296
]"})))
297297
)
298298
}
@@ -304,13 +304,13 @@ mod tests {
304304
result,
305305
err(displays_as(contains_substring(indoc! {
306306
"
307-
Difference(-\x1B[1;31mactual\x1B[0m / +\x1B[1;32mexpected\x1B[0m):
307+
Difference(-actual / +expected):
308308
[
309-
-\x1B[1;31m 1,\x1B[0m
310-
-\x1B[1;31m 2,\x1B[0m
309+
- 1,
310+
- 2,
311311
3,
312312
4,
313-
\x1B[3m<---- 46 common lines omitted ---->\x1B[0m
313+
<---- 46 common lines omitted ---->
314314
51,
315315
]"})))
316316
)
@@ -357,8 +357,8 @@ mod tests {
357357
err(displays_as(contains_substring(indoc!(
358358
"
359359
First line
360-
-\x1B[1;31mSecond line\x1B[0m
361-
+\x1B[1;32mSecond lines\x1B[0m
360+
-Second line
361+
+Second lines
362362
Third line
363363
"
364364
))))
@@ -380,9 +380,7 @@ mod tests {
380380

381381
verify_that!(
382382
result,
383-
err(displays_as(not(contains_substring(
384-
"Difference(-\x1B[1;31mactual\x1B[0m / +\x1B[1;32mexpected\x1B[0m):"
385-
))))
383+
err(displays_as(not(contains_substring("Difference(-actual / +expected):"))))
386384
)
387385
}
388386

@@ -401,9 +399,7 @@ mod tests {
401399

402400
verify_that!(
403401
result,
404-
err(displays_as(not(contains_substring(
405-
"Difference(-\x1B[1;31mactual\x1B[0m / +\x1B[1;32mexpected\x1B[0m):"
406-
))))
402+
err(displays_as(not(contains_substring("Difference(-actual / +expected):"))))
407403
)
408404
}
409405
}

0 commit comments

Comments
 (0)