update_engine: Move test-only utils to test_utils.{h,cc}.

utils.{h,cc} contains a collections of basic or small functions used
in different parts of the codebase. The test_utils.{h,cc} instead
contains functions only required during testing split out to a
separated file to be reused in different tests.

This CL moves without changes some functions defined in utils.h that
were only used during unittests. Two other basic functions were replaced
by the same function already present in base/ (StringHasSuffix and
StringHasPrefix). The functions in test_utils.h now have their own
namespace chromeos_update_engine::test_utils so is clear they come
from the test_utils file, in the same way the ones from utils are
in their own namespace.

Some othe minor linter fixes included here.

BUG=chromium:351429
TEST=Unittest still pass.

Change-Id: I73ab72a14158cb21c8e1f404cbc728423bc8f34f
Reviewed-on: https://chromium-review.googlesource.com/229021
Reviewed-by: Alex Deymo <deymo@chromium.org>
Commit-Queue: Alex Deymo <deymo@chromium.org>
Tested-by: Alex Deymo <deymo@chromium.org>
diff --git a/test_utils.cc b/test_utils.cc
index 4a49fb2..bd55ccb 100644
--- a/test_utils.cc
+++ b/test_utils.cc
@@ -4,6 +4,7 @@
 
 #include "update_engine/test_utils.h"
 
