Merge changes If4e8ff72,I53d861fb

* changes:
  Add data table for build library.
  Fix few UI bugs.
diff --git a/scripts/cargo2android.py b/scripts/cargo2android.py
index c6b5f04..730df5a 100755
--- a/scripts/cargo2android.py
+++ b/scripts/cargo2android.py
@@ -72,6 +72,7 @@
     'libminijail': 'libminijail_rust',
     'libsync': 'libsync_rust',
     'libx86_64': 'libx86_64_rust',
+    'libxml': 'libxml_rust',
     'protoc_gen_rust': 'protoc-gen-rust',
 }
 
@@ -303,6 +304,9 @@
     # which can be changed if self is a merged test module.
     self.decide_module_type()
     if should_merge_test:
+      if (self.main_src in self.runner.args.test_blocklist and
+          not other.main_src in self.runner.args.test_blocklist):
+        self.main_src = other.main_src
       self.srcs.append(other.main_src)
       # use a short unique name as the merged module name.
       prefix = self.root_pkg + '_tests'
diff --git a/vndk/tools/header-checker/src/dumper/abi_wrappers.cpp b/vndk/tools/header-checker/src/dumper/abi_wrappers.cpp
index 388f44c..2ff0385 100644
--- a/vndk/tools/header-checker/src/dumper/abi_wrappers.cpp
+++ b/vndk/tools/header-checker/src/dumper/abi_wrappers.cpp
@@ -69,7 +69,7 @@
 
 std::string ABIWrapper::GetDeclSourceFile(const clang::Decl *decl,
                                           const clang::CompilerInstance *cip,
-                                          const std::string &root_dir) {
+                                          const utils::RootDirs &root_dirs) {
   clang::SourceManager &sm = cip->getSourceManager();
   clang::SourceLocation location = decl->getLocation();
   // We need to use the expansion location to identify whether we should recurse
@@ -80,7 +80,7 @@
   // belonging to the library.
   clang::SourceLocation expansion_location = sm.getExpansionLoc(location);
   return utils::NormalizePath(sm.getFilename(expansion_location).str(),
-                              root_dir);
+                              root_dirs);
 }
 
 std::string ABIWrapper::GetCachedDeclSourceFile(
@@ -88,7 +88,7 @@
   assert(decl != nullptr);
   auto result = ast_caches_->decl_to_source_file_cache_.find(decl);
   if (result == ast_caches_->decl_to_source_file_cache_.end()) {
-    return GetDeclSourceFile(decl, cip, ast_caches_->root_dir_);
+    return GetDeclSourceFile(decl, cip, ast_caches_->root_dirs_);
   }
   return result->second;
 }
