Bug 860827 - Add unit tests for ReadSysFile(). r=dhylands, r=BenWa
☠☠ backed out by 62d955cfe160 ☠ ☠
authorVasil Dimov <vd@FreeBSD.org>
Thu, 18 Apr 2013 08:20:01 -0400
changeset 129194 a945d76d4e4e1b46571ab55db23107ab43c045c5
parent 129193 25d7bd2fd8d932411d3e21dbbef604cbea5e7219
child 129195 62d955cfe160fb391750445372dff7bce6a9d06d
push idunknown
push userunknown
push dateunknown
reviewersdhylands, BenWa
bugs860827
milestone23.0a1
Bug 860827 - Add unit tests for ReadSysFile(). r=dhylands, r=BenWa
toolkit/library/Makefile.in
toolkit/toolkit.mozbuild
xpcom/glue/FileUtils.cpp
xpcom/glue/FileUtils.h
xpcom/glue/tests/gtest/Makefile.in
xpcom/glue/tests/gtest/TestFileUtils.cpp
xpcom/glue/tests/gtest/moz.build
--- a/toolkit/library/Makefile.in
+++ b/toolkit/library/Makefile.in
@@ -292,17 +292,17 @@ endif
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),gonk)
 COMPONENT_LIBS += widget_gonk
 endif
 
 STATIC_LIBS += thebes gl ycbcr
 
 ifdef MOZ_ENABLE_GTEST
-COMPONENT_LIBS += gtest
+COMPONENT_LIBS += gtest xpcom_glue_gtest
 endif
 
 ifdef MOZ_ENABLE_PROFILER_SPS
 COMPONENT_LIBS += profiler
 endif
 
 ifeq (windows,$(MOZ_WIDGET_TOOLKIT))
 COMPONENT_LIBS += widget_windows
--- a/toolkit/toolkit.mozbuild
+++ b/toolkit/toolkit.mozbuild
@@ -125,16 +125,17 @@ if not CONFIG['MOZ_NATIVE_PNG']:
 
 add_tier_dir('platform', 'media/kiss_fft')
 
 if CONFIG['ENABLE_TESTS']:
     add_tier_dir('platform', 'testing/specialpowers')
 
 if CONFIG['MOZ_ENABLE_GTEST']:
     add_tier_dir('platform', 'testing/gtest')
+    add_tier_dir('platform', 'xpcom/glue/tests/gtest')
 
 add_tier_dir('platform', [
     'uriloader',
     'caps',
     'parser',
     'gfx',
     'image',
     'dom',
--- a/xpcom/glue/FileUtils.cpp
+++ b/xpcom/glue/FileUtils.cpp
@@ -112,17 +112,20 @@ mozilla::fallocate(PRFileDesc *aFD, int6
   }
 
   PR_Seek64(aFD, oldpos, PR_SEEK_SET);
   return nWrite == 1;
 #endif
   return false;
 }
 
-#ifdef MOZ_WIDGET_GONK
+/* Define ReadSysFile() only on GONK to avoid unnecessary lubxul bloat.
+Also define it in debug builds, so that unit tests for it can be written
+and run in non-GONK builds. */
+#if defined(MOZ_WIDGET_GONK) || defined(DEBUG)
 
 #undef TEMP_FAILURE_RETRY
 #define TEMP_FAILURE_RETRY(exp) (__extension__({ \
   typeof (exp) _rc; \
   do { \
     _rc = (exp); \
   } while (_rc == -1 && errno == EINTR); \
   _rc; \
@@ -183,17 +186,17 @@ mozilla::ReadSysFile(
   int v;
   if (!ReadSysFile(aFilename, &v)) {
     return false;
   }
   *aVal = (v != 0);
   return true;
 }
 
-#endif /* MOZ_WIDGET_GONK */
+#endif /* MOZ_WIDGET_GONK || DEBUG */
 
 void
 mozilla::ReadAheadLib(nsIFile* aFile)
 {
 #if defined(XP_WIN)
   nsAutoString path;
   if (!aFile || NS_FAILED(aFile->GetPath(path))) {
     return;
--- a/xpcom/glue/FileUtils.h
+++ b/xpcom/glue/FileUtils.h
@@ -140,17 +140,17 @@ NS_COM_GLUE void ReadAheadFile(pathstr_t
  * (on Windows, file must be opened with FILE_FLAG_SEQUENTIAL_SCAN)
  * @param aOffset Offset into the file to begin preloading
  * @param aCount Number of bytes to preload (SIZE_MAX implies file size)
  */
 NS_COM_GLUE void ReadAhead(filedesc_t aFd, const size_t aOffset = 0,
                            const size_t aCount = SIZE_MAX);
 
 
-#ifdef MOZ_WIDGET_GONK
+#if defined(MOZ_WIDGET_GONK) || defined(DEBUG)
 
 /**
  * Read the contents of a file.
  * This function is intended for reading a single-lined text files from
  * /sys/. If the file ends with a newline ('\n') then it will be discarded.
  * The output buffer will always be '\0'-terminated on successful completion.
  * If aBufSize == 0, then this function will return true if the file exists
  * and is readable (it will not attempt to read anything from it).
@@ -179,12 +179,12 @@ ReadSysFile(
  * (either 0 or 1).
  * @return true on success
  */
 bool
 ReadSysFile(
   const char* aFilename,
   bool* aVal);
 
-#endif /* MOZ_WIDGET_GONK */
+#endif /* MOZ_WIDGET_GONK || DEBUG */
 
 } // namespace mozilla
 #endif
new file mode 100644
--- /dev/null
+++ b/xpcom/glue/tests/gtest/Makefile.in
@@ -0,0 +1,27 @@
+# vim:set ts=8 sw=8 sts=8 noet:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DEPTH = @DEPTH@
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE_NAME = xpcom_glue_gtest
+LIBRARY_NAME = xpcom_glue_gtest
+EXPORT_LIBRARY = 1
+LIBXUL_LIBRARY = 1
+IS_COMPONENT = 1
+
+LOCAL_INCLUDES = \
+  -I$(srcdir)/../.. \
+  $(NULL)
+
+CPPSRCS = \
+  TestFileUtils.cpp \
+  $(NULL)
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/xpcom/glue/tests/gtest/TestFileUtils.cpp
@@ -0,0 +1,279 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* 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 <unistd.h>
+
+#include "FileUtils.h"
+
+#include "gtest/gtest.h"
+
+namespace mozilla {
+
+/**
+ * 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 = 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 = 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 = TEMP_FAILURE_RETRY(close(fd));
+  if (ret == -1) {
+    fprintf(stderr, "close(): %s: %s\n", aFilename, strerror(errno));
+    return false;
+  }
+  return true;
+}
+
+#if defined(MOZ_WIDGET_GONK) || defined(DEBUG)
+
+TEST(ReadSysFile, Nonexistent) {
+  bool ret;
+  int errno_saved;
+
+  ret = ReadSysFile("/nonexistent", NULL, 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 /* MOZ_WIDGET_GONK || DEBUG */
+
+}
new file mode 100644
--- /dev/null
+++ b/xpcom/glue/tests/gtest/moz.build
@@ -0,0 +1,7 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+MODULE = 'xpcom_glue_gtest'