Skip to content

Commit 25b51c7

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 4b7f548 commit 25b51c7

File tree

11 files changed

+214
-141
lines changed

11 files changed

+214
-141
lines changed

README.md

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

298298
### Configuration variable list
299299

300-
| Variable name | Description |
301-
| ------------------- | -------------------------------------------------- |
302-
| 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. |
300+
| Variable name | Description |
301+
| ------------- | ------------------------------------------------------- |
302+
| NO_COLOR | Disables coloured output. See <https://no-color.org/>. |
303+
| FORCE_COLOR | Forces colours even when the output is piped to a file. |
303304

304305
## Contributing Changes
305306

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/matcher_support/summarize_diff.rs

Lines changed: 26 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,13 @@
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+
};
2524

2625
/// Returns a string describing how the expected and actual lines differ.
2726
///
@@ -195,7 +194,7 @@ struct StyledLine<'a> {
195194

196195
impl<'a> Display for StyledLine<'a> {
197196
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
198-
if std::env::var(NO_COLOR_VAR).is_err() {
197+
if stdout_supports_colour() {
199198
write!(
200199
f,
201200
"{}{}{}{}",
@@ -207,36 +206,29 @@ impl<'a> Display for StyledLine<'a> {
207206
}
208207
}
209208

209+
#[rustversion::since(1.70)]
210+
fn stdout_supports_colour() -> bool {
211+
match (is_env_var_set("NO_COLOR"), is_env_var_set("FORCE_COLOR")) {
212+
(true, _) => false,
213+
(false, true) => true,
214+
(false, false) => std::io::stdout().is_terminal(),
215+
}
216+
}
217+
218+
#[rustversion::not(since(1.70))]
219+
fn stdout_supports_colour() -> bool {
220+
is_env_var_set("FORCE_COLOR")
221+
}
222+
223+
fn is_env_var_set(var: &'static str) -> bool {
224+
std::env::var(var).map(|s| !s.is_empty()).unwrap_or(false)
225+
}
226+
210227
#[cfg(test)]
211228
mod tests {
212229
use super::*;
213230
use crate::{matcher_support::edit_distance::Mode, prelude::*};
214231
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-
}
240232

241233
// Make a long text with each element of the iterator on one line.
242234
// `collection` must contains at least one element.
@@ -277,30 +269,8 @@ mod tests {
277269
}
278270

279271
#[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]
302272
fn create_diff_exact_small_difference_no_color() -> Result<()> {
303-
let _cleanup = set_var("NO_COLOR");
273+
std::env::set_var("NO_COLOR", "1");
304274

305275
verify_that!(
306276
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)