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