Merge remote-tracking branch 'aosp/upstream' into master am: 49104c833d
am: 60ead3d3de

Change-Id: I062abf66c35d51f5d5804f226c9d7b65aee578cd
diff --git a/.travis.yml b/.travis.yml
index 2fd6fa8..3bbe280 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -18,3 +18,4 @@
   - ./ninja_test
   - ./string_piece_test
   - ./strutil_test
+  - ./find_test
diff --git a/find.cc b/find.cc
index 2af242b..c36c468 100644
--- a/find.cc
+++ b/find.cc
@@ -252,6 +252,8 @@
       return this;
     size_t index = d.find('/');
     const string& p = d.substr(0, index).as_string();
+    if (p.empty() || p == ".")
+      return FindDir(d.substr(index + 1));;
     for (auto& child : children_) {
       if (p == child.first) {
         if (index == string::npos)
@@ -827,15 +829,17 @@
       }
     }
 
+    const DirentNode* root = root_.get();
+
     if (!fc.chdir.empty()) {
       if (!CanHandle(fc.chdir)) {
         LOG("FindEmulator: Cannot handle chdir (%.*s): %s",
             SPF(fc.chdir), cmd.c_str());
         return false;
       }
-      bool should_fallback = false;
-      if (!FindDir(fc.chdir, &should_fallback)) {
-        if (should_fallback)
+      root = root->FindDir(fc.chdir);
+      if (!root) {
+        if (Exists(fc.chdir))
           return false;
         if (!fc.redirect_to_devnull) {
           FIND_WARN_LOC(loc, "FindEmulator: cd: %.*s: No such file or directory",
@@ -847,18 +851,16 @@
 
     vector<string> results;
     for (const string& finddir : fc.finddirs) {
-      const string dir = ConcatDir(fc.chdir, finddir);
-
-      if (!CanHandle(dir)) {
+      if (!CanHandle(finddir)) {
         LOG("FindEmulator: Cannot handle find dir (%s): %s",
-            dir.c_str(), cmd.c_str());
+            finddir.c_str(), cmd.c_str());
         return false;
       }
 
-      bool should_fallback = false;
-      const DirentNode* base = FindDir(dir, &should_fallback);
+      const DirentNode* base;
+      base = root->FindDir(finddir);
       if (!base) {
-        if (should_fallback) {
+        if (Exists(finddir)) {
           return false;
         }
         if (!fc.redirect_to_devnull) {
diff --git a/find_test.cc b/find_test.cc
index ebdfa98..2ee7e39 100644
--- a/find_test.cc
+++ b/find_test.cc
@@ -17,13 +17,16 @@
 #include "find.h"
 
 #include <string>
+#include <unistd.h>
 
+#include "fileutil.h"
 #include "strutil.h"
 
+int FindUnitTests();
+
 int main(int argc, char* argv[]) {
   if (argc == 1) {
-    fprintf(stderr, "TODO: Write unit tests\n");
-    return 1;
+    return FindUnitTests();
   }
 
   InitFindEmulator();
@@ -48,3 +51,98 @@
     printf("%.*s\n", SPF(tok));
   }
 }
+
+string Run(const string& cmd) {
+  string s;
+  int ret = RunCommand("/bin/sh", "-c", cmd, RedirectStderr::NONE, &s);
+
+  if (ret != 0) {
+    fprintf(stderr, "Failed to run `%s`\n", cmd.c_str());
+    exit(ret);
+  }
+
+  return s;
+}
+
+static bool unit_test_failed = false;
+
+void CompareFind(const string& cmd) {
+  string native = Run(cmd);
+
+  FindCommand fc;
+  if (!fc.Parse(cmd)) {
+    fprintf(stderr, "Find emulator cannot parse `%s`\n", cmd.c_str());
+    exit(1);
+  }
+  string emulated;
+  if (!FindEmulator::Get()->HandleFind(cmd, fc, Loc(), &emulated)) {
+    fprintf(stderr, "Find emulator cannot parse `%s`\n", cmd.c_str());
+    exit(1);
+  }
+
+  vector<StringPiece> nativeWords;
+  vector<StringPiece> emulatedWords;
+
+  WordScanner(native).Split(&nativeWords);
+  WordScanner(emulated).Split(&emulatedWords);
+
+  if (nativeWords != emulatedWords) {
+    fprintf(stderr, "Failed to match `%s`:\n", cmd.c_str());
+
+    auto nativeIter = nativeWords.begin();
+    auto emulatedIter = emulatedWords.begin();
+    fprintf(stderr, "%-20s %-20s\n", "Native:", "Emulated:");
+    while (nativeIter != nativeWords.end() || emulatedIter != emulatedWords.end()) {
+      fprintf(stderr, " %-20s %-20s\n",
+              (nativeIter == nativeWords.end()) ? "" : (*nativeIter++).as_string().c_str(),
+              (emulatedIter == emulatedWords.end()) ? "" : (*emulatedIter++).as_string().c_str());
+    }
+    fprintf(stderr, "------------------------------------------\n");
+    unit_test_failed = true;
+  }
+}
+
+int FindUnitTests() {
+  Run("rm -rf out/find");
+  Run("mkdir -p out/find");
+  if (chdir("out/find")) {
+    perror("Failed to chdir(out/find)");
+    return 1;
+  }
+
+  // Set up files under out/find:
+  //  drwxr-x--- top
+  //  lrwxrwxrwx top/E -> missing
+  //  lrwxrwxrwx top/C -> A
+  //  -rw-r----- top/a
+  //  drwxr-x--- top/A
+  //  lrwxrwxrwx top/A/D -> B
+  //  -rw-r----- top/A/b
+  //  drwxr-x--- top/A/B
+  //  -rw-r----- top/A/B/z
+  Run("mkdir -p top/A/B");
+  Run("cd top && ln -s A C");
+  Run("cd top/A && ln -s B D");
+  Run("cd top && ln -s missing E");
+  Run("touch top/a top/A/b top/A/B/z");
+
+  InitFindEmulator();
+
+  CompareFind("find .");
+  CompareFind("find -L .");
+
+  CompareFind("find top/C");
+  CompareFind("find top/C/.");
+  CompareFind("find -L top/C");
+  CompareFind("find -L top/C/.");
+
+  CompareFind("cd top && find C");
+  CompareFind("cd top && find -L C");
+  CompareFind("cd top/C && find .");
+
+  CompareFind("cd top/C && find D/./z");
+
+  CompareFind("find .//top");
+
+  return unit_test_failed ? 1 : 0;
+}