Use Chromium's code for locating the src dir.

This code is much more sophisticated in that it doesn't rely
on argv[0], but rather asks the OS where our executable is.
We can then simply go two steps up since we count on running
in out/Whatever relative to the src dir. This is how Chromium
does it.

The aim here is to get rid of SetExecutablePath, which will
be the next CL.

Bug: webrtc:9792
Change-Id: I7da027b7391e759b1f612de12f27a244fe884c09
Reviewed-on: https://webrtc-review.googlesource.com/c/103121
Reviewed-by: Artem Titov <titovartem@webrtc.org>
Commit-Queue: Patrik Höglund <phoglund@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#25017}
This commit is contained in:
Patrik Höglund 2018-10-05 14:52:11 +02:00 committed by Commit Bot
parent 0a5792e907
commit 8434aeb3a7
7 changed files with 263 additions and 106 deletions

View File

@ -359,9 +359,10 @@ if (rtc_include_tests) {
}
if (is_ios) {
rtc_source_set("fileutils_objc") {
rtc_source_set("fileutils_ios_objc") {
visibility = [ ":fileutils" ]
sources = [
"testsupport/iosfileutils.h",
"testsupport/iosfileutils.mm",
]
deps = [
@ -373,6 +374,19 @@ if (is_ios) {
}
}
if (is_mac) {
rtc_source_set("fileutils_mac_objc") {
visibility = [ ":fileutils" ]
sources = [
"testsupport/macfileutils.h",
"testsupport/macfileutils.mm",
]
deps = [
"../rtc_base:checks",
]
}
}
rtc_source_set("fileutils") {
testonly = true
visibility = [ "*" ]
@ -388,7 +402,10 @@ rtc_source_set("fileutils") {
"//third_party/abseil-cpp/absl/types:optional",
]
if (is_ios) {
deps += [ ":fileutils_objc" ]
deps += [ ":fileutils_ios_objc" ]
}
if (is_mac) {
deps += [ ":fileutils_mac_objc" ]
}
if (is_win) {
deps += [ "../rtc_base:rtc_base" ]

View File

@ -12,11 +12,17 @@
#include <assert.h>
#ifdef WIN32
#if defined(WEBRTC_POSIX)
#include <unistd.h>
#endif
#if defined(WEBRTC_WIN)
#include <direct.h>
#include <tchar.h>
#include <windows.h>
#include <algorithm>
#include <codecvt>
#include <locale>
#include "Shlwapi.h"
#include "WinDef.h"
@ -42,35 +48,34 @@
#include <memory>
#include <utility>
#if defined(WEBRTC_IOS)
#include "test/testsupport/iosfileutils.h"
#endif
#if defined(WEBRTC_MAC)
#include "test/testsupport/macfileutils.h"
#endif
#include "rtc_base/arraysize.h"
#include "rtc_base/checks.h"
#include "rtc_base/stringutils.h"
namespace webrtc {
namespace test {
#if defined(WEBRTC_IOS)
// Defined in iosfileutils.mm. No header file to discourage use elsewhere.
std::string IOSOutputPath();
std::string IOSRootPath();
std::string IOSResourcePath(std::string name, std::string extension);
#endif
namespace {
#ifdef WIN32
#if defined(WEBRTC_WIN)
const char* kPathDelimiter = "\\";
#else
const char* kPathDelimiter = "/";
#endif
#ifdef WEBRTC_ANDROID
const char* kRootDirName = "/sdcard/chromium_tests_root/";
#else
#if !defined(WEBRTC_IOS)
const char* kOutputDirName = "out";
#if defined(WEBRTC_ANDROID)
// This is a special case in Chrome infrastructure. See
// base/test/test_support_android.cc.
const char* kAndroidChromiumTestsRoot = "/sdcard/chromium_tests_root/";
#endif
const char* kFallbackPath = "./";
#endif // !defined(WEBRTC_ANDROID)
#if !defined(WEBRTC_IOS)
const char* kResourcesDirName = "resources";
@ -83,6 +88,19 @@ bool relative_dir_path_set = false;
const char* kCannotFindProjectRootDir = "ERROR_CANNOT_FIND_PROJECT_ROOT_DIR";
std::string DirName(const std::string& path) {
if (path.empty())
return "";
if (path == kPathDelimiter)
return path;
std::string result = path;
if (result.back() == *kPathDelimiter)
result.pop_back(); // Remove trailing separator.
return result.substr(0, result.find_last_of(kPathDelimiter));
}
void SetExecutablePath(const std::string& path) {
std::string working_dir = WorkingDir();
std::string temp_path = path;
@ -99,7 +117,7 @@ void SetExecutablePath(const std::string& path) {
#endif
// Trim away the executable name; only store the relative dir path.
temp_path = temp_path.substr(0, temp_path.find_last_of(kPathDelimiter));
temp_path = DirName(temp_path);
strncpy(relative_dir_path, temp_path.c_str(), FILENAME_MAX);
relative_dir_path_set = true;
}
@ -115,78 +133,71 @@ bool DirExists(const std::string& directory_name) {
S_ISDIR(directory_info.st_mode);
}
#ifdef WEBRTC_ANDROID
// Finds the WebRTC src dir.
// The returned path always ends with a path separator.
std::string ProjectRootPath() {
return kRootDirName;
}
std::string OutputPath() {
return kRootDirName;
}
std::string WorkingDir() {
return kRootDirName;
}
#else // WEBRTC_ANDROID
std::string ProjectRootPath() {
#if defined(WEBRTC_IOS)
#if defined(WEBRTC_ANDROID)
return kAndroidChromiumTestsRoot;
#elif defined WEBRTC_IOS
return IOSRootPath();
#else
std::string path = WorkingDir();
if (path == kFallbackPath) {
#elif defined(WEBRTC_MAC)
std::string path;
GetNSExecutablePath(&path);
std::string exe_dir = DirName(path);
// On Mac, tests execute in out/Whatever, so src is two levels up except if
// the test is bundled (which our tests are not), in which case it's 5 levels.
return DirName(DirName(exe_dir)) + kPathDelimiter;
#elif defined(WEBRTC_POSIX)
char buf[PATH_MAX];
ssize_t count = ::readlink("/proc/self/exe", buf, arraysize(buf));
if (count <= 0) {
RTC_NOTREACHED() << "Unable to resolve /proc/self/exe.";
return kCannotFindProjectRootDir;
}
if (relative_dir_path_set) {
path = path + kPathDelimiter + relative_dir_path;
}
path = path + kPathDelimiter + ".." + kPathDelimiter + "..";
char canonical_path[FILENAME_MAX];
#ifdef WIN32
BOOL succeeded = PathCanonicalizeA(canonical_path, path.c_str());
#else
bool succeeded = realpath(path.c_str(), canonical_path) != NULL;
#endif
if (succeeded) {
path = std::string(canonical_path) + kPathDelimiter;
return path;
} else {
fprintf(stderr, "Cannot find project root directory!\n");
// On POSIX, tests execute in out/Whatever, so src is two levels up.
std::string exe_dir = DirName(std::string(buf, count));
return DirName(DirName(exe_dir)) + kPathDelimiter;
#elif defined(WEBRTC_WIN)
wchar_t buf[MAX_PATH];
buf[0] = 0;
if (GetModuleFileName(NULL, buf, MAX_PATH) == 0)
return kCannotFindProjectRootDir;
}
std::string exe_path = rtc::ToUtf8(std::wstring(buf));
std::string exe_dir = DirName(exe_path);
return DirName(DirName(exe_dir)) + kPathDelimiter;
#endif
}
std::string OutputPath() {
#if defined(WEBRTC_IOS)
return IOSOutputPath();
#elif defined(WEBRTC_ANDROID)
return kAndroidChromiumTestsRoot;
#else
std::string path = ProjectRootPath();
if (path == kCannotFindProjectRootDir) {
return kFallbackPath;
}
path += kOutputDirName;
RTC_DCHECK_NE(path, kCannotFindProjectRootDir);
path += "out";
if (!CreateDir(path)) {
return kFallbackPath;
return "./";
}
return path + kPathDelimiter;
#endif
}
std::string WorkingDir() {
#if defined(WEBRTC_ANDROID)
return kAndroidChromiumTestsRoot;
#endif
char path_buffer[FILENAME_MAX];
if (!GET_CURRENT_DIR(path_buffer, sizeof(path_buffer))) {
fprintf(stderr, "Cannot get current directory!\n");
return kFallbackPath;
return "./";
} else {
return std::string(path_buffer);
}
}
#endif // !WEBRTC_ANDROID
// Generate a temporary filename in a safe way.
// Largely copied from talk/base/{unixfilesystem,win32filesystem}.cc.
std::string TempFilename(const std::string& dir, const std::string& prefix) {

View File

@ -25,11 +25,14 @@ namespace test {
// to find the project root.
extern const char* kCannotFindProjectRootDir;
// Creates and returns the absolute path to the output directory where log files
// and other test artifacts should be put. The output directory is generally a
// directory named "out" at the top-level of the project, i.e. a subfolder to
// the path returned by ProjectRootPath(). The exception is Android where we use
// /sdcard/ instead.
// Returns the absolute path to the output directory where log files and other
// test artifacts should be put. The output directory is generally a directory
// named "out" at the project root. This root is assumed to be two levels above
// where the test binary is located; this is because tests execute in a dir
// out/Whatever relative to the project root. This convention is also followed
// in Chromium.
//
// The exception is Android where we use /sdcard/ instead.
//
// If symbolic links occur in the path they will be resolved and the actual
// directory will be returned.
@ -100,6 +103,9 @@ bool FileExists(const std::string& file_name);
// Checks if a directory exists.
bool DirExists(const std::string& directory_name);
// Strips the rightmost path segment from a path.
std::string DirName(const std::string& path);
// File size of the supplied file in bytes. Will return 0 if the file is
// empty or if the file does not exist/is readable.
size_t GetFileSize(const std::string& filename);
@ -110,6 +116,7 @@ size_t GetFileSize(const std::string& filename);
// the argv[0] being sent into the main function to make it possible for
// fileutils.h to find the correct project paths even when the working directory
// is outside the project tree (which happens in some cases).
// TODO(bugs.webrtc.org/9792): Deprecated - going away soon.
void SetExecutablePath(const std::string& path_to_executable);
} // namespace test

View File

@ -19,6 +19,7 @@
#include "absl/types/optional.h"
#include "rtc_base/checks.h"
#include "test/gmock.h"
#include "test/gtest.h"
#ifdef WIN32
@ -28,14 +29,19 @@ static const char* kPathDelimiter = "\\";
static const char* kPathDelimiter = "/";
#endif
static const char kTestName[] = "fileutils_unittest";
static const char kExtension[] = "tmp";
using ::testing::EndsWith;
namespace webrtc {
namespace test {
namespace {
std::string Path(const std::string& path) {
std::string result = path;
std::replace(result.begin(), result.end(), '/', *kPathDelimiter);
return result;
}
// Remove files and directories in a directory non-recursively and writes the
// number of deleted items in |num_deleted_entries|.
void CleanDir(const std::string& dir, size_t* num_deleted_entries) {
@ -86,34 +92,34 @@ class FileUtilsTest : public testing::Test {
std::string FileUtilsTest::original_working_dir_ = "";
// Tests that the project output dir path is returned for the default working
// directory that is automatically set when the test executable is launched.
// The test is not fully testing the implementation, since we cannot be sure
// of where the executable was launched from.
#if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS)
#define MAYBE_OutputPathFromUnchangedWorkingDir \
DISABLED_OutputPathFromUnchangedWorkingDir
// The location will vary depending on where the webrtc checkout is on the
// system, but it should end as described above and be an absolute path.
std::string ExpectedRootDirByPlatform() {
#if defined(WEBRTC_ANDROID)
return Path("chromium_tests_root/");
#elif defined(WEBRTC_IOS)
return Path("tmp/");
#else
#define MAYBE_OutputPathFromUnchangedWorkingDir \
OutputPathFromUnchangedWorkingDir
return Path("out/");
#endif
TEST_F(FileUtilsTest, MAYBE_OutputPathFromUnchangedWorkingDir) {
std::string path = webrtc::test::OutputPath();
std::string expected_end = "out";
expected_end = kPathDelimiter + expected_end + kPathDelimiter;
ASSERT_EQ(path.length() - expected_end.length(), path.find(expected_end));
}
TEST_F(FileUtilsTest, OutputPathFromUnchangedWorkingDir) {
std::string expected_end = ExpectedRootDirByPlatform();
std::string result = webrtc::test::OutputPath();
ASSERT_THAT(result, EndsWith(expected_end));
}
// Tests with current working directory set to a directory higher up in the
// directory tree than the project root dir.
#if defined(WEBRTC_ANDROID) || defined(WIN32) || defined(WEBRTC_IOS)
#define MAYBE_OutputPathFromRootWorkingDir DISABLED_OutputPathFromRootWorkingDir
#else
#define MAYBE_OutputPathFromRootWorkingDir OutputPathFromRootWorkingDir
#endif
TEST_F(FileUtilsTest, MAYBE_OutputPathFromRootWorkingDir) {
TEST_F(FileUtilsTest, OutputPathFromRootWorkingDir) {
ASSERT_EQ(0, chdir(kPathDelimiter));
ASSERT_EQ("./", webrtc::test::OutputPath());
std::string expected_end = ExpectedRootDirByPlatform();
std::string result = webrtc::test::OutputPath();
ASSERT_THAT(result, EndsWith(expected_end));
}
TEST_F(FileUtilsTest, TempFilename) {
@ -152,30 +158,37 @@ TEST_F(FileUtilsTest, MAYBE_CreateDir) {
}
TEST_F(FileUtilsTest, WorkingDirReturnsValue) {
// Hard to cover all platforms. Just test that it returns something without
// crashing:
// This will obviously be different depending on where the webrtc checkout is,
// so just check something is returned.
std::string working_dir = webrtc::test::WorkingDir();
ASSERT_GT(working_dir.length(), 0u);
}
// Due to multiple platforms, it is hard to make a complete test for
// ResourcePath. Manual testing has been performed by removing files and
// verified the result confirms with the specified documentation for the
// function.
TEST_F(FileUtilsTest, ResourcePathReturnsValue) {
std::string resource = webrtc::test::ResourcePath(kTestName, kExtension);
ASSERT_GT(resource.find(kTestName), 0u);
ASSERT_GT(resource.find(kExtension), 0u);
TEST_F(FileUtilsTest, ResourcePathReturnsCorrectPath) {
std::string result = webrtc::test::ResourcePath(
Path("video_coding/frame-ethernet-ii"), "pcap");
#if defined(WEBRTC_IOS)
// iOS bundles resources straight into the bundle root.
std::string expected_end = Path("/frame-ethernet-ii.pcap");
#else
// Other platforms: it's a separate dir.
std::string expected_end =
Path("resources/video_coding/frame-ethernet-ii.pcap");
#endif
ASSERT_THAT(result, EndsWith(expected_end));
ASSERT_TRUE(FileExists(result)) << "Expected " << result << " to exist; did "
<< "ResourcePath return an incorrect path?";
}
TEST_F(FileUtilsTest, ResourcePathFromRootWorkingDir) {
ASSERT_EQ(0, chdir(kPathDelimiter));
std::string resource = webrtc::test::ResourcePath(kTestName, kExtension);
std::string resource = webrtc::test::ResourcePath("whatever", "ext");
#if !defined(WEBRTC_IOS)
ASSERT_NE(resource.find("resources"), std::string::npos);
#endif
ASSERT_GT(resource.find(kTestName), 0u);
ASSERT_GT(resource.find(kExtension), 0u);
ASSERT_GT(resource.find("whatever"), 0u);
ASSERT_GT(resource.find("ext"), 0u);
}
TEST_F(FileUtilsTest, GetFileSizeExistingFile) {
@ -220,7 +233,7 @@ TEST_F(FileUtilsTest, WriteReadDeleteFilesAndDirs) {
// Create an empty temporary directory for this test.
const std::string temp_directory =
OutputPath() + "TempFileUtilsTestReadDirectory" + kPathDelimiter;
OutputPath() + Path("TempFileUtilsTestReadDirectory/");
CreateDir(temp_directory);
EXPECT_NO_FATAL_FAILURE(CleanDir(temp_directory, &num_deleted_entries));
EXPECT_TRUE(DirExists(temp_directory));
@ -231,7 +244,7 @@ TEST_F(FileUtilsTest, WriteReadDeleteFilesAndDirs) {
EXPECT_TRUE(FileExists(temp_filename));
// Add an empty directory.
const std::string temp_subdir = temp_directory + "subdir" + kPathDelimiter;
const std::string temp_subdir = temp_directory + Path("subdir/");
EXPECT_TRUE(CreateDir(temp_subdir));
EXPECT_TRUE(DirExists(temp_subdir));
@ -246,5 +259,21 @@ TEST_F(FileUtilsTest, WriteReadDeleteFilesAndDirs) {
EXPECT_FALSE(DirExists(temp_directory));
}
TEST_F(FileUtilsTest, DirNameStripsFilename) {
EXPECT_EQ(Path("/some/path"), DirName(Path("/some/path/file.txt")));
}
TEST_F(FileUtilsTest, DirNameKeepsStrippingRightmostPathComponent) {
EXPECT_EQ(Path("/some"), DirName(DirName(Path("/some/path/file.txt"))));
}
TEST_F(FileUtilsTest, DirNameDoesntCareIfAPathEndsInPathSeparator) {
EXPECT_EQ(Path("/some"), DirName(Path("/some/path/")));
}
TEST_F(FileUtilsTest, DirNameStopsAtRoot) {
EXPECT_EQ(Path("/"), DirName(Path("/")));
}
} // namespace test
} // namespace webrtc

View File

@ -0,0 +1,26 @@
/*
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef TEST_TESTSUPPORT_IOSFILEUTILS_H_
#define TEST_TESTSUPPORT_IOSFILEUTILS_H_
#include <string>
namespace webrtc {
namespace test {
std::string IOSOutputPath();
std::string IOSRootPath();
std::string IOSResourcePath(std::string name, std::string extension);
} // namespace test
} // namespace webrtc
#endif // TEST_TESTSUPPORT_IOSFILEUTILS_H_

View File

@ -0,0 +1,24 @@
/*
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef TEST_TESTSUPPORT_MACFILEUTILS_H_
#define TEST_TESTSUPPORT_MACFILEUTILS_H_
#include <string>
namespace webrtc {
namespace test {
void GetNSExecutablePath(std::string* path);
} // namespace test
} // namespace webrtc
#endif // TEST_TESTSUPPORT_MACFILEUTILS_H_

View File

@ -0,0 +1,43 @@
/*
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#import <Foundation/Foundation.h>
#include <dlfcn.h>
#include <mach-o/dyld.h>
#include <stdint.h>
#include <stdlib.h>
#include "rtc_base/checks.h"
namespace webrtc {
namespace test {
void GetNSExecutablePath(std::string* path) {
RTC_DCHECK(path);
// Executable path can have relative references ("..") depending on
// how the app was launched.
uint32_t executable_length = 0;
_NSGetExecutablePath(NULL, &executable_length);
RTC_DCHECK_GT(executable_length, 1u);
char executable_path[PATH_MAX + 1];
int rv = _NSGetExecutablePath(executable_path, &executable_length);
RTC_DCHECK_EQ(rv, 0);
char full_path[PATH_MAX];
if (realpath(executable_path, full_path) == nullptr) {
*path = "";
return;
}
*path = full_path;
}
} // namespace test
} // namespace webrtc