Make sure IsATTY does not clobber errno.
Exposition of the problem:
> cat main.cpp
TEST(errnoTest, errnoTest)
{
    ASSERT_EQ(errno, 0);
}
int main(int argc, char** argv)
{
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}
Compiled with gcc 10 like this:
> g++ -pthread -o runtest main.cpp -Wl,-Bstatic -lgtest -Wl,-Bdynamic
Before patch:
>  ./runtest
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from errnoTest
[ RUN      ] errnoTest.errnoTest
[       OK ] errnoTest.errnoTest (0 ms)
[----------] 1 test from errnoTest (0 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 1 test.
(output is colored, I run this inside an interactive terminal).
> ./runtest | cat
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from errnoTest
[ RUN      ] errnoTest.errnoTest
main.cpp:5: Failure
Expected equality of these values:
  (*__errno_location ())
    Which is: 25
  0
[  FAILED  ] errnoTest.errnoTest (0 ms)
[----------] 1 test from errnoTest (0 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 0 tests.
[  FAILED  ] 1 test, listed below:
[  FAILED  ] errnoTest.errnoTest
 1 FAILED TEST
(output is not colored, since IsTTY return false, because of the pipe,
however it also clobbered errno for the tests).
After the patch, both cases are working fine:
> ./runtest
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from errnoTest
[ RUN      ] errnoTest.errnoTest
[       OK ] errnoTest.errnoTest (0 ms)
[----------] 1 test from errnoTest (0 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 1 test.
> ./runtest | cat
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from errnoTest
[ RUN      ] errnoTest.errnoTest
[       OK ] errnoTest.errnoTest (0 ms)
[----------] 1 test from errnoTest (0 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 1 test.
			
			
This commit is contained in:
		@@ -252,6 +252,7 @@
 | 
				
			|||||||
#include <stdio.h>
 | 
					#include <stdio.h>
 | 
				
			||||||
#include <stdlib.h>
 | 
					#include <stdlib.h>
 | 
				
			||||||
#include <string.h>
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					#include <cerrno>
 | 
				
			||||||
#include <cstdint>
 | 
					#include <cstdint>
 | 
				
			||||||
#include <limits>
 | 
					#include <limits>
 | 
				
			||||||
#include <type_traits>
 | 
					#include <type_traits>
 | 
				
			||||||
@@ -1960,16 +1961,16 @@ namespace posix {
 | 
				
			|||||||
typedef struct _stat StatStruct;
 | 
					typedef struct _stat StatStruct;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# ifdef __BORLANDC__
 | 
					# ifdef __BORLANDC__
 | 
				
			||||||
inline int IsATTY(int fd) { return isatty(fd); }
 | 
					inline int DoIsATTY(int fd) { return isatty(fd); }
 | 
				
			||||||
inline int StrCaseCmp(const char* s1, const char* s2) {
 | 
					inline int StrCaseCmp(const char* s1, const char* s2) {
 | 
				
			||||||
  return stricmp(s1, s2);
 | 
					  return stricmp(s1, s2);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
inline char* StrDup(const char* src) { return strdup(src); }
 | 
					inline char* StrDup(const char* src) { return strdup(src); }
 | 
				
			||||||
# else  // !__BORLANDC__
 | 
					# else  // !__BORLANDC__
 | 
				
			||||||
#  if GTEST_OS_WINDOWS_MOBILE
 | 
					#  if GTEST_OS_WINDOWS_MOBILE
 | 
				
			||||||
inline int IsATTY(int /* fd */) { return 0; }
 | 
					inline int DoIsATTY(int /* fd */) { return 0; }
 | 
				
			||||||
#  else
 | 
					#  else
 | 
				
			||||||
inline int IsATTY(int fd) { return _isatty(fd); }
 | 
					inline int DoIsATTY(int fd) { return _isatty(fd); }
 | 
				
			||||||
#  endif  // GTEST_OS_WINDOWS_MOBILE
 | 
					#  endif  // GTEST_OS_WINDOWS_MOBILE
 | 
				
			||||||
inline int StrCaseCmp(const char* s1, const char* s2) {
 | 
					inline int StrCaseCmp(const char* s1, const char* s2) {
 | 
				
			||||||
  return _stricmp(s1, s2);
 | 
					  return _stricmp(s1, s2);
 | 
				
			||||||
@@ -1994,7 +1995,7 @@ inline bool IsDir(const StatStruct& st) {
 | 
				
			|||||||
typedef struct stat StatStruct;
 | 
					typedef struct stat StatStruct;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
inline int FileNo(FILE* file) { return fileno(file); }
 | 
					inline int FileNo(FILE* file) { return fileno(file); }
 | 
				
			||||||
inline int IsATTY(int fd) { return isatty(fd); }
 | 
					inline int DoIsATTY(int fd) { return isatty(fd); }
 | 
				
			||||||
inline int Stat(const char* path, StatStruct* buf) {
 | 
					inline int Stat(const char* path, StatStruct* buf) {
 | 
				
			||||||
  // stat function not implemented on ESP8266
 | 
					  // stat function not implemented on ESP8266
 | 
				
			||||||
  return 0;
 | 
					  return 0;
 | 
				
			||||||
@@ -2011,7 +2012,7 @@ inline bool IsDir(const StatStruct& st) { return S_ISDIR(st.st_mode); }
 | 
				
			|||||||
typedef struct stat StatStruct;
 | 
					typedef struct stat StatStruct;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
inline int FileNo(FILE* file) { return fileno(file); }
 | 
					inline int FileNo(FILE* file) { return fileno(file); }
 | 
				
			||||||
inline int IsATTY(int fd) { return isatty(fd); }
 | 
					inline int DoIsATTY(int fd) { return isatty(fd); }
 | 
				
			||||||
inline int Stat(const char* path, StatStruct* buf) { return stat(path, buf); }
 | 
					inline int Stat(const char* path, StatStruct* buf) { return stat(path, buf); }
 | 
				
			||||||
inline int StrCaseCmp(const char* s1, const char* s2) {
 | 
					inline int StrCaseCmp(const char* s1, const char* s2) {
 | 
				
			||||||
  return strcasecmp(s1, s2);
 | 
					  return strcasecmp(s1, s2);
 | 
				
			||||||
@@ -2022,6 +2023,18 @@ inline bool IsDir(const StatStruct& st) { return S_ISDIR(st.st_mode); }
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#endif  // GTEST_OS_WINDOWS
 | 
					#endif  // GTEST_OS_WINDOWS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					inline int IsATTY(int fd)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    // DoIsATTY might change errno (for example ENOTTY in case you redirect stdout
 | 
				
			||||||
 | 
					    // to a file on Linux), which is unexpected, so save the previous value, and
 | 
				
			||||||
 | 
					    // restore it after the call.
 | 
				
			||||||
 | 
					    int savedErrno = errno;
 | 
				
			||||||
 | 
					    int isAttyValue = DoIsATTY(fd);
 | 
				
			||||||
 | 
					    errno = savedErrno;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return isAttyValue;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Functions deprecated by MSVC 8.0.
 | 
					// Functions deprecated by MSVC 8.0.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
GTEST_DISABLE_MSC_DEPRECATED_PUSH_()
 | 
					GTEST_DISABLE_MSC_DEPRECATED_PUSH_()
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user