+#include <attr/xattr.h>
 #include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -30,8 +31,81 @@
 
 namespace chromeos_update_engine {
 
+namespace test_utils {
+
 const char* const kMountPathTemplate = "UpdateEngineTests_mnt-XXXXXX";
 
+const unsigned char kRandomString[] = {
+  0xf2, 0xb7, 0x55, 0x92, 0xea, 0xa6, 0xc9, 0x57,
+  0xe0, 0xf8, 0xeb, 0x34, 0x93, 0xd9, 0xc4, 0x8f,
+  0xcb, 0x20, 0xfa, 0x37, 0x4b, 0x40, 0xcf, 0xdc,
+  0xa5, 0x08, 0x70, 0x89, 0x79, 0x35, 0xe2, 0x3d,
+  0x56, 0xa4, 0x75, 0x73, 0xa3, 0x6d, 0xd1, 0xd5,
+  0x26, 0xbb, 0x9c, 0x60, 0xbd, 0x2f, 0x5a, 0xfa,
+  0xb7, 0xd4, 0x3a, 0x50, 0xa7, 0x6b, 0x3e, 0xfd,
+  0x61, 0x2b, 0x3a, 0x31, 0x30, 0x13, 0x33, 0x53,
+  0xdb, 0xd0, 0x32, 0x71, 0x5c, 0x39, 0xed, 0xda,
+  0xb4, 0x84, 0xca, 0xbc, 0xbd, 0x78, 0x1c, 0x0c,
+  0xd8, 0x0b, 0x41, 0xe8, 0xe1, 0xe0, 0x41, 0xad,
+  0x03, 0x12, 0xd3, 0x3d, 0xb8, 0x75, 0x9b, 0xe6,
+  0xd9, 0x01, 0xd0, 0x87, 0xf4, 0x36, 0xfa, 0xa7,
+  0x0a, 0xfa, 0xc5, 0x87, 0x65, 0xab, 0x9a, 0x7b,
+  0xeb, 0x58, 0x23, 0xf0, 0xa8, 0x0a, 0xf2, 0x33,
+  0x3a, 0xe2, 0xe3, 0x35, 0x74, 0x95, 0xdd, 0x3c,
+  0x59, 0x5a, 0xd9, 0x52, 0x3a, 0x3c, 0xac, 0xe5,
+  0x15, 0x87, 0x6d, 0x82, 0xbc, 0xf8, 0x7d, 0xbe,
+  0xca, 0xd3, 0x2c, 0xd6, 0xec, 0x38, 0xeb, 0xe4,
+  0x53, 0xb0, 0x4c, 0x3f, 0x39, 0x29, 0xf7, 0xa4,
+  0x73, 0xa8, 0xcb, 0x32, 0x50, 0x05, 0x8c, 0x1c,
+  0x1c, 0xca, 0xc9, 0x76, 0x0b, 0x8f, 0x6b, 0x57,
+  0x1f, 0x24, 0x2b, 0xba, 0x82, 0xba, 0xed, 0x58,
+  0xd8, 0xbf, 0xec, 0x06, 0x64, 0x52, 0x6a, 0x3f,
+  0xe4, 0xad, 0xce, 0x84, 0xb4, 0x27, 0x55, 0x14,
+  0xe3, 0x75, 0x59, 0x73, 0x71, 0x51, 0xea, 0xe8,
+  0xcc, 0xda, 0x4f, 0x09, 0xaf, 0xa4, 0xbc, 0x0e,
+  0xa6, 0x1f, 0xe2, 0x3a, 0xf8, 0x96, 0x7d, 0x30,
+  0x23, 0xc5, 0x12, 0xb5, 0xd8, 0x73, 0x6b, 0x71,
+  0xab, 0xf1, 0xd7, 0x43, 0x58, 0xa7, 0xc9, 0xf0,
+  0xe4, 0x85, 0x1c, 0xd6, 0x92, 0x50, 0x2c, 0x98,
+  0x36, 0xfe, 0x87, 0xaf, 0x43, 0x8f, 0x8f, 0xf5,
+  0x88, 0x48, 0x18, 0x42, 0xcf, 0x42, 0xc1, 0xa8,
+  0xe8, 0x05, 0x08, 0xa1, 0x45, 0x70, 0x5b, 0x8c,
+  0x39, 0x28, 0xab, 0xe9, 0x6b, 0x51, 0xd2, 0xcb,
+  0x30, 0x04, 0xea, 0x7d, 0x2f, 0x6e, 0x6c, 0x3b,
+  0x5f, 0x82, 0xd9, 0x5b, 0x89, 0x37, 0x65, 0x65,
+  0xbe, 0x9f, 0xa3, 0x5d,
+};
+
+bool IsXAttrSupported(const base::FilePath& dir_path) {
+  char *path = strdup(dir_path.Append("xattr_test_XXXXXX").value().c_str());
+
+  int fd = mkstemp(path);
+  if (fd == -1) {
+    PLOG(ERROR) << "Error creating temporary file in " << dir_path.value();
+    free(path);
+    return false;
+  }
+
+  if (unlink(path) != 0) {
+    PLOG(ERROR) << "Error unlinking temporary file " << path;
+    close(fd);
+    free(path);
+    return false;
+  }
+
+  int xattr_res = fsetxattr(fd, "user.xattr-test", "value", strlen("value"), 0);
+  if (xattr_res != 0) {
+    if (errno == ENOTSUP) {
+      // Leave it to call-sites to warn about non-support.
+    } else {
+      PLOG(ERROR) << "Error setting xattr on " << path;
+    }
+  }
+  close(fd);
+  free(path);
+  return xattr_res == 0;
+}
+
 bool WriteFileVector(const string& path, const vector<char>& data) {
   return utils::WriteFile(path.c_str(), &data[0], data.size());
 }
@@ -159,26 +233,26 @@
     string path = iter.GetFullPath();
     EXPECT_TRUE(expected_paths.find(path) != expected_paths.end()) << path;
     EXPECT_EQ(1, expected_paths.erase(path));
-    if (utils::StringHasSuffix(path, "/hi") ||
-        utils::StringHasSuffix(path, "/hello") ||
-        utils::StringHasSuffix(path, "/test") ||
-        utils::StringHasSuffix(path, "/testlink")) {
+    if (EndsWith(path, "/hi", true) ||
+        EndsWith(path, "/hello", true) ||
+        EndsWith(path, "/test", true) ||
+        EndsWith(path, "/testlink", true)) {
       EXPECT_TRUE(S_ISREG(iter.GetStat().st_mode));
-      if (utils::StringHasSuffix(path, "/test"))
+      if (EndsWith(path, "/test", true))
         test_ino = iter.GetStat().st_ino;
-      else if (utils::StringHasSuffix(path, "/testlink"))
+      else if (EndsWith(path, "/testlink", true))
         testlink_ino = iter.GetStat().st_ino;
-    } else if (utils::StringHasSuffix(path, "/some_dir") ||
-               utils::StringHasSuffix(path, "/empty_dir") ||
-               utils::StringHasSuffix(path, "/mnt") ||
-               utils::StringHasSuffix(path, "/lost+found") ||
+    } else if (EndsWith(path, "/some_dir", true) ||
+               EndsWith(path, "/empty_dir", true) ||
+               EndsWith(path, "/mnt", true) ||
+               EndsWith(path, "/lost+found", true) ||
                parent == path) {
       EXPECT_TRUE(S_ISDIR(iter.GetStat().st_mode));
-    } else if (utils::StringHasSuffix(path, "/fifo")) {
+    } else if (EndsWith(path, "/fifo", true)) {
       EXPECT_TRUE(S_ISFIFO(iter.GetStat().st_mode));
-    } else if (utils::StringHasSuffix(path, "/cdev")) {
+    } else if (EndsWith(path, "/cdev", true)) {
       EXPECT_TRUE(S_ISCHR(iter.GetStat().st_mode));
-    } else if (utils::StringHasSuffix(path, "/sym")) {
+    } else if (EndsWith(path, "/sym", true)) {
       EXPECT_TRUE(S_ISLNK(iter.GetStat().st_mode));
     } else {
       LOG(INFO) << "got non hardcoded path: " << path;
@@ -209,6 +283,63 @@
   unmounter_.reset(new ScopedFilesystemUnmounter(*mnt_path));
 }
 
+namespace {
+class ScopedDirCloser {
+ public:
+  explicit ScopedDirCloser(DIR** dir) : dir_(dir) {}
+  ~ScopedDirCloser() {
+    if (dir_ && *dir_) {
+      int r = closedir(*dir_);
+      TEST_AND_RETURN_ERRNO(r == 0);
+      *dir_ = nullptr;
+      dir_ = nullptr;
+    }
+  }
+ private:
+  DIR** dir_;
+};
+}  // namespace
+
+bool RecursiveUnlinkDir(const string& path) {
+  struct stat stbuf;
+  int r = lstat(path.c_str(), &stbuf);
+  TEST_AND_RETURN_FALSE_ERRNO((r == 0) || (errno == ENOENT));
+  if ((r < 0) && (errno == ENOENT))
+    // path request is missing. that's fine.
+    return true;
+  if (!S_ISDIR(stbuf.st_mode)) {
+    TEST_AND_RETURN_FALSE_ERRNO((unlink(path.c_str()) == 0) ||
+                                (errno == ENOENT));
+    // success or path disappeared before we could unlink.
+    return true;
+  }
+  {
+    // We have a dir, unlink all children, then delete dir
+    DIR *dir = opendir(path.c_str());
+    TEST_AND_RETURN_FALSE_ERRNO(dir);
+    ScopedDirCloser dir_closer(&dir);
+    struct dirent dir_entry;
+    struct dirent *dir_entry_p;
+    int err = 0;
+    while ((err = readdir_r(dir, &dir_entry, &dir_entry_p)) == 0) {
+      if (dir_entry_p == nullptr) {
+        // end of stream reached
+        break;
+      }
+      // Skip . and ..
+      if (!strcmp(dir_entry_p->d_name, ".") ||
+          !strcmp(dir_entry_p->d_name, ".."))
+        continue;
+      TEST_AND_RETURN_FALSE(RecursiveUnlinkDir(path + "/" +
+                                               dir_entry_p->d_name));
+    }
+    TEST_AND_RETURN_FALSE(err == 0);
+  }
+  // unlink dir
+  TEST_AND_RETURN_FALSE_ERRNO((rmdir(path.c_str()) == 0) || (errno == ENOENT));
+  return true;
+}
+
 static gboolean RunGMainLoopOnTimeout(gpointer user_data) {
   bool* timeout = static_cast<bool*>(user_data);
   *timeout = true;
@@ -252,4 +383,5 @@
   g_free(gval);
 }
 
+}  // namespace test_utils
 }  // namespace chromeos_update_engine