blob: cb88472e425bf2afc6a898864c9309cbd4ebb535 [file] [log] [blame]
kjellander@webrtc.org7951e812011-10-13 12:24:41 +00001/*
andrew@webrtc.org7a281a52012-06-27 03:22:37 +00002 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
kjellander@webrtc.org7951e812011-10-13 12:24:41 +00003 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "test/testsupport/fileutils.h"
kjellander@webrtc.org7951e812011-10-13 12:24:41 +000012
kjellander@webrtc.org7de47bc2014-04-16 08:04:26 +000013#include <assert.h>
henrike@webrtc.orgf2aafe42014-04-29 17:54:17 +000014
Patrik Höglund8434aeb2018-10-05 14:52:11 +020015#if defined(WEBRTC_POSIX)
16#include <unistd.h>
17#endif
18
19#if defined(WEBRTC_WIN)
kjellander@webrtc.org7951e812011-10-13 12:24:41 +000020#include <direct.h>
kjellander@webrtc.org7de47bc2014-04-16 08:04:26 +000021#include <tchar.h>
22#include <windows.h>
kjellander@webrtc.orgde499662013-08-29 11:26:41 +000023#include <algorithm>
Patrik Höglund8434aeb2018-10-05 14:52:11 +020024#include <codecvt>
25#include <locale>
kjellander@webrtc.org7de47bc2014-04-16 08:04:26 +000026
ehmaldonado00f2ee02016-11-18 07:06:41 -080027#include "Shlwapi.h"
ehmaldonado37535bf2016-12-05 06:42:45 -080028#include "WinDef.h"
ehmaldonado00f2ee02016-11-18 07:06:41 -080029
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020030#include "rtc_base/win32.h"
kjellander@webrtc.org7951e812011-10-13 12:24:41 +000031#define GET_CURRENT_DIR _getcwd
kjellander@webrtc.org7951e812011-10-13 12:24:41 +000032#else
alessiob00b16f42017-06-01 03:29:40 -070033#include <dirent.h>
kjellander@webrtc.org7951e812011-10-13 12:24:41 +000034#include <unistd.h>
kjellander@webrtc.org7de47bc2014-04-16 08:04:26 +000035
kjellander@webrtc.org7951e812011-10-13 12:24:41 +000036#define GET_CURRENT_DIR getcwd
kjellander@webrtc.org4d8cd9d2011-11-09 11:24:14 +000037#endif
38
39#include <sys/stat.h> // To check for directory existence.
Yves Gerey665174f2018-06-19 15:03:05 +020040#ifndef S_ISDIR // Not defined in stat.h on Windows.
41#define S_ISDIR(mode) (((mode)&S_IFMT) == S_IFDIR)
kjellander@webrtc.org7951e812011-10-13 12:24:41 +000042#endif
43
pbos@webrtc.org12dc1a32013-08-05 16:22:53 +000044#include <stdio.h>
kjellander@webrtc.org7de47bc2014-04-16 08:04:26 +000045#include <stdlib.h>
pbos@webrtc.org12dc1a32013-08-05 16:22:53 +000046#include <string.h>
kjellander@webrtc.org7951e812011-10-13 12:24:41 +000047
kwibergbfefb032016-05-01 14:53:46 -070048#include <memory>
alessiob00b16f42017-06-01 03:29:40 -070049#include <utility>
kwibergbfefb032016-05-01 14:53:46 -070050
Patrik Höglund8434aeb2018-10-05 14:52:11 +020051#if defined(WEBRTC_IOS)
52#include "test/testsupport/iosfileutils.h"
53#endif
54
55#if defined(WEBRTC_MAC)
56#include "test/testsupport/macfileutils.h"
57#endif
58
59#include "rtc_base/arraysize.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020060#include "rtc_base/checks.h"
Niels Möllerd7b91312018-05-24 11:21:34 +020061#include "rtc_base/stringutils.h"
kjellander@webrtc.org4ed4f242011-12-05 16:31:12 +000062
kjellander@webrtc.org7951e812011-10-13 12:24:41 +000063namespace webrtc {
64namespace test {
65
henrike@webrtc.org34773d92013-07-08 14:55:23 +000066namespace {
67
Patrik Höglund8434aeb2018-10-05 14:52:11 +020068#if defined(WEBRTC_WIN)
henrike@webrtc.org34773d92013-07-08 14:55:23 +000069const char* kPathDelimiter = "\\";
kjellander@webrtc.org4d8cd9d2011-11-09 11:24:14 +000070#else
henrike@webrtc.org34773d92013-07-08 14:55:23 +000071const char* kPathDelimiter = "/";
kjellander@webrtc.org4d8cd9d2011-11-09 11:24:14 +000072#endif
leozwang@webrtc.org363efef2012-10-16 04:31:20 +000073
Patrik Höglund8434aeb2018-10-05 14:52:11 +020074#if defined(WEBRTC_ANDROID)
75// This is a special case in Chrome infrastructure. See
76// base/test/test_support_android.cc.
77const char* kAndroidChromiumTestsRoot = "/sdcard/chromium_tests_root/";
kjellander@webrtc.org72fd3392014-11-05 06:28:50 +000078#endif
kjellander02060002016-02-16 22:06:12 -080079
henrika1d34fe92015-06-16 10:04:20 +020080#if !defined(WEBRTC_IOS)
kjellander@webrtc.org72fd3392014-11-05 06:28:50 +000081const char* kResourcesDirName = "resources";
henrika1d34fe92015-06-16 10:04:20 +020082#endif
kjellander@webrtc.org72fd3392014-11-05 06:28:50 +000083
pbos@webrtc.orgdb7d82f2013-07-05 08:49:09 +000084char relative_dir_path[FILENAME_MAX];
85bool relative_dir_path_set = false;
henrike@webrtc.org34773d92013-07-08 14:55:23 +000086
87} // namespace
88
89const char* kCannotFindProjectRootDir = "ERROR_CANNOT_FIND_PROJECT_ROOT_DIR";
90
Patrik Höglund8434aeb2018-10-05 14:52:11 +020091std::string DirName(const std::string& path) {
92 if (path.empty())
93 return "";
94 if (path == kPathDelimiter)
95 return path;
96
97 std::string result = path;
98 if (result.back() == *kPathDelimiter)
99 result.pop_back(); // Remove trailing separator.
100
101 return result.substr(0, result.find_last_of(kPathDelimiter));
102}
103
kjellander@webrtc.org193600b2012-10-17 04:39:44 +0000104void SetExecutablePath(const std::string& path) {
105 std::string working_dir = WorkingDir();
106 std::string temp_path = path;
107
108 // Handle absolute paths; convert them to relative paths to the working dir.
109 if (path.find(working_dir) != std::string::npos) {
110 temp_path = path.substr(working_dir.length() + 1);
111 }
Yves Gerey665174f2018-06-19 15:03:05 +0200112// On Windows, when tests are run under memory tools like DrMemory and TSan,
113// slashes occur in the path as directory separators. Make sure we replace
114// such cases with backslashes in order for the paths to be correct.
kjellander@webrtc.orgde499662013-08-29 11:26:41 +0000115#ifdef WIN32
116 std::replace(temp_path.begin(), temp_path.end(), '/', '\\');
117#endif
118
kjellander@webrtc.org193600b2012-10-17 04:39:44 +0000119 // Trim away the executable name; only store the relative dir path.
Patrik Höglund8434aeb2018-10-05 14:52:11 +0200120 temp_path = DirName(temp_path);
kjellander@webrtc.org83b767b2012-10-15 18:14:12 +0000121 strncpy(relative_dir_path, temp_path.c_str(), FILENAME_MAX);
122 relative_dir_path_set = true;
123}
124
ehmaldonado88df0bc2017-02-10 09:27:14 -0800125bool FileExists(const std::string& file_name) {
kjellander@webrtc.org83b767b2012-10-15 18:14:12 +0000126 struct stat file_info = {0};
127 return stat(file_name.c_str(), &file_info) == 0;
128}
129
alessiobe49fede2017-03-15 06:04:59 -0700130bool DirExists(const std::string& directory_name) {
131 struct stat directory_info = {0};
Yves Gerey665174f2018-06-19 15:03:05 +0200132 return stat(directory_name.c_str(), &directory_info) == 0 &&
133 S_ISDIR(directory_info.st_mode);
alessiobe49fede2017-03-15 06:04:59 -0700134}
135
Patrik Höglund8434aeb2018-10-05 14:52:11 +0200136// Finds the WebRTC src dir.
137// The returned path always ends with a path separator.
leozwang@webrtc.org363efef2012-10-16 04:31:20 +0000138std::string ProjectRootPath() {
Patrik Höglund8434aeb2018-10-05 14:52:11 +0200139#if defined(WEBRTC_ANDROID)
140 return kAndroidChromiumTestsRoot;
141#elif defined WEBRTC_IOS
Kári Tristan Helgason470c0882016-10-03 13:13:29 +0200142 return IOSRootPath();
Patrik Höglund8434aeb2018-10-05 14:52:11 +0200143#elif defined(WEBRTC_MAC)
144 std::string path;
145 GetNSExecutablePath(&path);
146 std::string exe_dir = DirName(path);
147 // On Mac, tests execute in out/Whatever, so src is two levels up except if
148 // the test is bundled (which our tests are not), in which case it's 5 levels.
149 return DirName(DirName(exe_dir)) + kPathDelimiter;
150#elif defined(WEBRTC_POSIX)
151 char buf[PATH_MAX];
152 ssize_t count = ::readlink("/proc/self/exe", buf, arraysize(buf));
153 if (count <= 0) {
154 RTC_NOTREACHED() << "Unable to resolve /proc/self/exe.";
kjellander@webrtc.org7951e812011-10-13 12:24:41 +0000155 return kCannotFindProjectRootDir;
156 }
Patrik Höglund8434aeb2018-10-05 14:52:11 +0200157 // On POSIX, tests execute in out/Whatever, so src is two levels up.
158 std::string exe_dir = DirName(std::string(buf, count));
159 return DirName(DirName(exe_dir)) + kPathDelimiter;
160#elif defined(WEBRTC_WIN)
161 wchar_t buf[MAX_PATH];
162 buf[0] = 0;
163 if (GetModuleFileName(NULL, buf, MAX_PATH) == 0)
ehmaldonado00f2ee02016-11-18 07:06:41 -0800164 return kCannotFindProjectRootDir;
Patrik Höglund8434aeb2018-10-05 14:52:11 +0200165
166 std::string exe_path = rtc::ToUtf8(std::wstring(buf));
167 std::string exe_dir = DirName(exe_path);
168 return DirName(DirName(exe_dir)) + kPathDelimiter;
Kári Tristan Helgason470c0882016-10-03 13:13:29 +0200169#endif
kjellander@webrtc.org7951e812011-10-13 12:24:41 +0000170}
171
andrew@webrtc.org0db7dc62011-11-13 01:34:05 +0000172std::string OutputPath() {
kjellander02060002016-02-16 22:06:12 -0800173#if defined(WEBRTC_IOS)
174 return IOSOutputPath();
Patrik Höglund8434aeb2018-10-05 14:52:11 +0200175#elif defined(WEBRTC_ANDROID)
176 return kAndroidChromiumTestsRoot;
kjellander02060002016-02-16 22:06:12 -0800177#else
kjellander@webrtc.org72fd3392014-11-05 06:28:50 +0000178 std::string path = ProjectRootPath();
Patrik Höglund8434aeb2018-10-05 14:52:11 +0200179 RTC_DCHECK_NE(path, kCannotFindProjectRootDir);
180 path += "out";
kjellander@webrtc.org72fd3392014-11-05 06:28:50 +0000181 if (!CreateDir(path)) {
Patrik Höglund8434aeb2018-10-05 14:52:11 +0200182 return "./";
kjellander@webrtc.org72fd3392014-11-05 06:28:50 +0000183 }
184 return path + kPathDelimiter;
kjellander02060002016-02-16 22:06:12 -0800185#endif
kjellander@webrtc.org4d8cd9d2011-11-09 11:24:14 +0000186}
kjellander@webrtc.org4ed4f242011-12-05 16:31:12 +0000187
188std::string WorkingDir() {
Patrik Höglund8434aeb2018-10-05 14:52:11 +0200189#if defined(WEBRTC_ANDROID)
190 return kAndroidChromiumTestsRoot;
191#endif
kjellander@webrtc.org4ed4f242011-12-05 16:31:12 +0000192 char path_buffer[FILENAME_MAX];
193 if (!GET_CURRENT_DIR(path_buffer, sizeof(path_buffer))) {
194 fprintf(stderr, "Cannot get current directory!\n");
Patrik Höglund8434aeb2018-10-05 14:52:11 +0200195 return "./";
kjellander@webrtc.org5b97b122011-12-08 07:42:18 +0000196 } else {
kjellander@webrtc.org4ed4f242011-12-05 16:31:12 +0000197 return std::string(path_buffer);
198 }
199}
200
kjellander@webrtc.org7de47bc2014-04-16 08:04:26 +0000201// Generate a temporary filename in a safe way.
202// Largely copied from talk/base/{unixfilesystem,win32filesystem}.cc.
Yves Gerey665174f2018-06-19 15:03:05 +0200203std::string TempFilename(const std::string& dir, const std::string& prefix) {
kjellander@webrtc.org7de47bc2014-04-16 08:04:26 +0000204#ifdef WIN32
205 wchar_t filename[MAX_PATH];
Yves Gerey665174f2018-06-19 15:03:05 +0200206 if (::GetTempFileName(rtc::ToUtf16(dir).c_str(), rtc::ToUtf16(prefix).c_str(),
207 0, filename) != 0)
nisse07b83882017-03-14 01:32:50 -0700208 return rtc::ToUtf8(filename);
kjellander@webrtc.org7de47bc2014-04-16 08:04:26 +0000209 assert(false);
210 return "";
211#else
212 int len = dir.size() + prefix.size() + 2 + 6;
kwibergbfefb032016-05-01 14:53:46 -0700213 std::unique_ptr<char[]> tempname(new char[len]);
kjellander@webrtc.org7de47bc2014-04-16 08:04:26 +0000214
Yves Gerey665174f2018-06-19 15:03:05 +0200215 snprintf(tempname.get(), len, "%s/%sXXXXXX", dir.c_str(), prefix.c_str());
kjellander@webrtc.org7de47bc2014-04-16 08:04:26 +0000216 int fd = ::mkstemp(tempname.get());
217 if (fd == -1) {
218 assert(false);
219 return "";
220 } else {
221 ::close(fd);
222 }
223 std::string ret(tempname.get());
224 return ret;
225#endif
226}
227
Artem Titove62f6002018-03-19 15:40:00 +0100228std::string GenerateTempFilename(const std::string& dir,
229 const std::string& prefix) {
230 std::string filename = TempFilename(dir, prefix);
231 RemoveFile(filename);
232 return filename;
233}
234
Danil Chapovalov431abd92018-06-18 12:54:17 +0200235absl::optional<std::vector<std::string>> ReadDirectory(std::string path) {
alessiob00b16f42017-06-01 03:29:40 -0700236 if (path.length() == 0)
Danil Chapovalov431abd92018-06-18 12:54:17 +0200237 return absl::optional<std::vector<std::string>>();
alessiob00b16f42017-06-01 03:29:40 -0700238
239#if defined(WEBRTC_WIN)
240 // Append separator character if needed.
241 if (path.back() != '\\')
242 path += '\\';
243
244 // Init.
245 WIN32_FIND_DATA data;
246 HANDLE handle = ::FindFirstFile(rtc::ToUtf16(path + '*').c_str(), &data);
247 if (handle == INVALID_HANDLE_VALUE)
Danil Chapovalov431abd92018-06-18 12:54:17 +0200248 return absl::optional<std::vector<std::string>>();
alessiob00b16f42017-06-01 03:29:40 -0700249
250 // Populate output.
251 std::vector<std::string> found_entries;
252 do {
253 const std::string name = rtc::ToUtf8(data.cFileName);
254 if (name != "." && name != "..")
255 found_entries.emplace_back(path + name);
256 } while (::FindNextFile(handle, &data) == TRUE);
257
258 // Release resources.
259 if (handle != INVALID_HANDLE_VALUE)
260 ::FindClose(handle);
261#else
262 // Append separator character if needed.
263 if (path.back() != '/')
264 path += '/';
265
266 // Init.
267 DIR* dir = ::opendir(path.c_str());
268 if (dir == nullptr)
Danil Chapovalov431abd92018-06-18 12:54:17 +0200269 return absl::optional<std::vector<std::string>>();
alessiob00b16f42017-06-01 03:29:40 -0700270
271 // Populate output.
272 std::vector<std::string> found_entries;
273 while (dirent* dirent = readdir(dir)) {
274 const std::string& name = dirent->d_name;
275 if (name != "." && name != "..")
276 found_entries.emplace_back(path + name);
277 }
278
279 // Release resources.
280 closedir(dir);
281#endif
282
Danil Chapovalov431abd92018-06-18 12:54:17 +0200283 return absl::optional<std::vector<std::string>>(std::move(found_entries));
alessiob00b16f42017-06-01 03:29:40 -0700284}
285
ehmaldonado88df0bc2017-02-10 09:27:14 -0800286bool CreateDir(const std::string& directory_name) {
kjellander@webrtc.org4ed4f242011-12-05 16:31:12 +0000287 struct stat path_info = {0};
288 // Check if the path exists already:
289 if (stat(directory_name.c_str(), &path_info) == 0) {
290 if (!S_ISDIR(path_info.st_mode)) {
Yves Gerey665174f2018-06-19 15:03:05 +0200291 fprintf(stderr,
292 "Path %s exists but is not a directory! Remove this "
kjellander@webrtc.org4ed4f242011-12-05 16:31:12 +0000293 "file and re-run to create the directory.\n",
294 directory_name.c_str());
295 return false;
296 }
297 } else {
298#ifdef WIN32
299 return _mkdir(directory_name.c_str()) == 0;
300#else
Yves Gerey665174f2018-06-19 15:03:05 +0200301 return mkdir(directory_name.c_str(), S_IRWXU | S_IRWXG | S_IRWXO) == 0;
kjellander@webrtc.org4ed4f242011-12-05 16:31:12 +0000302#endif
303 }
304 return true;
305}
306
nisse57efb032017-05-18 03:55:59 -0700307bool RemoveDir(const std::string& directory_name) {
308#ifdef WIN32
Yves Gerey665174f2018-06-19 15:03:05 +0200309 return RemoveDirectoryA(directory_name.c_str()) != FALSE;
nisse57efb032017-05-18 03:55:59 -0700310#else
Yves Gerey665174f2018-06-19 15:03:05 +0200311 return rmdir(directory_name.c_str()) == 0;
nisse57efb032017-05-18 03:55:59 -0700312#endif
313}
314
315bool RemoveFile(const std::string& file_name) {
316#ifdef WIN32
317 return DeleteFileA(file_name.c_str()) != FALSE;
318#else
319 return unlink(file_name.c_str()) == 0;
320#endif
321}
322
ehmaldonado88df0bc2017-02-10 09:27:14 -0800323std::string ResourcePath(const std::string& name,
324 const std::string& extension) {
henrika1d34fe92015-06-16 10:04:20 +0200325#if defined(WEBRTC_IOS)
326 return IOSResourcePath(name, extension);
327#else
kjellander@webrtc.org4ed4f242011-12-05 16:31:12 +0000328 std::string platform = "win";
329#ifdef WEBRTC_LINUX
330 platform = "linux";
331#endif // WEBRTC_LINUX
332#ifdef WEBRTC_MAC
333 platform = "mac";
334#endif // WEBRTC_MAC
ivoc72c08ed2016-01-20 07:26:24 -0800335#ifdef WEBRTC_ANDROID
336 platform = "android";
337#endif // WEBRTC_ANDROID
kjellander@webrtc.org4ed4f242011-12-05 16:31:12 +0000338
Yves Gerey665174f2018-06-19 15:03:05 +0200339 std::string resources_path =
340 ProjectRootPath() + kResourcesDirName + kPathDelimiter;
Niels Möller3d95c312018-07-27 10:02:35 +0200341 std::string resource_file =
342 resources_path + name + "_" + platform + "." + extension;
kjellander@webrtc.org80b26612011-12-07 18:50:17 +0000343 if (FileExists(resource_file)) {
kjellander@webrtc.org4ed4f242011-12-05 16:31:12 +0000344 return resource_file;
345 }
Niels Möller3d95c312018-07-27 10:02:35 +0200346 // Fall back on name without platform.
kjellander@webrtc.org4ed4f242011-12-05 16:31:12 +0000347 return resources_path + name + "." + extension;
henrika1d34fe92015-06-16 10:04:20 +0200348#endif // defined (WEBRTC_IOS)
kjellander@webrtc.org4ed4f242011-12-05 16:31:12 +0000349}
350
Niels Möller392f8d02018-06-01 10:14:44 +0200351std::string JoinFilename(const std::string& dir, const std::string& name) {
352 RTC_CHECK(!dir.empty()) << "Special cases not implemented.";
353 return dir + kPathDelimiter + name;
354}
355
ehmaldonado88df0bc2017-02-10 09:27:14 -0800356size_t GetFileSize(const std::string& filename) {
kjellander@webrtc.org5b97b122011-12-08 07:42:18 +0000357 FILE* f = fopen(filename.c_str(), "rb");
358 size_t size = 0;
359 if (f != NULL) {
360 if (fseek(f, 0, SEEK_END) == 0) {
361 size = ftell(f);
362 }
363 fclose(f);
364 }
365 return size;
366}
367
kjellander@webrtc.org7951e812011-10-13 12:24:41 +0000368} // namespace test
kjellander@webrtc.org4d8cd9d2011-11-09 11:24:14 +0000369} // namespace webrtc