diff --git a/vndk/tools/header-checker/src/dumper/abi_wrappers.h b/vndk/tools/header-checker/src/dumper/abi_wrappers.h
index 92e1019..b2a039e 100644
--- a/vndk/tools/header-checker/src/dumper/abi_wrappers.h
+++ b/vndk/tools/header-checker/src/dumper/abi_wrappers.h
@@ -49,7 +49,7 @@
  public:
   static std::string GetDeclSourceFile(const clang::Decl *decl,
                                        const clang::CompilerInstance *cip,
-                                       const std::string &root_dir);
+                                       const utils::RootDirs &root_dirs);
 
  protected:
   std::string GetCachedDeclSourceFile(const clang::Decl *decl,
diff --git a/vndk/tools/header-checker/src/dumper/ast_processing.cpp b/vndk/tools/header-checker/src/dumper/ast_processing.cpp
index de97da6..12dcbb3 100644
--- a/vndk/tools/header-checker/src/dumper/ast_processing.cpp
+++ b/vndk/tools/header-checker/src/dumper/ast_processing.cpp
@@ -16,7 +16,6 @@
 
 #include "dumper/abi_wrappers.h"
 #include "repr/ir_dumper.h"
-#include "utils/header_abi_util.h"
 
 #include <clang/AST/PrettyPrinter.h>
 #include <clang/AST/QualTypeNames.h>
@@ -33,14 +32,15 @@
 
 class PrintNormalizedPath : public clang::PrintingCallbacks {
  public:
-  PrintNormalizedPath(const std::string root_dir) : root_dir_(root_dir) {}
+  PrintNormalizedPath(const utils::RootDirs &root_dirs)
+      : root_dirs_(root_dirs) {}
 
   std::string remapPath(llvm::StringRef path) const {
-    return utils::NormalizePath(path.str(), root_dir_);
+    return utils::NormalizePath(path.str(), root_dirs_);
   }
 
  private:
-  const std::string root_dir_;
+  const utils::RootDirs &root_dirs_;
 };
 
 HeaderASTVisitor::HeaderASTVisitor(
@@ -106,7 +106,7 @@
   if (!decl->getDefinition()) {
     if (!options_.dump_function_declarations_ ||
         options_.source_file_ !=
-            ABIWrapper::GetDeclSourceFile(decl, cip_, options_.root_dir_)) {
+            ABIWrapper::GetDeclSourceFile(decl, cip_, options_.root_dirs_)) {
       return true;
     }
   }
@@ -177,7 +177,7 @@
     return true;
   }
   std::string source_file =
-      ABIWrapper::GetDeclSourceFile(decl, cip_, options_.root_dir_);
+      ABIWrapper::GetDeclSourceFile(decl, cip_, options_.root_dirs_);
   ast_caches_->decl_to_source_file_cache_.insert(
       std::make_pair(decl, source_file));
   // If no exported headers are specified we assume the whole AST is exported.
@@ -208,15 +208,15 @@
   // names to avoid inconsistency between C and C++ (for C++ files, this is true
   // by default)
   policy.SuppressTagKeyword = true;
-  PrintNormalizedPath callbacks(options_.root_dir_);
+  PrintNormalizedPath callbacks(options_.root_dirs_);
   policy.Callbacks = &callbacks;
   ctx.setPrintingPolicy(policy);
   clang::TranslationUnitDecl *translation_unit = ctx.getTranslationUnitDecl();
   std::unique_ptr<clang::MangleContext> mangle_contextp(
       ctx.createMangleContext());
-  ASTCaches ast_caches(
-      ABIWrapper::GetDeclSourceFile(translation_unit, cip_, options_.root_dir_),
-      options_.root_dir_);
+  ASTCaches ast_caches(ABIWrapper::GetDeclSourceFile(translation_unit, cip_,
+                                                     options_.root_dirs_),
+                       options_.root_dirs_);
 
   std::unique_ptr<repr::ModuleIR> module(
       new repr::ModuleIR(nullptr /*FIXME*/));
diff --git a/vndk/tools/header-checker/src/dumper/ast_util.h b/vndk/tools/header-checker/src/dumper/ast_util.h
index a4872e7..9960f0b 100644
--- a/vndk/tools/header-checker/src/dumper/ast_util.h
+++ b/vndk/tools/header-checker/src/dumper/ast_util.h
@@ -15,6 +15,8 @@
 #ifndef AST_UTIL_H_
 #define AST_UTIL_H_
 
+#include "utils/source_path_utils.h"
+
 #include <clang/AST/AST.h>
 #include <clang/AST/Type.h>
 
@@ -30,12 +32,12 @@
 
 struct ASTCaches {
   ASTCaches(const std::string &translation_unit_source,
-            const std::string &root_dir)
-      : translation_unit_source_(translation_unit_source), root_dir_(root_dir) {
-  }
+            const utils::RootDirs &root_dirs)
+      : translation_unit_source_(translation_unit_source),
+        root_dirs_(root_dirs) {}
 
   std::string translation_unit_source_;
-  const std::string root_dir_;
+  const utils::RootDirs &root_dirs_;
   std::map<const clang::Decl *, std::string> decl_to_source_file_cache_;
 
   llvm::DenseSet<clang::QualType> converted_qual_types_;
diff --git a/vndk/tools/header-checker/src/dumper/header_checker.cpp b/vndk/tools/header-checker/src/dumper/header_checker.cpp
index 7832ce8..209aebb 100644
--- a/vndk/tools/header-checker/src/dumper/header_checker.cpp
+++ b/vndk/tools/header-checker/src/dumper/header_checker.cpp
@@ -42,9 +42,11 @@
 using header_checker::dumper::HeaderCheckerOptions;
 using header_checker::repr::TextFormatIR;
 using header_checker::utils::CollectAllExportedHeaders;
-using header_checker::utils::GetCwd;
 using header_checker::utils::HideIrrelevantCommandLineOptions;
 using header_checker::utils::NormalizePath;
+using header_checker::utils::ParseRootDirs;
+using header_checker::utils::RootDir;
+using header_checker::utils::RootDirs;
 
 
 static llvm::cl::OptionCategory header_checker_category(
@@ -63,11 +65,13 @@
     "I", llvm::cl::desc("<export_include_dirs>"), llvm::cl::Prefix,
     llvm::cl::ZeroOrMore, llvm::cl::cat(header_checker_category));
 
-static llvm::cl::opt<std::string> root_dir(
+static llvm::cl::list<std::string> root_dirs(
     "root-dir",
-    llvm::cl::desc("Specify the directory that the paths in the dump file are "
-                   "relative to. Default to current working directory"),
-    llvm::cl::Optional, llvm::cl::cat(header_checker_category));
+    llvm::cl::desc("Specify the directory that the paths in the dump files "
+                   "are relative to. The format is <path>:<replacement> or "
+                   "<path>. If this option is not specified, it defaults to "
+                   "current working directory."),
+    llvm::cl::ZeroOrMore, llvm::cl::cat(header_checker_category));
 
 static llvm::cl::opt<bool> no_filter(
     "no-filter", llvm::cl::desc("Do not filter any abi"), llvm::cl::Optional,
@@ -172,17 +176,17 @@
     ::exit(1);
   }
 
-  const std::string root_dir_or_cwd = (root_dir.empty() ? GetCwd() : root_dir);
+  RootDirs parsed_root_dirs = ParseRootDirs(root_dirs);
 
   bool dump_exported_only = (!no_filter && !exported_header_dirs.empty());
   std::set<std::string> exported_headers =
-      CollectAllExportedHeaders(exported_header_dirs, root_dir_or_cwd);
+      CollectAllExportedHeaders(exported_header_dirs, parsed_root_dirs);
 
   // Initialize clang tools and run front-end action.
   std::vector<std::string> header_files{ header_file };
   HeaderCheckerOptions options(
-      NormalizePath(header_file, root_dir_or_cwd), out_dump,
-      std::move(exported_headers), root_dir_or_cwd, output_format,
+      NormalizePath(header_file, parsed_root_dirs), out_dump,
+      std::move(exported_headers), std::move(parsed_root_dirs), output_format,
       dump_exported_only, dump_function_declarations, suppress_errors);
 
   clang::tooling::ClangTool tool(*compilations, header_files);
diff --git a/vndk/tools/header-checker/src/dumper/header_checker.h b/vndk/tools/header-checker/src/dumper/header_checker.h
index 53c917a..7a7506d 100644
--- a/vndk/tools/header-checker/src/dumper/header_checker.h
+++ b/vndk/tools/header-checker/src/dumper/header_checker.h
@@ -16,6 +16,7 @@
 #define HEADER_CHECKER_H_
 
 #include "repr/ir_representation.h"
+#include "utils/source_path_utils.h"
 
 #include <set>
 #include <string>
@@ -30,7 +31,7 @@
   std::string source_file_;
   std::string dump_name_;
   const std::set<std::string> exported_headers_;
-  const std::string root_dir_;
+  const utils::RootDirs root_dirs_;
   repr::TextFormatIR text_format_;
   const bool dump_exported_only_;
   bool dump_function_declarations_;
@@ -39,12 +40,12 @@
  public:
   HeaderCheckerOptions(std::string source_file, std::string dump_name,
                        std::set<std::string> exported_headers,
-                       std::string root_dir, repr::TextFormatIR text_format,
-                       bool dump_exported_only,
+                       utils::RootDirs root_dirs,
+                       repr::TextFormatIR text_format, bool dump_exported_only,
                        bool dump_function_declarations, bool suppress_errors)
       : source_file_(std::move(source_file)), dump_name_(std::move(dump_name)),
         exported_headers_(std::move(exported_headers)),
-        root_dir_(std::move(root_dir)), text_format_(text_format),
+        root_dirs_(std::move(root_dirs)), text_format_(text_format),
         dump_exported_only_(dump_exported_only),
         dump_function_declarations_(dump_function_declarations),
         suppress_errors_(suppress_errors) {}
diff --git a/vndk/tools/header-checker/src/linker/header_abi_linker.cpp b/vndk/tools/header-checker/src/linker/header_abi_linker.cpp
index f857081..e877150 100644
--- a/vndk/tools/header-checker/src/linker/header_abi_linker.cpp
+++ b/vndk/tools/header-checker/src/linker/header_abi_linker.cpp
@@ -19,7 +19,7 @@
 #include "repr/symbol/so_file_parser.h"
 #include "repr/symbol/version_script_parser.h"
 #include "utils/command_line_utils.h"
-#include "utils/header_abi_util.h"
+#include "utils/source_path_utils.h"
 
 #include <llvm/ADT/Optional.h>
 #include <llvm/Support/CommandLine.h>
@@ -39,8 +39,9 @@
 using namespace header_checker;
 using header_checker::repr::TextFormatIR;
 using header_checker::utils::CollectAllExportedHeaders;
-using header_checker::utils::GetCwd;
 using header_checker::utils::HideIrrelevantCommandLineOptions;
+using header_checker::utils::ParseRootDirs;
+using header_checker::utils::RootDir;
 
 
 static llvm::cl::OptionCategory header_linker_category(
@@ -58,11 +59,13 @@
     "I", llvm::cl::desc("<export_include_dirs>"), llvm::cl::Prefix,
     llvm::cl::ZeroOrMore, llvm::cl::cat(header_linker_category));
 
-static llvm::cl::opt<std::string> root_dir(
+static llvm::cl::list<std::string> root_dirs(
     "root-dir",
-    llvm::cl::desc("Specify the directory that the paths in the dump files are "
-                   "relative to. Default to current working directory"),
-    llvm::cl::Optional, llvm::cl::cat(header_linker_category));
+    llvm::cl::desc("Specify the directory that the paths in the dump files "
+                   "are relative to. The format is <path>:<replacement> or "
+                   "<path>. If this option is not specified, it defaults to "
+                   "current working directory."),
+    llvm::cl::ZeroOrMore, llvm::cl::cat(header_linker_category));
 
 static llvm::cl::opt<std::string> version_script(
     "v", llvm::cl::desc("<version_script>"), llvm::cl::Optional,
@@ -255,8 +258,8 @@
   }
 
   // Construct the list of exported headers for source location filtering.
-  exported_headers_ = CollectAllExportedHeaders(
-      exported_header_dirs_, root_dir.empty() ? GetCwd() : root_dir);
+  exported_headers_ = CollectAllExportedHeaders(exported_header_dirs_,
+                                                ParseRootDirs(root_dirs));
 
   // Read all input ABI dumps.
   auto merger = ReadInputDumpFiles();
diff --git a/vndk/tools/header-checker/src/utils/header_abi_util.h b/vndk/tools/header-checker/src/utils/header_abi_util.h
index c9c6b1d..9915c52 100644
--- a/vndk/tools/header-checker/src/utils/header_abi_util.h
+++ b/vndk/tools/header-checker/src/utils/header_abi_util.h
@@ -26,16 +26,6 @@
 namespace utils {
 
 
-std::string GetCwd();
-
-// Resolve '..' and '.'; if the path starts with root_dir, remove the prefix;
-// don't resolve symbolic links.
-std::string NormalizePath(const std::string &path, const std::string &root_dir);
-
-std::set<std::string>
-CollectAllExportedHeaders(const std::vector<std::string> &exported_header_dirs,
-                          const std::string &root_dir);
-
 inline std::string FindAndReplace(const std::string &candidate_str,
                                   const std::string &find_str,
                                   const std::string &replace_str) {
diff --git a/vndk/tools/header-checker/src/utils/source_path_utils.cpp b/vndk/tools/header-checker/src/utils/source_path_utils.cpp
index bc69809..5e6daf4 100644
--- a/vndk/tools/header-checker/src/utils/source_path_utils.cpp
+++ b/vndk/tools/header-checker/src/utils/source_path_utils.cpp
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "utils/header_abi_util.h"
+#include "utils/source_path_utils.h"
 
 #include <llvm/Support/FileSystem.h>
 #include <llvm/Support/Path.h>
@@ -37,33 +37,87 @@
           file_name.endswith(".cc") || file_name.endswith(".c"));
 }
 
-std::string GetCwd() {
+static std::string GetCwd() {
   llvm::SmallString<256> cwd;
   if (llvm::sys::fs::current_path(cwd)) {
     llvm::errs() << "ERROR: Failed to get current working directory\n";
     ::exit(1);
   }
-  return cwd.c_str();
+  return std::string(cwd);
 }
 
-std::string NormalizePath(const std::string &path,
-                          const std::string &root_dir) {
-  llvm::SmallString<256> norm_path(path);
+RootDirs ParseRootDirs(const std::vector<std::string> &args) {
+  RootDirs root_dirs;
+  for (const std::string_view arg : args) {
+    std::string_view path;
+    std::string_view replacement;
+    size_t colon_index = arg.find(":");
+    if (colon_index != std::string_view::npos) {
+      path = arg.substr(0, colon_index);
+      replacement = arg.substr(colon_index + 1);
+    } else {
+      path = arg;
+      replacement = "";
+    }
+    llvm::SmallString<256> norm_replacement(replacement.begin(),
+                                            replacement.end());
+    llvm::sys::path::remove_dots(norm_replacement, /* remove_dot_dot = */ true);
+    root_dirs.emplace_back(NormalizePath(path, {}),
+                           std::string(norm_replacement));
+  }
+  if (root_dirs.empty()) {
+    root_dirs.emplace_back(GetCwd(), "");
+  }
+  // Sort by length in descending order so that NormalizePath finds the longest
+  // matching root dir.
+  std::sort(root_dirs.begin(), root_dirs.end(),
+            [](RootDir &first, RootDir &second) {
+              return first.path.size() > second.path.size();
+            });
+  for (size_t index = 1; index < root_dirs.size(); index++) {
+    if (root_dirs[index - 1].path == root_dirs[index].path) {
+      llvm::errs() << "Duplicate root dir: " << root_dirs[index].path << "\n";
+      ::exit(1);
+    }
+  }
+  return root_dirs;
+}
+
+std::string NormalizePath(std::string_view path, const RootDirs &root_dirs) {
+  llvm::SmallString<256> norm_path(path.begin(), path.end());
   if (llvm::sys::fs::make_absolute(norm_path)) {
     return "";
   }
   llvm::sys::path::remove_dots(norm_path, /* remove_dot_dot = */ true);
-  // Convert /cwd/path to /path.
-  if (llvm::sys::path::replace_path_prefix(norm_path, root_dir, "")) {
-    // Convert /path to path.
-    return llvm::sys::path::relative_path(norm_path.str()).str();
+  llvm::StringRef separator = llvm::sys::path::get_separator();
+  // Convert /root/dir/path to path.
+  for (const RootDir &root_dir : root_dirs) {
+    // llvm::sys::path::replace_path_prefix("AB", "A", "") returns "B", so do
+    // not use it.
+    if (!norm_path.startswith(root_dir.path)) {
+      continue;
+    }
+    if (norm_path.size() == root_dir.path.size()) {
+      return root_dir.replacement;
+    }
+    llvm::StringRef suffix = norm_path.substr(root_dir.path.size());
+    if (suffix.startswith(separator)) {
+      if (root_dir.replacement.empty()) {
+        return suffix.substr(separator.size()).str();
+      }
+      // replacement == "/"
+      if (llvm::StringRef(root_dir.replacement).endswith(separator)) {
+        return root_dir.replacement + suffix.substr(separator.size()).str();
+      }
+      return root_dir.replacement + suffix.str();
+    }
   }
   return std::string(norm_path);
 }
 
 static bool CollectExportedHeaderSet(const std::string &dir_name,
                                      std::set<std::string> *exported_headers,
-                                     const std::string &root_dir) {
+                                     const RootDirs &root_dirs) {
   std::error_code ec;
   llvm::sys::fs::recursive_directory_iterator walker(dir_name, ec);
   // Default construction - end of directory.
@@ -98,17 +152,17 @@
       continue;
     }
 
-    exported_headers->insert(NormalizePath(file_path, root_dir));
+    exported_headers->insert(NormalizePath(file_path, root_dirs));
   }
   return true;
 }
 
 std::set<std::string>
 CollectAllExportedHeaders(const std::vector<std::string> &exported_header_dirs,
-                          const std::string &root_dir) {
+                          const RootDirs &root_dirs) {
   std::set<std::string> exported_headers;
   for (auto &&dir : exported_header_dirs) {
-    if (!CollectExportedHeaderSet(dir, &exported_headers, root_dir)) {
+    if (!CollectExportedHeaderSet(dir, &exported_headers, root_dirs)) {
       llvm::errs() << "Couldn't collect exported headers\n";
       ::exit(1);
     }
diff --git a/vndk/tools/header-checker/src/utils/source_path_utils.h b/vndk/tools/header-checker/src/utils/source_path_utils.h
new file mode 100644
index 0000000..efd927c
--- /dev/null
+++ b/vndk/tools/header-checker/src/utils/source_path_utils.h
@@ -0,0 +1,52 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_PATH_UTILS_H_
+#define SOURCE_PATH_UTILS_H_
+
+#include <set>
+#include <string>
+#include <vector>
+
+
+namespace header_checker {
+namespace utils {
+
+
+struct RootDir {
+  std::string path;
+  std::string replacement;
+
+  RootDir(std::string p, std::string r)
+      : path(std::move(p)), replacement(std::move(r)) {}
+};
+
+typedef std::vector<RootDir> RootDirs;
+
+RootDirs ParseRootDirs(const std::vector<std::string> &args);
+
+// Resolve '..' and '.'; if the path starts with any of root_dirs, replace the
+// prefix; don't resolve symbolic links.
+std::string NormalizePath(std::string_view path, const RootDirs &root_dirs);
+
+std::set<std::string>
+CollectAllExportedHeaders(const std::vector<std::string> &exported_header_dirs,
+                          const RootDirs &root_dirs);
+
+
+}  // namespace utils
+}  // namespace header_checker
+
+
+#endif  // SOURCE_PATH_UTILS_H_
diff --git a/vndk/tools/header-checker/src/utils/source_path_utils_test.cpp b/vndk/tools/header-checker/src/utils/source_path_utils_test.cpp
index 4ef1c65..2b74eba 100644
--- a/vndk/tools/header-checker/src/utils/source_path_utils_test.cpp
+++ b/vndk/tools/header-checker/src/utils/source_path_utils_test.cpp
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "utils/header_abi_util.h"
+#include "utils/source_path_utils.h"
 
 #include <gtest/gtest.h>
 
@@ -21,25 +21,59 @@
 namespace utils {
 
 
-TEST(CollectExportedHeadersTest, NormalizeAbsolutePaths) {
-  const std::string root = "/root/dir";
-  EXPECT_EQ("", NormalizePath(root, root));
-  EXPECT_EQ("/unit/test", NormalizePath("/unit/test", root));
-  EXPECT_EQ("/root/unit/test", NormalizePath(root + "/../unit/test", root));
+TEST(SourcePathUtilsTest, NormalizeAbsolutePaths) {
+  const std::vector<std::string> args{"/root/dir"};
+  const RootDirs root_dirs = ParseRootDirs(args);
+  ASSERT_EQ(1, root_dirs.size());
+  ASSERT_EQ("/root/dir", root_dirs[0].path);
+  ASSERT_EQ("", root_dirs[0].replacement);
+
+  EXPECT_EQ("", NormalizePath("/root/dir", root_dirs));
+  EXPECT_EQ("test", NormalizePath("/root/dir/test", root_dirs));
+  EXPECT_EQ("/root/unit/test",
+            NormalizePath("/root/dir/../unit/test", root_dirs));
 }
 
 
-TEST(CollectExportedHeadersTest, NormalizeCwdPaths) {
-  const std::string cwd = GetCwd();
-  ASSERT_NE("", cwd);
+TEST(SourcePathUtilsTest, NormalizeCwdPaths) {
+  const RootDirs cwd = ParseRootDirs(std::vector<std::string>());
+  ASSERT_EQ(1, cwd.size());
+  ASSERT_NE("", cwd[0].path);
+  ASSERT_EQ("", cwd[0].replacement);
+
   EXPECT_EQ("", NormalizePath("", cwd));
   EXPECT_EQ("unit/test", NormalizePath("./unit/test/.", cwd));
   EXPECT_EQ("unit/test", NormalizePath("unit//test//", cwd));
   EXPECT_EQ("test", NormalizePath("unit/../test", cwd));
-  EXPECT_EQ("unit/test", NormalizePath(cwd + "/unit/test", cwd));
+  EXPECT_EQ("unit/test", NormalizePath(cwd[0].path + "/unit/test", cwd));
   EXPECT_EQ('/', NormalizePath("../unit/test", cwd)[0]);
 }
 
 
+TEST(SourcePathUtilsTest, NormalizePathsWithMultipleRootDirs) {
+  const std::vector<std::string> args{"/before:/", "/before/dir:after"};
+  const RootDirs root_dirs = ParseRootDirs(args);
+  ASSERT_EQ(2, root_dirs.size());
+  ASSERT_EQ("/before/dir", root_dirs[0].path);
+  ASSERT_EQ("after", root_dirs[0].replacement);
+  ASSERT_EQ("/before", root_dirs[1].path);
+  ASSERT_EQ("/", root_dirs[1].replacement);
+
+  EXPECT_EQ("/directory", NormalizePath("/before/directory", root_dirs));
+  EXPECT_EQ("after", NormalizePath("/before/dir", root_dirs));
+}
+
+
+TEST(SourcePathUtilsTest, NormalizeRelativePaths) {
+  const std::vector<std::string> args{"../before/.:..//after/."};
+  const RootDirs root_dirs = ParseRootDirs(args);
+  ASSERT_EQ(1, root_dirs.size());
+  ASSERT_EQ('/', root_dirs[0].path[0]);
+  ASSERT_EQ("../after", root_dirs[0].replacement);
+
+  EXPECT_EQ("../after", NormalizePath("../before", root_dirs));
+}
+
+
 }  // namespace utils
 }  // namespace header_checker