Bug 860827 - Add unit tests for ReadSysFile(). r=dhylands, r=BenWa
authorVasil Dimov <vd@FreeBSD.org>
Tue, 23 Apr 2013 13:55:46 -0400
changeset 140611 de74564a8bf371937b5b512c0ec494a402c61bde
parent 140610 f7526aecfa5b6b24dc6b9c5ea40667f2c13c1fd4
child 140612 8e57ba27d6dd53136fd239d024af0333c900d498
push id2579
push userakeybl@mozilla.com
push dateMon, 24 Jun 2013 18:52:47 +0000
treeherdermozilla-beta@b69b7de8a05a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdhylands, BenWa
bugs860827
milestone23.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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,17 @@ mozilla::fallocate(PRFileDesc *aFD, int6
   }
 
   PR_Seek64(aFD, oldpos, PR_SEEK_SET);
   return nWrite == 1;
 #endif
   return false;
 }
 
-#ifdef MOZ_WIDGET_GONK
+#ifdef ReadSysFile_PRESENT
 
 #undef TEMP_FAILURE_RETRY
 #define TEMP_FAILURE_RETRY(exp) (__extension__({ \
   typeof (exp) _rc; \
   do { \
     _rc = (exp); \
   } while (_rc == -1 && errno == EINTR); \
   _rc; \
@@ -183,17 +183,17 @@ mozilla::ReadSysFile(
   int v;
   if (!ReadSysFile(aFilename, &v)) {
     return false;
   }
   *aVal = (v != 0);
   return true;
 }
 
-#endif /* MOZ_WIDGET_GONK */
+#endif /* ReadSysFile_PRESENT */
 
 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,24 @@ 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
+/* 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)) && defined(XP_UNIX)
+
+#ifndef ReadSysFile_PRESENT
+#define ReadSysFile_PRESENT
+#endif /* ReadSysFile_PRESENT */
 
 /**
  * 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 +186,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) && XP_UNIX */
 
 } // 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 {
+
+#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 = 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;
+}
+
+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 /* ReadSysFile_PRESENT */
+
+}
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'