michael@0: /* vim:set ts=2 sw=2 sts=2 et: */ michael@0: /* Any copyright is dedicated to the Public Domain. michael@0: * http://creativecommons.org/publicdomain/zero/1.0/ michael@0: */ michael@0: michael@0: /* Test ReadSysFile() */ michael@0: michael@0: #include michael@0: #include michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include "FileUtils.h" michael@0: michael@0: #include "gtest/gtest.h" michael@0: michael@0: namespace mozilla { michael@0: michael@0: #ifdef ReadSysFile_PRESENT michael@0: michael@0: /** michael@0: * Create a file with the specified contents. michael@0: */ michael@0: static bool michael@0: WriteFile( michael@0: const char* aFilename, michael@0: const void* aContents, michael@0: size_t aContentsLen) michael@0: { michael@0: int fd; michael@0: ssize_t ret; michael@0: size_t offt; michael@0: michael@0: fd = MOZ_TEMP_FAILURE_RETRY( michael@0: open(aFilename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR)); michael@0: if (fd == -1) { michael@0: fprintf(stderr, "open(): %s: %s\n", aFilename, strerror(errno)); michael@0: return false; michael@0: } michael@0: michael@0: offt = 0; michael@0: do { michael@0: ret = MOZ_TEMP_FAILURE_RETRY( michael@0: write(fd, (char*)aContents + offt, aContentsLen - offt)); michael@0: if (ret == -1) { michael@0: fprintf(stderr, "write(): %s: %s\n", aFilename, strerror(errno)); michael@0: close(fd); michael@0: return false; michael@0: } michael@0: offt += ret; michael@0: } while (offt < aContentsLen); michael@0: michael@0: ret = MOZ_TEMP_FAILURE_RETRY(close(fd)); michael@0: if (ret == -1) { michael@0: fprintf(stderr, "close(): %s: %s\n", aFilename, strerror(errno)); michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: TEST(ReadSysFile, Nonexistent) { michael@0: bool ret; michael@0: int errno_saved; michael@0: michael@0: ret = ReadSysFile("/nonexistent", nullptr, 0); michael@0: errno_saved = errno; michael@0: michael@0: ASSERT_FALSE(ret); michael@0: ASSERT_EQ(errno_saved, ENOENT); michael@0: } michael@0: michael@0: TEST(ReadSysFile, Main) { michael@0: /* Use a different file name for each test since different tests could be michael@0: executed concurrently. */ michael@0: static const char* fn = "TestReadSysFileMain"; michael@0: /* If we have a file which contains "abcd" and we read it with ReadSysFile(), michael@0: providing a buffer of size 10 bytes, we would expect 5 bytes to be written michael@0: to that buffer: "abcd\0". */ michael@0: struct { michael@0: /* input (file contents), e.g. "abcd" */ michael@0: const char* input; michael@0: /* pretended output buffer size, e.g. 10; the actual buffer is larger michael@0: and we check if anything was written past the end of the allowed length */ michael@0: size_t output_size; michael@0: /* expected number of bytes written to the output buffer, including the michael@0: terminating '\0', e.g. 5 */ michael@0: size_t output_len; michael@0: /* expected output buffer contents, e.g. "abcd\0", the first output_len michael@0: bytes of the output buffer should match the first 'output_len' bytes from michael@0: 'output', the rest of the output buffer should be untouched. */ michael@0: const char* output; michael@0: } tests[] = { michael@0: /* No new lines */ michael@0: {"", 0, 0, ""}, michael@0: {"", 1, 1, "\0"}, /* \0 is redundant, but we write it for clarity */ michael@0: {"", 9, 1, "\0"}, michael@0: michael@0: {"a", 0, 0, ""}, michael@0: {"a", 1, 1, "\0"}, michael@0: {"a", 2, 2, "a\0"}, michael@0: {"a", 9, 2, "a\0"}, michael@0: michael@0: {"abcd", 0, 0, ""}, michael@0: {"abcd", 1, 1, "\0"}, michael@0: {"abcd", 2, 2, "a\0"}, michael@0: {"abcd", 3, 3, "ab\0"}, michael@0: {"abcd", 4, 4, "abc\0"}, michael@0: {"abcd", 5, 5, "abcd\0"}, michael@0: {"abcd", 9, 5, "abcd\0"}, michael@0: michael@0: /* A single trailing new line */ michael@0: {"\n", 0, 0, ""}, michael@0: {"\n", 1, 1, "\0"}, michael@0: {"\n", 2, 1, "\0"}, michael@0: {"\n", 9, 1, "\0"}, michael@0: michael@0: {"a\n", 0, 0, ""}, michael@0: {"a\n", 1, 1, "\0"}, michael@0: {"a\n", 2, 2, "a\0"}, michael@0: {"a\n", 3, 2, "a\0"}, michael@0: {"a\n", 9, 2, "a\0"}, michael@0: michael@0: {"abcd\n", 0, 0, ""}, michael@0: {"abcd\n", 1, 1, "\0"}, michael@0: {"abcd\n", 2, 2, "a\0"}, michael@0: {"abcd\n", 3, 3, "ab\0"}, michael@0: {"abcd\n", 4, 4, "abc\0"}, michael@0: {"abcd\n", 5, 5, "abcd\0"}, michael@0: {"abcd\n", 6, 5, "abcd\0"}, michael@0: {"abcd\n", 9, 5, "abcd\0"}, michael@0: michael@0: /* Multiple trailing new lines */ michael@0: {"\n\n", 0, 0, ""}, michael@0: {"\n\n", 1, 1, "\0"}, michael@0: {"\n\n", 2, 2, "\n\0"}, michael@0: {"\n\n", 3, 2, "\n\0"}, michael@0: {"\n\n", 9, 2, "\n\0"}, michael@0: michael@0: {"a\n\n", 0, 0, ""}, michael@0: {"a\n\n", 1, 1, "\0"}, michael@0: {"a\n\n", 2, 2, "a\0"}, michael@0: {"a\n\n", 3, 3, "a\n\0"}, michael@0: {"a\n\n", 4, 3, "a\n\0"}, michael@0: {"a\n\n", 9, 3, "a\n\0"}, michael@0: michael@0: {"abcd\n\n", 0, 0, ""}, michael@0: {"abcd\n\n", 1, 1, "\0"}, michael@0: {"abcd\n\n", 2, 2, "a\0"}, michael@0: {"abcd\n\n", 3, 3, "ab\0"}, michael@0: {"abcd\n\n", 4, 4, "abc\0"}, michael@0: {"abcd\n\n", 5, 5, "abcd\0"}, michael@0: {"abcd\n\n", 6, 6, "abcd\n\0"}, michael@0: {"abcd\n\n", 7, 6, "abcd\n\0"}, michael@0: {"abcd\n\n", 9, 6, "abcd\n\0"}, michael@0: michael@0: /* New line in the middle */ michael@0: {"ab\ncd", 9, 6, "ab\ncd\0"}, michael@0: }; michael@0: michael@0: for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { michael@0: ASSERT_TRUE(WriteFile(fn, tests[i].input, strlen(tests[i].input))); michael@0: /* Leave the file to exist if some of the assertions fail. */ michael@0: michael@0: char buf[128]; michael@0: static const char unmodified = 'X'; michael@0: michael@0: memset(buf, unmodified, sizeof(buf)); michael@0: michael@0: ASSERT_TRUE(ReadSysFile(fn, buf, tests[i].output_size)); michael@0: michael@0: if (tests[i].output_size == 0) { michael@0: /* The buffer must be unmodified. We check only the first byte. */ michael@0: ASSERT_EQ(unmodified, buf[0]); michael@0: } else { michael@0: ASSERT_EQ(tests[i].output_len, strlen(buf) + 1); michael@0: ASSERT_STREQ(tests[i].output, buf); michael@0: /* Check that the first byte after the trailing '\0' has not been michael@0: modified. */ michael@0: ASSERT_EQ(unmodified, buf[tests[i].output_len]); michael@0: } michael@0: } michael@0: michael@0: unlink(fn); michael@0: } michael@0: michael@0: TEST(ReadSysFile, Int) { michael@0: static const char* fn = "TestReadSysFileInt"; michael@0: struct { michael@0: /* input (file contents), e.g. "5" */ michael@0: const char* input; michael@0: /* expected return value, if false, then the output is not checked */ michael@0: bool ret; michael@0: /* expected result */ michael@0: int output; michael@0: } tests[] = { michael@0: {"0", true, 0}, michael@0: {"00", true, 0}, michael@0: {"1", true, 1}, michael@0: {"5", true, 5}, michael@0: {"55", true, 55}, michael@0: michael@0: {" 123", true, 123}, michael@0: {"123 ", true, 123}, michael@0: {" 123 ", true, 123}, michael@0: {"123\n", true, 123}, michael@0: michael@0: {"", false, 0}, michael@0: {" ", false, 0}, michael@0: {"a", false, 0}, michael@0: michael@0: {"-1", true, -1}, michael@0: {" -456 ", true, -456}, michael@0: {" -78.9 ", true, -78}, michael@0: }; michael@0: michael@0: for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { michael@0: ASSERT_TRUE(WriteFile(fn, tests[i].input, strlen(tests[i].input))); michael@0: /* Leave the file to exist if some of the assertions fail. */ michael@0: michael@0: bool ret; michael@0: int output = 424242; michael@0: michael@0: ret = ReadSysFile(fn, &output); michael@0: michael@0: ASSERT_EQ(tests[i].ret, ret); michael@0: michael@0: if (ret) { michael@0: ASSERT_EQ(tests[i].output, output); michael@0: } michael@0: } michael@0: michael@0: unlink(fn); michael@0: } michael@0: michael@0: TEST(ReadSysFile, Bool) { michael@0: static const char* fn = "TestReadSysFileBool"; michael@0: struct { michael@0: /* input (file contents), e.g. "1" */ michael@0: const char* input; michael@0: /* expected return value */ michael@0: bool ret; michael@0: /* expected result */ michael@0: bool output; michael@0: } tests[] = { michael@0: {"0", true, false}, michael@0: {"00", true, false}, michael@0: {"1", true, true}, michael@0: {"5", true, true}, michael@0: {"23", true, true}, michael@0: {"-1", true, true}, michael@0: michael@0: {"", false, true /* unused */}, michael@0: }; michael@0: michael@0: for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { michael@0: ASSERT_TRUE(WriteFile(fn, tests[i].input, strlen(tests[i].input))); michael@0: /* Leave the file to exist if some of the assertions fail. */ michael@0: michael@0: bool ret; michael@0: bool output; michael@0: michael@0: ret = ReadSysFile(fn, &output); michael@0: michael@0: ASSERT_EQ(tests[i].ret, ret); michael@0: michael@0: if (ret) { michael@0: ASSERT_EQ(tests[i].output, output); michael@0: } michael@0: } michael@0: michael@0: unlink(fn); michael@0: } michael@0: michael@0: #endif /* ReadSysFile_PRESENT */ michael@0: michael@0: }