From 2aa4a82499d4becd2284cdb482213d541b8804dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 28 Apr 2024 16:29:10 +0200 Subject: Adding upstream version 86.0.1. Signed-off-by: Daniel Baumann --- .../test/testsupport/always_passing_unittest.cc | 19 ++ .../libwebrtc/webrtc/test/testsupport/fileutils.cc | 363 +++++++++++++++++++++ .../libwebrtc/webrtc/test/testsupport/fileutils.h | 110 +++++++ .../webrtc/test/testsupport/fileutils_unittest.cc | 240 ++++++++++++++ .../webrtc/test/testsupport/frame_reader.h | 75 +++++ .../webrtc/test/testsupport/frame_writer.h | 105 ++++++ .../webrtc/test/testsupport/iosfileutils.mm | 63 ++++ .../webrtc/test/testsupport/jpeg_frame_writer.cc | 90 +++++ .../test/testsupport/jpeg_frame_writer_ios.cc | 31 ++ .../test/testsupport/metrics/video_metrics.cc | 193 +++++++++++ .../test/testsupport/metrics/video_metrics.h | 121 +++++++ .../testsupport/metrics/video_metrics_unittest.cc | 147 +++++++++ .../test/testsupport/mock/mock_frame_reader.h | 33 ++ .../webrtc/test/testsupport/packet_reader.cc | 49 +++ .../webrtc/test/testsupport/packet_reader.h | 54 +++ .../test/testsupport/packet_reader_unittest.cc | 125 +++++++ .../libwebrtc/webrtc/test/testsupport/perf_test.cc | 92 ++++++ .../libwebrtc/webrtc/test/testsupport/perf_test.h | 71 ++++ .../webrtc/test/testsupport/perf_test_unittest.cc | 39 +++ .../webrtc/test/testsupport/test_artifacts.cc | 70 ++++ .../webrtc/test/testsupport/test_artifacts.h | 39 +++ .../test/testsupport/test_artifacts_unittest.cc | 61 ++++ .../webrtc/test/testsupport/unittest_utils.h | 56 ++++ .../webrtc/test/testsupport/y4m_frame_writer.cc | 56 ++++ .../test/testsupport/y4m_frame_writer_unittest.cc | 77 +++++ .../webrtc/test/testsupport/yuv_frame_reader.cc | 91 ++++++ .../test/testsupport/yuv_frame_reader_unittest.cc | 82 +++++ .../webrtc/test/testsupport/yuv_frame_writer.cc | 77 +++++ .../test/testsupport/yuv_frame_writer_unittest.cc | 68 ++++ 29 files changed, 2697 insertions(+) create mode 100644 third_party/libwebrtc/webrtc/test/testsupport/always_passing_unittest.cc create mode 100644 third_party/libwebrtc/webrtc/test/testsupport/fileutils.cc create mode 100644 third_party/libwebrtc/webrtc/test/testsupport/fileutils.h create mode 100644 third_party/libwebrtc/webrtc/test/testsupport/fileutils_unittest.cc create mode 100644 third_party/libwebrtc/webrtc/test/testsupport/frame_reader.h create mode 100644 third_party/libwebrtc/webrtc/test/testsupport/frame_writer.h create mode 100644 third_party/libwebrtc/webrtc/test/testsupport/iosfileutils.mm create mode 100644 third_party/libwebrtc/webrtc/test/testsupport/jpeg_frame_writer.cc create mode 100644 third_party/libwebrtc/webrtc/test/testsupport/jpeg_frame_writer_ios.cc create mode 100644 third_party/libwebrtc/webrtc/test/testsupport/metrics/video_metrics.cc create mode 100644 third_party/libwebrtc/webrtc/test/testsupport/metrics/video_metrics.h create mode 100644 third_party/libwebrtc/webrtc/test/testsupport/metrics/video_metrics_unittest.cc create mode 100644 third_party/libwebrtc/webrtc/test/testsupport/mock/mock_frame_reader.h create mode 100644 third_party/libwebrtc/webrtc/test/testsupport/packet_reader.cc create mode 100644 third_party/libwebrtc/webrtc/test/testsupport/packet_reader.h create mode 100644 third_party/libwebrtc/webrtc/test/testsupport/packet_reader_unittest.cc create mode 100644 third_party/libwebrtc/webrtc/test/testsupport/perf_test.cc create mode 100644 third_party/libwebrtc/webrtc/test/testsupport/perf_test.h create mode 100644 third_party/libwebrtc/webrtc/test/testsupport/perf_test_unittest.cc create mode 100644 third_party/libwebrtc/webrtc/test/testsupport/test_artifacts.cc create mode 100644 third_party/libwebrtc/webrtc/test/testsupport/test_artifacts.h create mode 100644 third_party/libwebrtc/webrtc/test/testsupport/test_artifacts_unittest.cc create mode 100644 third_party/libwebrtc/webrtc/test/testsupport/unittest_utils.h create mode 100644 third_party/libwebrtc/webrtc/test/testsupport/y4m_frame_writer.cc create mode 100644 third_party/libwebrtc/webrtc/test/testsupport/y4m_frame_writer_unittest.cc create mode 100644 third_party/libwebrtc/webrtc/test/testsupport/yuv_frame_reader.cc create mode 100644 third_party/libwebrtc/webrtc/test/testsupport/yuv_frame_reader_unittest.cc create mode 100644 third_party/libwebrtc/webrtc/test/testsupport/yuv_frame_writer.cc create mode 100644 third_party/libwebrtc/webrtc/test/testsupport/yuv_frame_writer_unittest.cc (limited to 'third_party/libwebrtc/webrtc/test/testsupport') diff --git a/third_party/libwebrtc/webrtc/test/testsupport/always_passing_unittest.cc b/third_party/libwebrtc/webrtc/test/testsupport/always_passing_unittest.cc new file mode 100644 index 0000000000..e6318f7e0b --- /dev/null +++ b/third_party/libwebrtc/webrtc/test/testsupport/always_passing_unittest.cc @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2014 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. + */ + +#include "test/gtest.h" + +namespace webrtc { + +// A test that always passes. Useful when all tests in a executable are +// disabled, since a gtest returns exit code 1 if no tests have executed. +TEST(AlwaysPassingTest, AlwaysPassingTest) {} + +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/test/testsupport/fileutils.cc b/third_party/libwebrtc/webrtc/test/testsupport/fileutils.cc new file mode 100644 index 0000000000..2aff75a4d9 --- /dev/null +++ b/third_party/libwebrtc/webrtc/test/testsupport/fileutils.cc @@ -0,0 +1,363 @@ +/* + * Copyright (c) 2012 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. + */ + +#include "test/testsupport/fileutils.h" + +#include + +#ifdef WIN32 +#include +#include +#include +#include + +#include "Shlwapi.h" +#include "WinDef.h" + +#include "rtc_base/win32.h" +#define GET_CURRENT_DIR _getcwd +#else +#include +#include + +#define GET_CURRENT_DIR getcwd +#endif + +#include // To check for directory existence. +#ifndef S_ISDIR // Not defined in stat.h on Windows. +#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#endif + +#include +#include +#include + +#include +#include + +#include "rtc_base/checks.h" +#include "typedefs.h" // NOLINT(build/include) // For architecture defines + +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 +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"; +#endif +const char* kFallbackPath = "./"; +#endif // !defined(WEBRTC_ANDROID) + +#if !defined(WEBRTC_IOS) +const char* kResourcesDirName = "resources"; +#endif + +char relative_dir_path[FILENAME_MAX]; +bool relative_dir_path_set = false; + +} // namespace + +const char* kCannotFindProjectRootDir = "ERROR_CANNOT_FIND_PROJECT_ROOT_DIR"; + +void SetExecutablePath(const std::string& path) { + std::string working_dir = WorkingDir(); + std::string temp_path = path; + + // Handle absolute paths; convert them to relative paths to the working dir. + if (path.find(working_dir) != std::string::npos) { + temp_path = path.substr(working_dir.length() + 1); + } + // On Windows, when tests are run under memory tools like DrMemory and TSan, + // slashes occur in the path as directory separators. Make sure we replace + // such cases with backslashes in order for the paths to be correct. +#ifdef WIN32 + std::replace(temp_path.begin(), temp_path.end(), '/', '\\'); +#endif + + // Trim away the executable name; only store the relative dir path. + temp_path = temp_path.substr(0, temp_path.find_last_of(kPathDelimiter)); + strncpy(relative_dir_path, temp_path.c_str(), FILENAME_MAX); + relative_dir_path_set = true; +} + +bool FileExists(const std::string& file_name) { + struct stat file_info = {0}; + return stat(file_name.c_str(), &file_info) == 0; +} + +bool DirExists(const std::string& directory_name) { + struct stat directory_info = {0}; + return stat(directory_name.c_str(), &directory_info) == 0 && S_ISDIR( + directory_info.st_mode); +} + +#ifdef WEBRTC_ANDROID + +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) + return IOSRootPath(); +#else + std::string path = WorkingDir(); + if (path == kFallbackPath) { + 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"); + return kCannotFindProjectRootDir; + } +#endif +} + +std::string OutputPath() { +#if defined(WEBRTC_IOS) + return IOSOutputPath(); +#else + std::string path = ProjectRootPath(); + if (path == kCannotFindProjectRootDir) { + return kFallbackPath; + } + path += kOutputDirName; + if (!CreateDir(path)) { + return kFallbackPath; + } + return path + kPathDelimiter; +#endif +} + +std::string WorkingDir() { + char path_buffer[FILENAME_MAX]; + if (!GET_CURRENT_DIR(path_buffer, sizeof(path_buffer))) { + fprintf(stderr, "Cannot get current directory!\n"); + return kFallbackPath; + } 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) { +#ifdef WIN32 + wchar_t filename[MAX_PATH]; + if (::GetTempFileName(rtc::ToUtf16(dir).c_str(), + rtc::ToUtf16(prefix).c_str(), 0, filename) != 0) + return rtc::ToUtf8(filename); + assert(false); + return ""; +#else + int len = dir.size() + prefix.size() + 2 + 6; + std::unique_ptr tempname(new char[len]); + + snprintf(tempname.get(), len, "%s/%sXXXXXX", dir.c_str(), + prefix.c_str()); + int fd = ::mkstemp(tempname.get()); + if (fd == -1) { + assert(false); + return ""; + } else { + ::close(fd); + } + std::string ret(tempname.get()); + return ret; +#endif +} + +rtc::Optional> ReadDirectory(std::string path) { + if (path.length() == 0) + return rtc::Optional>(); + +#if defined(WEBRTC_WIN) + // Append separator character if needed. + if (path.back() != '\\') + path += '\\'; + + // Init. + WIN32_FIND_DATA data; + HANDLE handle = ::FindFirstFile(rtc::ToUtf16(path + '*').c_str(), &data); + if (handle == INVALID_HANDLE_VALUE) + return rtc::Optional>(); + + // Populate output. + std::vector found_entries; + do { + const std::string name = rtc::ToUtf8(data.cFileName); + if (name != "." && name != "..") + found_entries.emplace_back(path + name); + } while (::FindNextFile(handle, &data) == TRUE); + + // Release resources. + if (handle != INVALID_HANDLE_VALUE) + ::FindClose(handle); +#else + // Append separator character if needed. + if (path.back() != '/') + path += '/'; + + // Init. + DIR* dir = ::opendir(path.c_str()); + if (dir == nullptr) + return rtc::Optional>(); + + // Populate output. + std::vector found_entries; + while (dirent* dirent = readdir(dir)) { + const std::string& name = dirent->d_name; + if (name != "." && name != "..") + found_entries.emplace_back(path + name); + } + + // Release resources. + closedir(dir); +#endif + + return rtc::Optional>(std::move(found_entries)); +} + +bool CreateDir(const std::string& directory_name) { + struct stat path_info = {0}; + // Check if the path exists already: + if (stat(directory_name.c_str(), &path_info) == 0) { + if (!S_ISDIR(path_info.st_mode)) { + fprintf(stderr, "Path %s exists but is not a directory! Remove this " + "file and re-run to create the directory.\n", + directory_name.c_str()); + return false; + } + } else { +#ifdef WIN32 + return _mkdir(directory_name.c_str()) == 0; +#else + return mkdir(directory_name.c_str(), S_IRWXU | S_IRWXG | S_IRWXO) == 0; +#endif + } + return true; +} + +bool RemoveDir(const std::string& directory_name) { +#ifdef WIN32 + return RemoveDirectoryA(directory_name.c_str()) != FALSE; +#else + return rmdir(directory_name.c_str()) == 0; +#endif +} + +bool RemoveFile(const std::string& file_name) { +#ifdef WIN32 + return DeleteFileA(file_name.c_str()) != FALSE; +#else + return unlink(file_name.c_str()) == 0; +#endif +} + +std::string ResourcePath(const std::string& name, + const std::string& extension) { +#if defined(WEBRTC_IOS) + return IOSResourcePath(name, extension); +#else + std::string platform = "win"; +#ifdef WEBRTC_LINUX + platform = "linux"; +#endif // WEBRTC_LINUX +#ifdef WEBRTC_MAC + platform = "mac"; +#endif // WEBRTC_MAC +#ifdef WEBRTC_ANDROID + platform = "android"; +#endif // WEBRTC_ANDROID + +#ifdef WEBRTC_ARCH_64_BITS + std::string architecture = "64"; +#else + std::string architecture = "32"; +#endif // WEBRTC_ARCH_64_BITS + + std::string resources_path = ProjectRootPath() + kResourcesDirName + + kPathDelimiter; + std::string resource_file = resources_path + name + "_" + platform + "_" + + architecture + "." + extension; + if (FileExists(resource_file)) { + return resource_file; + } + // Try without architecture. + resource_file = resources_path + name + "_" + platform + "." + extension; + if (FileExists(resource_file)) { + return resource_file; + } + // Try without platform. + resource_file = resources_path + name + "_" + architecture + "." + extension; + if (FileExists(resource_file)) { + return resource_file; + } + + // Fall back on name without architecture or platform. + return resources_path + name + "." + extension; +#endif // defined (WEBRTC_IOS) +} + +size_t GetFileSize(const std::string& filename) { + FILE* f = fopen(filename.c_str(), "rb"); + size_t size = 0; + if (f != NULL) { + if (fseek(f, 0, SEEK_END) == 0) { + size = ftell(f); + } + fclose(f); + } + return size; +} + +} // namespace test +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/test/testsupport/fileutils.h b/third_party/libwebrtc/webrtc/test/testsupport/fileutils.h new file mode 100644 index 0000000000..f0560cf979 --- /dev/null +++ b/third_party/libwebrtc/webrtc/test/testsupport/fileutils.h @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2011 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. + */ + +#include + +#ifndef TEST_TESTSUPPORT_FILEUTILS_H_ +#define TEST_TESTSUPPORT_FILEUTILS_H_ + +#include +#include + +#include "api/optional.h" + +namespace webrtc { +namespace test { + +// This is the "directory" returned if the ProjectPath() function fails +// 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. +// +// If symbolic links occur in the path they will be resolved and the actual +// directory will be returned. +// +// Returns the path WITH a trailing path delimiter. If the project root is not +// found, the current working directory ("./") is returned as a fallback. +std::string OutputPath(); + +// Generates an empty file with a unique name in the specified directory and +// returns the file name and path. +std::string TempFilename(const std::string &dir, const std::string &prefix); + +// Returns a path to a resource file for the currently executing platform. +// Adapts to what filenames are currently present in the +// [project-root]/resources/ dir. +// Returns an absolute path according to this priority list (the directory +// part of the path is left out for readability): +// 1. [name]_[platform]_[architecture].[extension] +// 2. [name]_[platform].[extension] +// 3. [name]_[architecture].[extension] +// 4. [name].[extension] +// Where +// * platform is either of "win", "mac" or "linux". +// * architecture is either of "32" or "64". +// +// Arguments: +// name - Name of the resource file. If a plain filename (no directory path) +// is supplied, the file is assumed to be located in resources/ +// If a directory path is prepended to the filename, a subdirectory +// hierarchy reflecting that path is assumed to be present. +// extension - File extension, without the dot, i.e. "bmp" or "yuv". +std::string ResourcePath(const std::string& name, + const std::string& extension); + +// Gets the current working directory for the executing program. +// Returns "./" if for some reason it is not possible to find the working +// directory. +std::string WorkingDir(); + +// Reads the content of a directory and, in case of success, returns a vector +// of strings with one element for each found file or directory. Each element is +// a path created by prepending |dir| to the file/directory name. "." and ".." +// are never added in the returned vector. +rtc::Optional> ReadDirectory(std::string path); + +// Creates a directory if it not already exists. +// Returns true if successful. Will print an error message to stderr and return +// false if a file with the same name already exists. +bool CreateDir(const std::string& directory_name); + +// Removes a directory, which must already be empty. +bool RemoveDir(const std::string& directory_name); + +// Removes a file. +bool RemoveFile(const std::string& file_name); + +// Checks if a file exists. +bool FileExists(const std::string& file_name); + +// Checks if a directory exists. +bool DirExists(const std::string& directory_name); + +// 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); + +// Sets the executable path, i.e. the path to the executable that is being used +// when launching it. This is usually the path relative to the working directory +// but can also be an absolute path. The intention with this function is to pass +// 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). +void SetExecutablePath(const std::string& path_to_executable); + +} // namespace test +} // namespace webrtc + +#endif // TEST_TESTSUPPORT_FILEUTILS_H_ diff --git a/third_party/libwebrtc/webrtc/test/testsupport/fileutils_unittest.cc b/third_party/libwebrtc/webrtc/test/testsupport/fileutils_unittest.cc new file mode 100644 index 0000000000..79988c0dd3 --- /dev/null +++ b/third_party/libwebrtc/webrtc/test/testsupport/fileutils_unittest.cc @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2012 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. + */ + +#include "test/testsupport/fileutils.h" + +#include + +#include +#include +#include +#include + +#include "api/optional.h" +#include "rtc_base/checks.h" +#include "rtc_base/pathutils.h" +#include "test/gtest.h" + +#ifdef WIN32 +#define chdir _chdir +static const char* kPathDelimiter = "\\"; +#else +static const char* kPathDelimiter = "/"; +#endif + +static const char kTestName[] = "fileutils_unittest"; +static const char kExtension[] = "tmp"; + +namespace webrtc { +namespace test { + +namespace { + +// 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) { + RTC_DCHECK(num_deleted_entries); + *num_deleted_entries = 0; + rtc::Optional> dir_content = ReadDirectory(dir); + EXPECT_TRUE(dir_content); + for (const auto& entry : *dir_content) { + if (DirExists(entry)) { + EXPECT_TRUE(RemoveDir(entry)); + (*num_deleted_entries)++; + } else if (FileExists(entry)) { + EXPECT_TRUE(RemoveFile(entry)); + (*num_deleted_entries)++; + } else { + FAIL(); + } + } +} + +void WriteStringInFile(const std::string& what, const std::string& file_path) { + std::ofstream out(file_path); + out << what; + out.close(); +} + +} // namespace + +// Test fixture to restore the working directory between each test, since some +// of them change it with chdir during execution (not restored by the +// gtest framework). +class FileUtilsTest : public testing::Test { + protected: + FileUtilsTest() { + } + ~FileUtilsTest() override {} + // Runs before the first test + static void SetUpTestCase() { + original_working_dir_ = webrtc::test::WorkingDir(); + } + void SetUp() override { + ASSERT_EQ(chdir(original_working_dir_.c_str()), 0); + } + void TearDown() override { + ASSERT_EQ(chdir(original_working_dir_.c_str()), 0); + } + private: + static std::string original_working_dir_; +}; + +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 +#else +#define MAYBE_OutputPathFromUnchangedWorkingDir \ + OutputPathFromUnchangedWorkingDir +#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)); +} + +// 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) { + ASSERT_EQ(0, chdir(kPathDelimiter)); + ASSERT_EQ("./", webrtc::test::OutputPath()); +} + +TEST_F(FileUtilsTest, TempFilename) { + std::string temp_filename = webrtc::test::TempFilename( + webrtc::test::OutputPath(), "TempFilenameTest"); + ASSERT_TRUE(webrtc::test::FileExists(temp_filename)) + << "Couldn't find file: " << temp_filename; + remove(temp_filename.c_str()); +} + +// Only tests that the code executes +#if defined(WEBRTC_IOS) +#define MAYBE_CreateDir DISABLED_CreateDir +#else +#define MAYBE_CreateDir CreateDir +#endif +TEST_F(FileUtilsTest, MAYBE_CreateDir) { + std::string directory = "fileutils-unittest-empty-dir"; + // Make sure it's removed if a previous test has failed: + remove(directory.c_str()); + ASSERT_TRUE(webrtc::test::CreateDir(directory)); + remove(directory.c_str()); +} + +TEST_F(FileUtilsTest, WorkingDirReturnsValue) { + // Hard to cover all platforms. Just test that it returns something without + // crashing: + 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, ResourcePathFromRootWorkingDir) { + ASSERT_EQ(0, chdir(kPathDelimiter)); + std::string resource = webrtc::test::ResourcePath(kTestName, kExtension); +#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); +} + +TEST_F(FileUtilsTest, GetFileSizeExistingFile) { + // Create a file with some dummy data in. + std::string temp_filename = webrtc::test::TempFilename( + webrtc::test::OutputPath(), "fileutils_unittest"); + FILE* file = fopen(temp_filename.c_str(), "wb"); + ASSERT_TRUE(file != NULL) << "Failed to open file: " << temp_filename; + ASSERT_GT(fprintf(file, "%s", "Dummy data"), 0) << + "Failed to write to file: " << temp_filename; + fclose(file); + ASSERT_GT(webrtc::test::GetFileSize(std::string(temp_filename.c_str())), 0u); + remove(temp_filename.c_str()); +} + +TEST_F(FileUtilsTest, GetFileSizeNonExistingFile) { + ASSERT_EQ(0u, webrtc::test::GetFileSize("non-existing-file.tmp")); +} + +TEST_F(FileUtilsTest, DirExists) { + // Check that an existing directory is recognized as such. + ASSERT_TRUE(webrtc::test::DirExists(webrtc::test::OutputPath())) + << "Existing directory not found"; + + // Check that a non-existing directory is recognized as such. + std::string directory = "direxists-unittest-non_existing-dir"; + ASSERT_FALSE(webrtc::test::DirExists(directory)) + << "Non-existing directory found"; + + // Check that an existing file is not recognized as an existing directory. + std::string temp_filename = webrtc::test::TempFilename( + webrtc::test::OutputPath(), "TempFilenameTest"); + ASSERT_TRUE(webrtc::test::FileExists(temp_filename)) + << "Couldn't find file: " << temp_filename; + ASSERT_FALSE(webrtc::test::DirExists(temp_filename)) + << "Existing file recognized as existing directory"; + remove(temp_filename.c_str()); +} + +TEST_F(FileUtilsTest, WriteReadDeleteFilesAndDirs) { + size_t num_deleted_entries; + + // Create an empty temporary directory for this test. + const std::string temp_directory = + OutputPath() + "TempFileUtilsTestReadDirectory" + kPathDelimiter; + CreateDir(temp_directory); + EXPECT_NO_FATAL_FAILURE(CleanDir(temp_directory, &num_deleted_entries)); + EXPECT_TRUE(DirExists(temp_directory)); + + // Add a file. + const std::string temp_filename = temp_directory + "TempFilenameTest"; + WriteStringInFile("test\n", temp_filename); + EXPECT_TRUE(FileExists(temp_filename)); + + // Add an empty directory. + const std::string temp_subdir = temp_directory + "subdir" + kPathDelimiter; + EXPECT_TRUE(CreateDir(temp_subdir)); + EXPECT_TRUE(DirExists(temp_subdir)); + + // Checks. + rtc::Optional> dir_content = + ReadDirectory(temp_directory); + EXPECT_TRUE(dir_content); + EXPECT_EQ(2u, dir_content->size()); + EXPECT_NO_FATAL_FAILURE(CleanDir(temp_directory, &num_deleted_entries)); + EXPECT_EQ(2u, num_deleted_entries); + EXPECT_TRUE(RemoveDir(temp_directory)); + EXPECT_FALSE(DirExists(temp_directory)); +} + +} // namespace test +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/test/testsupport/frame_reader.h b/third_party/libwebrtc/webrtc/test/testsupport/frame_reader.h new file mode 100644 index 0000000000..2e19af344e --- /dev/null +++ b/third_party/libwebrtc/webrtc/test/testsupport/frame_reader.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2011 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_FRAME_READER_H_ +#define TEST_TESTSUPPORT_FRAME_READER_H_ + +#include + +#include + +#include "rtc_base/scoped_ref_ptr.h" +#include "typedefs.h" // NOLINT(build/include) + +namespace webrtc { +class I420Buffer; +namespace test { + +// Handles reading of I420 frames from video files. +class FrameReader { + public: + virtual ~FrameReader() {} + + // Initializes the frame reader, i.e. opens the input file. + // This must be called before reading of frames has started. + // Returns false if an error has occurred, in addition to printing to stderr. + virtual bool Init() = 0; + + // Reads a frame from the input file. On success, returns the frame. + // Returns nullptr if encountering end of file or a read error. + virtual rtc::scoped_refptr ReadFrame() = 0; + + // Closes the input file if open. Essentially makes this class impossible + // to use anymore. Will also be invoked by the destructor. + virtual void Close() = 0; + + // Frame length in bytes of a single frame image. + virtual size_t FrameLength() = 0; + // Total number of frames in the input video source. + virtual int NumberOfFrames() = 0; +}; + +class YuvFrameReaderImpl : public FrameReader { + public: + // Creates a file handler. The input file is assumed to exist and be readable. + // Parameters: + // input_filename The file to read from. + // width, height Size of each frame to read. + YuvFrameReaderImpl(std::string input_filename, int width, int height); + ~YuvFrameReaderImpl() override; + bool Init() override; + rtc::scoped_refptr ReadFrame() override; + void Close() override; + size_t FrameLength() override; + int NumberOfFrames() override; + + private: + const std::string input_filename_; + size_t frame_length_in_bytes_; + const int width_; + const int height_; + int number_of_frames_; + FILE* input_file_; +}; + +} // namespace test +} // namespace webrtc + +#endif // TEST_TESTSUPPORT_FRAME_READER_H_ diff --git a/third_party/libwebrtc/webrtc/test/testsupport/frame_writer.h b/third_party/libwebrtc/webrtc/test/testsupport/frame_writer.h new file mode 100644 index 0000000000..7556050a21 --- /dev/null +++ b/third_party/libwebrtc/webrtc/test/testsupport/frame_writer.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2012 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_FRAME_WRITER_H_ +#define TEST_TESTSUPPORT_FRAME_WRITER_H_ + +#include + +#include + +#include "api/video/video_frame.h" +#include "typedefs.h" // NOLINT(build/include) + +namespace webrtc { +namespace test { + +// Handles writing of video files. +class FrameWriter { + public: + virtual ~FrameWriter() {} + + // Initializes the file handler, i.e. opens the input and output files etc. + // This must be called before reading or writing frames has started. + // Returns false if an error has occurred, in addition to printing to stderr. + virtual bool Init() = 0; + + // Writes a frame of the configured frame length to the output file. + // Returns true if the write was successful, false otherwise. + virtual bool WriteFrame(uint8_t* frame_buffer) = 0; + + // Closes the output file if open. Essentially makes this class impossible + // to use anymore. Will also be invoked by the destructor. + virtual void Close() = 0; + + // Frame length in bytes of a single frame image. + virtual size_t FrameLength() = 0; +}; + +// Writes raw I420 frames in sequence. +class YuvFrameWriterImpl : public FrameWriter { + public: + // Creates a file handler. The input file is assumed to exist and be readable + // and the output file must be writable. + // Parameters: + // output_filename The file to write. Will be overwritten if already + // existing. + // width, height Size of each frame to read. + YuvFrameWriterImpl(std::string output_filename, int width, int height); + ~YuvFrameWriterImpl() override; + bool Init() override; + bool WriteFrame(uint8_t* frame_buffer) override; + void Close() override; + size_t FrameLength() override; + + protected: + const std::string output_filename_; + size_t frame_length_in_bytes_; + const int width_; + const int height_; + FILE* output_file_; +}; + +// Writes raw I420 frames in sequence, but with Y4M file and frame headers for +// more convenient playback in external media players. +class Y4mFrameWriterImpl : public YuvFrameWriterImpl { + public: + Y4mFrameWriterImpl(std::string output_filename, + int width, + int height, + int frame_rate); + ~Y4mFrameWriterImpl() override; + bool Init() override; + bool WriteFrame(uint8_t* frame_buffer) override; + + private: + const int frame_rate_; +}; + +// LibJpeg is not available on iOS. This class will do nothing on iOS. +class JpegFrameWriter { + public: + JpegFrameWriter(const std::string &output_filename); + // Quality can be from 0 (worst) to 100 (best). Best quality is still lossy. + // WriteFrame can be called only once. Subsequent calls will fail. + bool WriteFrame(const VideoFrame& input_frame, int quality); + +#if !defined(WEBRTC_IOS) + private: + bool frame_written_; + const std::string output_filename_; + FILE* output_file_; +#endif +}; + +} // namespace test +} // namespace webrtc + +#endif // TEST_TESTSUPPORT_FRAME_WRITER_H_ diff --git a/third_party/libwebrtc/webrtc/test/testsupport/iosfileutils.mm b/third_party/libwebrtc/webrtc/test/testsupport/iosfileutils.mm new file mode 100644 index 0000000000..a9156db656 --- /dev/null +++ b/third_party/libwebrtc/webrtc/test/testsupport/iosfileutils.mm @@ -0,0 +1,63 @@ +/* + * Copyright 2015 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. + */ + +#if defined(WEBRTC_IOS) + +#import +#include + +#include "rtc_base/checks.h" +#include "sdk/objc/Framework/Classes/Common/helpers.h" +#include "typedefs.h" // NOLINT(build/include) + +namespace webrtc { +namespace test { + +using webrtc::ios::NSStringFromStdString; +using webrtc::ios::StdStringFromNSString; + +// For iOS, resource files are added to the application bundle in the root +// and not in separate folders as is the case for other platforms. This method +// therefore removes any prepended folders and uses only the actual file name. +std::string IOSResourcePath(std::string name, std::string extension) { + @autoreleasepool { + NSString* path = NSStringFromStdString(name); + NSString* fileName = path.lastPathComponent; + NSString* fileType = NSStringFromStdString(extension); + // Get full pathname for the resource identified by the name and extension. + NSString* pathString = [[NSBundle mainBundle] pathForResource:fileName + ofType:fileType]; + return StdStringFromNSString(pathString); + } +} + +std::string IOSRootPath() { + @autoreleasepool { + NSBundle* mainBundle = [NSBundle mainBundle]; + return StdStringFromNSString(mainBundle.bundlePath) + "/"; + } +} + +// For iOS, we don't have access to the output directory. Return the path to the +// temporary directory instead. This is mostly used by tests that need to write +// output files to disk. +std::string IOSOutputPath() { + @autoreleasepool { + NSString* tempDir = NSTemporaryDirectory(); + if (tempDir == nil) + tempDir = @"/tmp"; + return StdStringFromNSString(tempDir); + } +} + +} // namespace test +} // namespace webrtc + +#endif // defined(WEBRTC_IOS) diff --git a/third_party/libwebrtc/webrtc/test/testsupport/jpeg_frame_writer.cc b/third_party/libwebrtc/webrtc/test/testsupport/jpeg_frame_writer.cc new file mode 100644 index 0000000000..4888f65aeb --- /dev/null +++ b/third_party/libwebrtc/webrtc/test/testsupport/jpeg_frame_writer.cc @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2017 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. + */ + +#include + + +#include "common_types.h" // NOLINT(build/include) +#include "common_video/libyuv/include/webrtc_libyuv.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "test/testsupport/frame_writer.h" + +extern "C" { +#if defined(USE_SYSTEM_LIBJPEG) +#include +#else +// Include directory supplied by gn +#include "jpeglib.h" // NOLINT +#endif +} + +namespace webrtc { +namespace test { + +JpegFrameWriter::JpegFrameWriter(const std::string &output_filename) + : frame_written_(false), + output_filename_(output_filename), + output_file_(nullptr) {} + +bool JpegFrameWriter::WriteFrame(const VideoFrame& input_frame, int quality) { + if (frame_written_) { + RTC_LOG(LS_ERROR) << "Only a single frame can be saved to Jpeg."; + return false; + } + const int kColorPlanes = 3; // R, G and B. + size_t rgb_len = input_frame.height() * input_frame.width() * kColorPlanes; + std::unique_ptr rgb_buf(new uint8_t[rgb_len]); + + // kRGB24 actually corresponds to FourCC 24BG which is 24-bit BGR. + if (ConvertFromI420(input_frame, VideoType::kRGB24, 0, rgb_buf.get()) < 0) { + RTC_LOG(LS_ERROR) << "Could not convert input frame to RGB."; + return false; + } + output_file_ = fopen(output_filename_.c_str(), "wb"); + if (!output_file_) { + RTC_LOG(LS_ERROR) << "Couldn't open file to write jpeg frame to:" + << output_filename_; + return false; + } + + // Invoking LIBJPEG + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + JSAMPROW row_pointer[1]; + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_compress(&cinfo); + + jpeg_stdio_dest(&cinfo, output_file_); + + cinfo.image_width = input_frame.width(); + cinfo.image_height = input_frame.height(); + cinfo.input_components = kColorPlanes; + cinfo.in_color_space = JCS_EXT_BGR; + jpeg_set_defaults(&cinfo); + jpeg_set_quality(&cinfo, quality, TRUE); + + jpeg_start_compress(&cinfo, TRUE); + int row_stride = input_frame.width() * kColorPlanes; + while (cinfo.next_scanline < cinfo.image_height) { + row_pointer[0] = &rgb_buf.get()[cinfo.next_scanline * row_stride]; + jpeg_write_scanlines(&cinfo, row_pointer, 1); + } + + jpeg_finish_compress(&cinfo); + jpeg_destroy_compress(&cinfo); + fclose(output_file_); + + frame_written_ = true; + return true; +} + +} // namespace test +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/test/testsupport/jpeg_frame_writer_ios.cc b/third_party/libwebrtc/webrtc/test/testsupport/jpeg_frame_writer_ios.cc new file mode 100644 index 0000000000..567e75e254 --- /dev/null +++ b/third_party/libwebrtc/webrtc/test/testsupport/jpeg_frame_writer_ios.cc @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2017 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. + */ + +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "test/testsupport/frame_writer.h" + + +namespace webrtc { +namespace test { + +JpegFrameWriter::JpegFrameWriter(const std::string& /*output_filename*/) {} + +bool JpegFrameWriter::WriteFrame(const VideoFrame& /*input_frame*/, + int /*quality*/) { + RTC_LOG(LS_WARNING) + << "Libjpeg isn't available on IOS. Jpeg frame writer is not " + "supported. No frame will be saved."; + // Don't fail. + return true; +} + +} // namespace test +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/test/testsupport/metrics/video_metrics.cc b/third_party/libwebrtc/webrtc/test/testsupport/metrics/video_metrics.cc new file mode 100644 index 0000000000..ea5d3f2b5f --- /dev/null +++ b/third_party/libwebrtc/webrtc/test/testsupport/metrics/video_metrics.cc @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2012 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. + */ + +#include "test/testsupport/metrics/video_metrics.h" + +#include +#include + +#include // min_element, max_element +#include + +#include "api/video/i420_buffer.h" +#include "api/video/video_frame.h" +#include "common_video/libyuv/include/webrtc_libyuv.h" +#include "test/frame_utils.h" +#include "libyuv/convert.h" + +namespace webrtc { +namespace test { + +// Copy here so our callers won't need to include libyuv for this constant. +double kMetricsPerfectPSNR = kPerfectPSNR; + +// Used for calculating min and max values. +static bool LessForFrameResultValue(const FrameResult& s1, + const FrameResult& s2) { + return s1.value < s2.value; +} + +enum VideoMetricsType { kPSNR, kSSIM, kBoth }; + +// Calculates metrics for a frame and adds statistics to the result for it. +void CalculateFrame(VideoMetricsType video_metrics_type, + const I420BufferInterface& ref, + const I420BufferInterface& test, + int frame_number, + QualityMetricsResult* result) { + FrameResult frame_result = {0, 0}; + frame_result.frame_number = frame_number; + switch (video_metrics_type) { + case kPSNR: + frame_result.value = I420PSNR(ref, test); + break; + case kSSIM: + frame_result.value = I420SSIM(ref, test); + break; + default: + assert(false); + } + result->frames.push_back(frame_result); +} + +// Calculates average, min and max values for the supplied struct, if non-NULL. +void CalculateStats(QualityMetricsResult* result) { + if (result == NULL || result->frames.size() == 0) { + return; + } + // Calculate average. + std::vector::iterator iter; + double metrics_values_sum = 0.0; + for (iter = result->frames.begin(); iter != result->frames.end(); ++iter) { + metrics_values_sum += iter->value; + } + result->average = metrics_values_sum / result->frames.size(); + + // Calculate min/max statistics. + iter = std::min_element(result->frames.begin(), result->frames.end(), + LessForFrameResultValue); + result->min = iter->value; + result->min_frame_number = iter->frame_number; + iter = std::max_element(result->frames.begin(), result->frames.end(), + LessForFrameResultValue); + result->max = iter->value; + result->max_frame_number = iter->frame_number; +} + +// Single method that handles all combinations of video metrics calculation, to +// minimize code duplication. Either psnr_result or ssim_result may be NULL, +// depending on which VideoMetricsType is targeted. +int CalculateMetrics(VideoMetricsType video_metrics_type, + const char* ref_filename, + const char* test_filename, + int width, + int height, + QualityMetricsResult* psnr_result, + QualityMetricsResult* ssim_result) { + assert(ref_filename != NULL); + assert(test_filename != NULL); + assert(width > 0); + assert(height > 0); + + FILE* ref_fp = fopen(ref_filename, "rb"); + if (ref_fp == NULL) { + // Cannot open reference file. + fprintf(stderr, "Cannot open file %s\n", ref_filename); + return -1; + } + FILE* test_fp = fopen(test_filename, "rb"); + if (test_fp == NULL) { + // Cannot open test file. + fprintf(stderr, "Cannot open file %s\n", test_filename); + fclose(ref_fp); + return -2; + } + int frame_number = 0; + + // Read reference and test frames. + for (;;) { + rtc::scoped_refptr ref_i420_buffer( + test::ReadI420Buffer(width, height, ref_fp)); + if (!ref_i420_buffer) + break; + + rtc::scoped_refptr test_i420_buffer( + test::ReadI420Buffer(width, height, test_fp)); + + if (!test_i420_buffer) + break; + + switch (video_metrics_type) { + case kPSNR: + CalculateFrame(kPSNR, *ref_i420_buffer, *test_i420_buffer, frame_number, + psnr_result); + break; + case kSSIM: + CalculateFrame(kSSIM, *ref_i420_buffer, *test_i420_buffer, frame_number, + ssim_result); + break; + case kBoth: + CalculateFrame(kPSNR, *ref_i420_buffer, *test_i420_buffer, frame_number, + psnr_result); + CalculateFrame(kSSIM, *ref_i420_buffer, *test_i420_buffer, frame_number, + ssim_result); + break; + } + frame_number++; + } + int return_code = 0; + if (frame_number == 0) { + fprintf(stderr, "Tried to measure video metrics from empty files " + "(reference file: %s test file: %s)\n", ref_filename, + test_filename); + return_code = -3; + } else { + CalculateStats(psnr_result); + CalculateStats(ssim_result); + } + fclose(ref_fp); + fclose(test_fp); + return return_code; +} + +int I420MetricsFromFiles(const char* ref_filename, + const char* test_filename, + int width, + int height, + QualityMetricsResult* psnr_result, + QualityMetricsResult* ssim_result) { + assert(psnr_result != NULL); + assert(ssim_result != NULL); + return CalculateMetrics(kBoth, ref_filename, test_filename, width, height, + psnr_result, ssim_result); +} + +int I420PSNRFromFiles(const char* ref_filename, + const char* test_filename, + int width, + int height, + QualityMetricsResult* result) { + assert(result != NULL); + return CalculateMetrics(kPSNR, ref_filename, test_filename, width, height, + result, NULL); +} + +int I420SSIMFromFiles(const char* ref_filename, + const char* test_filename, + int width, + int height, + QualityMetricsResult* result) { + assert(result != NULL); + return CalculateMetrics(kSSIM, ref_filename, test_filename, width, height, + NULL, result); +} + +} // namespace test +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/test/testsupport/metrics/video_metrics.h b/third_party/libwebrtc/webrtc/test/testsupport/metrics/video_metrics.h new file mode 100644 index 0000000000..f72ea9b7bb --- /dev/null +++ b/third_party/libwebrtc/webrtc/test/testsupport/metrics/video_metrics.h @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2011 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 TESTSUPPORT_METRICS_VIDEO_METRICS_H_ +#define TESTSUPPORT_METRICS_VIDEO_METRICS_H_ + +#include +#include + +namespace webrtc { +namespace test { + +// The highest PSNR value our algorithms will return. +extern double kMetricsPerfectPSNR; + +// Contains video quality metrics result for a single frame. +struct FrameResult { + int frame_number; + double value; +}; + +// Result from a PSNR/SSIM calculation operation. +// The frames in this data structure are 0-indexed. +struct QualityMetricsResult { + QualityMetricsResult() : + average(0.0), + min(std::numeric_limits::max()), + max(std::numeric_limits::min()), + min_frame_number(-1), + max_frame_number(-1) + {}; + double average; + double min; + double max; + int min_frame_number; + int max_frame_number; + std::vector frames; +}; + +// Calculates PSNR and SSIM values for the reference and test video files +// (must be in I420 format). All calculated values are filled into the +// QualityMetricsResult structs. +// +// PSNR values have the unit decibel (dB) where a high value means the test file +// is similar to the reference file. The higher value, the more similar. The +// maximum PSNR value is kMetricsInfinitePSNR. For more info about PSNR, see +// http://en.wikipedia.org/wiki/PSNR. +// +// SSIM values range between -1.0 and 1.0, where 1.0 means the files are +// identical. For more info about SSIM, see http://en.wikipedia.org/wiki/SSIM +// This function only compares video frames up to the point when the shortest +// video ends. +// Return value: +// 0 if successful, negative on errors: +// -1 if the source file cannot be opened +// -2 if the test file cannot be opened +// -3 if any of the files are empty +// -4 if any arguments are invalid. +int I420MetricsFromFiles(const char* ref_filename, + const char* test_filename, + int width, + int height, + QualityMetricsResult* psnr_result, + QualityMetricsResult* ssim_result); + +// Calculates PSNR values for the reference and test video files (must be in +// I420 format). All calculated values are filled into the QualityMetricsResult +// struct. +// +// PSNR values have the unit decibel (dB) where a high value means the test file +// is similar to the reference file. The higher value, the more similar. The +// maximum PSNR value is kMetricsInfinitePSNR. For more info about PSNR, see +// http://en.wikipedia.org/wiki/PSNR. +// +// This function only compares video frames up to the point when the shortest +// video ends. +// +// Return value: +// 0 if successful, negative on errors: +// -1 if the source file cannot be opened +// -2 if the test file cannot be opened +// -3 if any of the files are empty +// -4 if any arguments are invalid. +int I420PSNRFromFiles(const char* ref_filename, + const char* test_filename, + int width, + int height, + QualityMetricsResult* result); + +// Calculates SSIM values for the reference and test video files (must be in +// I420 format). All calculated values are filled into the QualityMetricsResult +// struct. +// SSIM values range between -1.0 and 1.0, where 1.0 means the files are +// identical. +// This function only compares video frames up to the point when the shortest +// video ends. +// For more info about SSIM, see http://en.wikipedia.org/wiki/SSIM +// +// Return value: +// 0 if successful, negative on errors: +// -1 if the source file cannot be opened +// -2 if the test file cannot be opened +// -3 if any of the files are empty +// -4 if any arguments are invalid. +int I420SSIMFromFiles(const char* ref_filename, + const char* test_filename, + int width, + int height, + QualityMetricsResult* result); + +} // namespace test +} // namespace webrtc + +#endif // TESTSUPPORT_METRICS_VIDEO_METRICS_H_ diff --git a/third_party/libwebrtc/webrtc/test/testsupport/metrics/video_metrics_unittest.cc b/third_party/libwebrtc/webrtc/test/testsupport/metrics/video_metrics_unittest.cc new file mode 100644 index 0000000000..5b00458761 --- /dev/null +++ b/third_party/libwebrtc/webrtc/test/testsupport/metrics/video_metrics_unittest.cc @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2011 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. + */ + +#include "test/testsupport/metrics/video_metrics.h" + +#include "test/gtest.h" +#include "test/testsupport/fileutils.h" + +namespace webrtc { + +static const int kWidth = 352; +static const int kHeight = 288; + +static const int kMissingReferenceFileReturnCode = -1; +static const int kMissingTestFileReturnCode = -2; +static const int kEmptyFileReturnCode = -3; +static const double kPsnrPerfectResult = 48.0; +static const double kSsimPerfectResult = 1.0; + +class VideoMetricsTest: public testing::Test { + protected: + VideoMetricsTest() { + video_file_ = webrtc::test::ResourcePath("foreman_cif_short", "yuv"); + } + virtual ~VideoMetricsTest() {} + void SetUp() { + non_existing_file_ = webrtc::test::OutputPath() + + "video_metrics_unittest_non_existing"; + remove(non_existing_file_.c_str()); // To be sure it doesn't exist. + + // Create an empty file: + empty_file_ = webrtc::test::TempFilename( + webrtc::test::OutputPath(), "video_metrics_unittest_empty_file"); + FILE* dummy = fopen(empty_file_.c_str(), "wb"); + fclose(dummy); + } + void TearDown() { + remove(empty_file_.c_str()); + } + webrtc::test::QualityMetricsResult psnr_result_; + webrtc::test::QualityMetricsResult ssim_result_; + std::string non_existing_file_; + std::string empty_file_; + std::string video_file_; +}; + +// Tests that it is possible to run with the same reference as test file +TEST_F(VideoMetricsTest, ReturnsPerfectResultForIdenticalFilesPSNR) { + EXPECT_EQ(0, I420PSNRFromFiles(video_file_.c_str(), video_file_.c_str(), + kWidth, kHeight, &psnr_result_)); + EXPECT_EQ(kPsnrPerfectResult, psnr_result_.average); +} + +TEST_F(VideoMetricsTest, ReturnsPerfectResultForIdenticalFilesSSIM) { + EXPECT_EQ(0, I420SSIMFromFiles(video_file_.c_str(), video_file_.c_str(), + kWidth, kHeight, &ssim_result_)); + EXPECT_EQ(kSsimPerfectResult, ssim_result_.average); +} + +TEST_F(VideoMetricsTest, ReturnsPerfectResultForIdenticalFilesBothMetrics) { + EXPECT_EQ(0, I420MetricsFromFiles(video_file_.c_str(), video_file_.c_str(), + kWidth, kHeight, &psnr_result_, + &ssim_result_)); + EXPECT_EQ(kPsnrPerfectResult, psnr_result_.average); + EXPECT_EQ(kSsimPerfectResult, ssim_result_.average); +} + +// Tests that the right return code is given when the reference file is missing. +TEST_F(VideoMetricsTest, MissingReferenceFilePSNR) { + EXPECT_EQ(kMissingReferenceFileReturnCode, + I420PSNRFromFiles(non_existing_file_.c_str(), + video_file_.c_str(), kWidth, kHeight, + &ssim_result_)); +} + +TEST_F(VideoMetricsTest, MissingReferenceFileSSIM) { + EXPECT_EQ(kMissingReferenceFileReturnCode, + I420SSIMFromFiles(non_existing_file_.c_str(), + video_file_.c_str(), kWidth, kHeight, + &ssim_result_)); +} + +TEST_F(VideoMetricsTest, MissingReferenceFileBothMetrics) { + EXPECT_EQ(kMissingReferenceFileReturnCode, + I420MetricsFromFiles(non_existing_file_.c_str(), + video_file_.c_str(), kWidth, kHeight, + &psnr_result_, &ssim_result_)); +} + +// Tests that the right return code is given when the test file is missing. +TEST_F(VideoMetricsTest, MissingTestFilePSNR) { + EXPECT_EQ(kMissingTestFileReturnCode, + I420PSNRFromFiles(video_file_.c_str(), non_existing_file_.c_str(), + kWidth, kHeight, &ssim_result_)); +} + +TEST_F(VideoMetricsTest, MissingTestFileSSIM) { + EXPECT_EQ(kMissingTestFileReturnCode, + I420SSIMFromFiles(video_file_.c_str(), non_existing_file_.c_str(), + kWidth, kHeight, &ssim_result_)); +} + +TEST_F(VideoMetricsTest, MissingTestFileBothMetrics) { + EXPECT_EQ(kMissingTestFileReturnCode, + I420MetricsFromFiles(video_file_.c_str(), + non_existing_file_.c_str(), kWidth, kHeight, + &psnr_result_, &ssim_result_)); +} + +// Tests that the method can be executed with empty files. +TEST_F(VideoMetricsTest, EmptyFilesPSNR) { + EXPECT_EQ(kEmptyFileReturnCode, + I420PSNRFromFiles(empty_file_.c_str(), video_file_.c_str(), + kWidth, kHeight, &ssim_result_)); + EXPECT_EQ(kEmptyFileReturnCode, + I420PSNRFromFiles(video_file_.c_str(), empty_file_.c_str(), + kWidth, kHeight, &ssim_result_)); +} + +TEST_F(VideoMetricsTest, EmptyFilesSSIM) { + EXPECT_EQ(kEmptyFileReturnCode, + I420SSIMFromFiles(empty_file_.c_str(), video_file_.c_str(), + kWidth, kHeight, &ssim_result_)); + EXPECT_EQ(kEmptyFileReturnCode, + I420SSIMFromFiles(video_file_.c_str(), empty_file_.c_str(), + kWidth, kHeight, &ssim_result_)); +} + +TEST_F(VideoMetricsTest, EmptyFilesBothMetrics) { + EXPECT_EQ(kEmptyFileReturnCode, + I420MetricsFromFiles(empty_file_.c_str(), video_file_.c_str(), + kWidth, kHeight, + &psnr_result_, &ssim_result_)); + EXPECT_EQ(kEmptyFileReturnCode, + I420MetricsFromFiles(video_file_.c_str(), empty_file_.c_str(), + kWidth, kHeight, + &psnr_result_, &ssim_result_)); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/test/testsupport/mock/mock_frame_reader.h b/third_party/libwebrtc/webrtc/test/testsupport/mock/mock_frame_reader.h new file mode 100644 index 0000000000..4ba416fedf --- /dev/null +++ b/third_party/libwebrtc/webrtc/test/testsupport/mock/mock_frame_reader.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2011 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_MOCK_MOCK_FRAME_READER_H_ +#define TEST_TESTSUPPORT_MOCK_MOCK_FRAME_READER_H_ + +#include "test/testsupport/frame_reader.h" + +#include "test/gmock.h" + +namespace webrtc { +namespace test { + +class MockFrameReader : public FrameReader { + public: + MOCK_METHOD0(Init, bool()); + MOCK_METHOD0(ReadFrame, rtc::scoped_refptr()); + MOCK_METHOD0(Close, void()); + MOCK_METHOD0(FrameLength, size_t()); + MOCK_METHOD0(NumberOfFrames, int()); +}; + +} // namespace test +} // namespace webrtc + +#endif // TEST_TESTSUPPORT_MOCK_MOCK_FRAME_READER_H_ diff --git a/third_party/libwebrtc/webrtc/test/testsupport/packet_reader.cc b/third_party/libwebrtc/webrtc/test/testsupport/packet_reader.cc new file mode 100644 index 0000000000..ba52f94e57 --- /dev/null +++ b/third_party/libwebrtc/webrtc/test/testsupport/packet_reader.cc @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2011 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. + */ + +#include "test/testsupport/packet_reader.h" + +#include +#include +#include + +namespace webrtc { +namespace test { + +PacketReader::PacketReader() + : initialized_(false) {} + +PacketReader::~PacketReader() {} + +void PacketReader::InitializeReading(uint8_t* data, + size_t data_length_in_bytes, + size_t packet_size_in_bytes) { + assert(data); + assert(packet_size_in_bytes > 0); + data_ = data; + data_length_ = data_length_in_bytes; + packet_size_ = packet_size_in_bytes; + currentIndex_ = 0; + initialized_ = true; +} + +int PacketReader::NextPacket(uint8_t** packet_pointer) { + if (!initialized_) { + fprintf(stderr, "Attempting to use uninitialized PacketReader!\n"); + return -1; + } + *packet_pointer = data_ + currentIndex_; + size_t old_index = currentIndex_; + currentIndex_ = std::min(currentIndex_ + packet_size_, data_length_); + return static_cast(currentIndex_ - old_index); +} + +} // namespace test +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/test/testsupport/packet_reader.h b/third_party/libwebrtc/webrtc/test/testsupport/packet_reader.h new file mode 100644 index 0000000000..dbdb3da9e9 --- /dev/null +++ b/third_party/libwebrtc/webrtc/test/testsupport/packet_reader.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2011 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_PACKET_READER_H_ +#define TEST_TESTSUPPORT_PACKET_READER_H_ + +#include +#include "typedefs.h" // NOLINT(build/include) + +namespace webrtc { +namespace test { + +// Reads chunks of data to simulate network packets from a byte array. +class PacketReader { + public: + PacketReader(); + virtual ~PacketReader(); + + // Inizializes a new reading operation. Must be done before invoking the + // NextPacket method. + // * data_length_in_bytes is the length of the data byte array. + // 0 length will result in no packets are read. + // * packet_size_in_bytes is the number of bytes to read in each NextPacket + // method call. Must be > 0 + virtual void InitializeReading(uint8_t* data, size_t data_length_in_bytes, + size_t packet_size_in_bytes); + + // Moves the supplied pointer to the beginning of the next packet. + // Returns: + // * The size of the packet ready to read (lower than the packet size for + // the last packet) + // * 0 if there are no more packets to read + // * -1 if InitializeReading has not been called (also prints to stderr). + virtual int NextPacket(uint8_t** packet_pointer); + + private: + uint8_t* data_; + size_t data_length_; + size_t packet_size_; + size_t currentIndex_; + bool initialized_; +}; + +} // namespace test +} // namespace webrtc + +#endif // TEST_TESTSUPPORT_PACKET_READER_H_ diff --git a/third_party/libwebrtc/webrtc/test/testsupport/packet_reader_unittest.cc b/third_party/libwebrtc/webrtc/test/testsupport/packet_reader_unittest.cc new file mode 100644 index 0000000000..3255151d05 --- /dev/null +++ b/third_party/libwebrtc/webrtc/test/testsupport/packet_reader_unittest.cc @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2012 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. + */ + +#include "test/testsupport/packet_reader.h" + +#include "test/gtest.h" +#include "test/testsupport/unittest_utils.h" + +namespace webrtc { +namespace test { + +class PacketReaderTest: public PacketRelatedTest { + protected: + PacketReaderTest() {} + virtual ~PacketReaderTest() {} + void SetUp() { + reader_ = new PacketReader(); + } + void TearDown() { + delete reader_; + } + void VerifyPacketData(size_t expected_length, + int actual_length, + uint8_t* original_data_pointer, + uint8_t* new_data_pointer) { + EXPECT_EQ(static_cast(expected_length), actual_length); + EXPECT_EQ(*original_data_pointer, *new_data_pointer); + EXPECT_EQ(0, memcmp(original_data_pointer, new_data_pointer, + actual_length)); + } + PacketReader* reader_; +}; + +// Test lack of initialization +TEST_F(PacketReaderTest, Uninitialized) { + uint8_t* data_pointer = NULL; + EXPECT_EQ(-1, reader_->NextPacket(&data_pointer)); + EXPECT_EQ(NULL, data_pointer); +} + +TEST_F(PacketReaderTest, InitializeZeroLengthArgument) { + reader_->InitializeReading(packet_data_, 0, kPacketSizeInBytes); + ASSERT_EQ(0, reader_->NextPacket(&packet_data_pointer_)); +} + +// Test with something smaller than one packet +TEST_F(PacketReaderTest, NormalSmallData) { + const int kDataLengthInBytes = 1499; + uint8_t data[kDataLengthInBytes]; + uint8_t* data_pointer = data; + memset(data, 1, kDataLengthInBytes); + + reader_->InitializeReading(data, kDataLengthInBytes, kPacketSizeInBytes); + int length_to_read = reader_->NextPacket(&data_pointer); + VerifyPacketData(kDataLengthInBytes, length_to_read, data, data_pointer); + EXPECT_EQ(0, data_pointer - data); // pointer hasn't moved + + // Reading another one shall result in 0 bytes: + length_to_read = reader_->NextPacket(&data_pointer); + EXPECT_EQ(0, length_to_read); + EXPECT_EQ(kDataLengthInBytes, data_pointer - data); +} + +// Test with data length that exactly matches one packet +TEST_F(PacketReaderTest, NormalOnePacketData) { + uint8_t data[kPacketSizeInBytes]; + uint8_t* data_pointer = data; + memset(data, 1, kPacketSizeInBytes); + + reader_->InitializeReading(data, kPacketSizeInBytes, kPacketSizeInBytes); + int length_to_read = reader_->NextPacket(&data_pointer); + VerifyPacketData(kPacketSizeInBytes, length_to_read, data, data_pointer); + EXPECT_EQ(0, data_pointer - data); // pointer hasn't moved + + // Reading another one shall result in 0 bytes: + length_to_read = reader_->NextPacket(&data_pointer); + EXPECT_EQ(0, length_to_read); + EXPECT_EQ(kPacketSizeInBytes, static_cast(data_pointer - data)); +} + +// Test with data length that will result in 3 packets +TEST_F(PacketReaderTest, NormalLargeData) { + reader_->InitializeReading(packet_data_, kPacketDataLength, + kPacketSizeInBytes); + + int length_to_read = reader_->NextPacket(&packet_data_pointer_); + VerifyPacketData(kPacketSizeInBytes, length_to_read, + packet1_, packet_data_pointer_); + + length_to_read = reader_->NextPacket(&packet_data_pointer_); + VerifyPacketData(kPacketSizeInBytes, length_to_read, + packet2_, packet_data_pointer_); + + length_to_read = reader_->NextPacket(&packet_data_pointer_); + VerifyPacketData(1u, length_to_read, + packet3_, packet_data_pointer_); + + // Reading another one shall result in 0 bytes: + length_to_read = reader_->NextPacket(&packet_data_pointer_); + EXPECT_EQ(0, length_to_read); + EXPECT_EQ(kPacketDataLength, + static_cast(packet_data_pointer_ - packet_data_)); +} + +// Test with empty data. +TEST_F(PacketReaderTest, EmptyData) { + const int kDataLengthInBytes = 0; + // But don't really try to allocate a zero-length array... + uint8_t data[kPacketSizeInBytes]; + uint8_t* data_pointer = data; + reader_->InitializeReading(data, kDataLengthInBytes, kPacketSizeInBytes); + EXPECT_EQ(kDataLengthInBytes, reader_->NextPacket(&data_pointer)); + // Do it again to make sure nothing changes + EXPECT_EQ(kDataLengthInBytes, reader_->NextPacket(&data_pointer)); +} + +} // namespace test +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/test/testsupport/perf_test.cc b/third_party/libwebrtc/webrtc/test/testsupport/perf_test.cc new file mode 100644 index 0000000000..b7578d101e --- /dev/null +++ b/third_party/libwebrtc/webrtc/test/testsupport/perf_test.cc @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2012 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. + */ + +// A stripped-down version of Chromium's chrome/test/perf/perf_test.cc. +// ResultsToString(), PrintResult(size_t value) and AppendResult(size_t value) +// have been modified. The remainder are identical to the Chromium version. + +#include "test/testsupport/perf_test.h" + +#include +#include +#include + +namespace { + +void PrintResultsImpl(const std::string& graph_name, + const std::string& trace, + const std::string& values, + const std::string& units, + bool important) { + // <*>RESULT : = + // <*>RESULT : = {, } + // <*>RESULT : = [,value,value,...,] + + if (important) { + printf("*"); + } + printf("RESULT %s: %s= %s %s\n", graph_name.c_str(), trace.c_str(), + values.c_str(), units.c_str()); +} + +} // namespace + +namespace webrtc { +namespace test { + +void PrintResult(const std::string& measurement, + const std::string& modifier, + const std::string& trace, + const double value, + const std::string& units, + bool important) { + std::ostringstream value_stream; + value_stream << value; + PrintResultsImpl(measurement + modifier, trace, value_stream.str(), units, + important); +} + +void PrintResultMeanAndError(const std::string& measurement, + const std::string& modifier, + const std::string& trace, + const double mean, + const double error, + const std::string& units, + bool important) { + std::ostringstream value_stream; + value_stream << '{' << mean << ',' << error << '}'; + PrintResultsImpl(measurement + modifier, trace, value_stream.str(), units, + important); +} + +void PrintResultList(const std::string& measurement, + const std::string& modifier, + const std::string& trace, + const std::vector& values, + const std::string& units, + bool important) { + std::ostringstream value_stream; + value_stream << '['; + if (!values.empty()) { + auto it = values.begin(); + while (true) { + value_stream << *it; + if (++it == values.end()) + break; + value_stream << ','; + } + } + value_stream << ']'; + PrintResultsImpl(measurement + modifier, trace, value_stream.str(), units, + important); +} + +} // namespace test +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/test/testsupport/perf_test.h b/third_party/libwebrtc/webrtc/test/testsupport/perf_test.h new file mode 100644 index 0000000000..31b7b1345c --- /dev/null +++ b/third_party/libwebrtc/webrtc/test/testsupport/perf_test.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2012 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. + */ + +// A stripped-down version of Chromium's chrome/test/perf/perf_test.h. +// Several functions have been removed; the prototypes of the remainder have +// not been changed. + +#ifndef TEST_TESTSUPPORT_PERF_TEST_H_ +#define TEST_TESTSUPPORT_PERF_TEST_H_ + +#include +#include +#include + +namespace webrtc { +namespace test { + +// Prints numerical information to stdout in a controlled format, for +// post-processing. |measurement| is a description of the quantity being +// measured, e.g. "vm_peak"; |modifier| is provided as a convenience and +// will be appended directly to the name of the |measurement|, e.g. +// "_browser"; |trace| is a description of the particular data point, e.g. +// "reference"; |value| is the measured value; and |units| is a description +// of the units of measure, e.g. "bytes". If |important| is true, the output +// line will be specially marked, to notify the post-processor. The strings +// may be empty. They should not contain any colons (:) or equals signs (=). +// A typical post-processing step would be to produce graphs of the data +// produced for various builds, using the combined |measurement| + |modifier| +// string to specify a particular graph and the |trace| to identify a trace +// (i.e., data series) on that graph. +void PrintResult(const std::string& measurement, + const std::string& modifier, + const std::string& trace, + const double value, + const std::string& units, + bool important); + +// Like PrintResult(), but prints a (mean, standard deviation) result pair. +// The || should be two comma-separated numbers, the mean and +// standard deviation (or other error metric) of the measurement. +void PrintResultMeanAndError(const std::string& measurement, + const std::string& modifier, + const std::string& trace, + const double mean, + const double error, + const std::string& units, + bool important); + + +// Like PrintResult(), but prints an entire list of results. The |values| +// will generally be a list of comma-separated numbers. A typical +// post-processing step might produce plots of their mean and standard +// deviation. +void PrintResultList(const std::string& measurement, + const std::string& modifier, + const std::string& trace, + const std::vector& values, + const std::string& units, + bool important); + +} // namespace test +} // namespace webrtc + +#endif // TEST_TESTSUPPORT_PERF_TEST_H_ diff --git a/third_party/libwebrtc/webrtc/test/testsupport/perf_test_unittest.cc b/third_party/libwebrtc/webrtc/test/testsupport/perf_test_unittest.cc new file mode 100644 index 0000000000..466e2b3e69 --- /dev/null +++ b/third_party/libwebrtc/webrtc/test/testsupport/perf_test_unittest.cc @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2012 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. + */ + +#include "test/testsupport/perf_test.h" + +#include + +#include "test/gtest.h" + +namespace webrtc { +namespace test { + +TEST(PerfTest, AppendResult) { + testing::internal::CaptureStdout(); + std::string expected = "RESULT measurementmodifier: trace= 42 units\n"; + PrintResult("measurement", "modifier", "trace", 42, "units", false); + + expected += "*RESULT foobar: baz= 7 widgets\n"; + PrintResult("foo", "bar", "baz", 7, "widgets", true); + + expected += "RESULT foobar: baz= {1,2} lemurs\n"; + PrintResultMeanAndError("foo", "bar", "baz", 1, 2, "lemurs", false); + + expected += "RESULT foobar: baz= [1,2,3] units\n"; + PrintResultList("foo", "bar", "baz", {1, 2, 3}, "units", false); + + std::string output = testing::internal::GetCapturedStdout(); + EXPECT_EQ(expected, output); +} + +} // namespace test +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/test/testsupport/test_artifacts.cc b/third_party/libwebrtc/webrtc/test/testsupport/test_artifacts.cc new file mode 100644 index 0000000000..3d97a94472 --- /dev/null +++ b/third_party/libwebrtc/webrtc/test/testsupport/test_artifacts.cc @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2016 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. + */ + +#include "test/testsupport/test_artifacts.h" + +#include + +#include "rtc_base/file.h" +#include "rtc_base/flags.h" +#include "rtc_base/logging.h" +#include "rtc_base/pathutils.h" +#include "test/testsupport/fileutils.h" + +namespace { +const std::string& DefaultArtifactPath() { + static const std::string path = webrtc::test::OutputPath(); + return path; +} +} + +DEFINE_string(test_artifacts_dir, + DefaultArtifactPath().c_str(), + "The output folder where test output should be saved."); + +namespace webrtc { +namespace test { + +bool GetTestArtifactsDir(std::string* out_dir) { + if (strlen(FLAG_test_artifacts_dir) == 0) { + RTC_LOG(LS_WARNING) << "No test_out_dir defined."; + return false; + } + *out_dir = FLAG_test_artifacts_dir; + return true; +} + +bool WriteToTestArtifactsDir(const char* filename, + const uint8_t* buffer, + size_t length) { + if (strlen(FLAG_test_artifacts_dir) == 0) { + RTC_LOG(LS_WARNING) << "No test_out_dir defined."; + return false; + } + + if (filename == nullptr || strlen(filename) == 0) { + RTC_LOG(LS_WARNING) << "filename must be provided."; + return false; + } + + rtc::File output = + rtc::File::Create(rtc::Pathname(FLAG_test_artifacts_dir, filename)); + + return output.IsOpen() && output.Write(buffer, length) == length; +} + +bool WriteToTestArtifactsDir(const char* filename, const std::string& content) { + return WriteToTestArtifactsDir( + filename, reinterpret_cast(content.c_str()), + content.length()); +} + +} // namespace test +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/test/testsupport/test_artifacts.h b/third_party/libwebrtc/webrtc/test/testsupport/test_artifacts.h new file mode 100644 index 0000000000..ee07a9be52 --- /dev/null +++ b/third_party/libwebrtc/webrtc/test/testsupport/test_artifacts.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2016 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_TEST_ARTIFACTS_H_ +#define TEST_TESTSUPPORT_TEST_ARTIFACTS_H_ + +#include + +#include + +namespace webrtc { +namespace test { + +// If the test_artifacts_dir flag is set, returns true and copies the location +// of the dir to |out_dir|. Otherwise, return false. +bool GetTestArtifactsDir(std::string* out_dir); + +// Writes a |length| bytes array |buffer| to |filename| in isolated output +// directory defined by swarming. If the file is existing, content will be +// appended. Otherwise a new file will be created. This function returns false +// if isolated output directory has not been defined, or |filename| indicates an +// invalid or non-writable file, or underlying file system errors. +bool WriteToTestArtifactsDir(const char* filename, + const uint8_t* buffer, + size_t length); + +bool WriteToTestArtifactsDir(const char* filename, const std::string& content); + +} // namespace test +} // namespace webrtc + +#endif // TEST_TESTSUPPORT_TEST_ARTIFACTS_H_ diff --git a/third_party/libwebrtc/webrtc/test/testsupport/test_artifacts_unittest.cc b/third_party/libwebrtc/webrtc/test/testsupport/test_artifacts_unittest.cc new file mode 100644 index 0000000000..251c5cde88 --- /dev/null +++ b/third_party/libwebrtc/webrtc/test/testsupport/test_artifacts_unittest.cc @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2016 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. + */ + +#include "test/testsupport/test_artifacts.h" + +#include + +#include + +#include "rtc_base/file.h" +#include "rtc_base/flags.h" +#include "rtc_base/pathutils.h" +#include "rtc_base/platform_file.h" +#include "test/gtest.h" + +DECLARE_string(test_artifacts_dir); + +namespace webrtc { +namespace test { + +TEST(IsolatedOutputTest, ShouldRejectInvalidIsolatedOutDir) { + const char* backup = FLAG_test_artifacts_dir; + FLAG_test_artifacts_dir = ""; + ASSERT_FALSE(WriteToTestArtifactsDir("a-file", "some-contents")); + FLAG_test_artifacts_dir = backup; +} + +TEST(IsolatedOutputTest, ShouldRejectInvalidFileName) { + ASSERT_FALSE(WriteToTestArtifactsDir(nullptr, "some-contents")); + ASSERT_FALSE(WriteToTestArtifactsDir("", "some-contents")); +} + +// Sets isolated_out_dir= to execute this test. +TEST(IsolatedOutputTest, ShouldBeAbleToWriteContent) { + const char* filename = "a-file"; + const char* content = "some-contents"; + if (WriteToTestArtifactsDir(filename, content)) { + rtc::Pathname out_file(FLAG_test_artifacts_dir, filename); + rtc::File input = rtc::File::Open(out_file); + EXPECT_TRUE(input.IsOpen()); + EXPECT_TRUE(input.Seek(0)); + uint8_t buffer[32]; + EXPECT_EQ(input.Read(buffer, strlen(content)), strlen(content)); + buffer[strlen(content)] = 0; + EXPECT_EQ(std::string(content), + std::string(reinterpret_cast(buffer))); + input.Close(); + + EXPECT_TRUE(rtc::File::Remove(out_file)); + } +} + +} // namespace test +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/test/testsupport/unittest_utils.h b/third_party/libwebrtc/webrtc/test/testsupport/unittest_utils.h new file mode 100644 index 0000000000..5fee2ca454 --- /dev/null +++ b/third_party/libwebrtc/webrtc/test/testsupport/unittest_utils.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2012 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_UNITTEST_UTILS_H_ +#define TEST_TESTSUPPORT_UNITTEST_UTILS_H_ + +namespace webrtc { +namespace test { + +const size_t kPacketSizeInBytes = 1500; +const size_t kPacketDataLength = kPacketSizeInBytes * 2 + 1; +const int kPacketDataNumberOfPackets = 3; + +// A base test fixture for packet related tests. Contains +// two full prepared packets with 1s, 2s in their data and a third packet with +// a single 3 in it (size=1). +// A packet data structure is also available, that contains these three packets +// in order. +class PacketRelatedTest: public testing::Test { + protected: + // Tree packet byte arrays with data used for verification: + uint8_t packet1_[kPacketSizeInBytes]; + uint8_t packet2_[kPacketSizeInBytes]; + uint8_t packet3_[1]; + // Construct a data structure containing these packets + uint8_t packet_data_[kPacketDataLength]; + uint8_t* packet_data_pointer_; + + PacketRelatedTest() { + packet_data_pointer_ = packet_data_; + + memset(packet1_, 1, kPacketSizeInBytes); + memset(packet2_, 2, kPacketSizeInBytes); + memset(packet3_, 3, 1); + // Fill the packet_data: + memcpy(packet_data_pointer_, packet1_, kPacketSizeInBytes); + memcpy(packet_data_pointer_ + kPacketSizeInBytes, packet2_, + kPacketSizeInBytes); + memcpy(packet_data_pointer_ + kPacketSizeInBytes * 2, packet3_, 1); + } + virtual ~PacketRelatedTest() {} + void SetUp() {} + void TearDown() {} +}; + +} // namespace test +} // namespace webrtc + +#endif // TEST_TESTSUPPORT_UNITTEST_UTILS_H_ diff --git a/third_party/libwebrtc/webrtc/test/testsupport/y4m_frame_writer.cc b/third_party/libwebrtc/webrtc/test/testsupport/y4m_frame_writer.cc new file mode 100644 index 0000000000..e0c1ed54ba --- /dev/null +++ b/third_party/libwebrtc/webrtc/test/testsupport/y4m_frame_writer.cc @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2017 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. + */ + +#include "rtc_base/checks.h" +#include "test/testsupport/frame_writer.h" + +namespace webrtc { +namespace test { + +Y4mFrameWriterImpl::Y4mFrameWriterImpl(std::string output_filename, + int width, + int height, + int frame_rate) + : YuvFrameWriterImpl(output_filename, width, height), + frame_rate_(frame_rate) {} + +Y4mFrameWriterImpl::~Y4mFrameWriterImpl() = default; + +bool Y4mFrameWriterImpl::Init() { + if (!YuvFrameWriterImpl::Init()) { + return false; + } + int bytes_written = fprintf(output_file_, "YUV4MPEG2 W%d H%d F%d:1 C420\n", + width_, height_, frame_rate_); + if (bytes_written < 0) { + fprintf(stderr, "Failed to write Y4M file header to file %s\n", + output_filename_.c_str()); + return false; + } + return true; +} + +bool Y4mFrameWriterImpl::WriteFrame(uint8_t* frame_buffer) { + if (output_file_ == nullptr) { + fprintf(stderr, + "Y4mFrameWriterImpl is not initialized (output file is NULL)\n"); + return false; + } + int bytes_written = fprintf(output_file_, "FRAME\n"); + if (bytes_written < 0) { + fprintf(stderr, "Failed to write Y4M frame header to file %s\n", + output_filename_.c_str()); + return false; + } + return YuvFrameWriterImpl::WriteFrame(frame_buffer); +} + +} // namespace test +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/test/testsupport/y4m_frame_writer_unittest.cc b/third_party/libwebrtc/webrtc/test/testsupport/y4m_frame_writer_unittest.cc new file mode 100644 index 0000000000..d9ec09a2b7 --- /dev/null +++ b/third_party/libwebrtc/webrtc/test/testsupport/y4m_frame_writer_unittest.cc @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2017 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. + */ + +#include +#include + +#include "test/gtest.h" +#include "test/testsupport/fileutils.h" +#include "test/testsupport/frame_writer.h" + +namespace webrtc { +namespace test { + +namespace { +const size_t kFrameWidth = 50; +const size_t kFrameHeight = 20; +const size_t kFrameLength = 3 * kFrameWidth * kFrameHeight / 2; // I420. +const size_t kFrameRate = 30; + +const std::string kFileHeader = "YUV4MPEG2 W50 H20 F30:1 C420\n"; +const std::string kFrameHeader = "FRAME\n"; +} // namespace + +class Y4mFrameWriterTest : public testing::Test { + protected: + Y4mFrameWriterTest() = default; + ~Y4mFrameWriterTest() override = default; + + void SetUp() override { + temp_filename_ = webrtc::test::TempFilename(webrtc::test::OutputPath(), + "y4m_frame_writer_unittest"); + frame_writer_.reset(new Y4mFrameWriterImpl(temp_filename_, kFrameWidth, + kFrameHeight, kFrameRate)); + ASSERT_TRUE(frame_writer_->Init()); + } + + void TearDown() override { remove(temp_filename_.c_str()); } + + std::unique_ptr frame_writer_; + std::string temp_filename_; +}; + +TEST_F(Y4mFrameWriterTest, InitSuccess) {} + +TEST_F(Y4mFrameWriterTest, FrameLength) { + EXPECT_EQ(kFrameLength, frame_writer_->FrameLength()); +} + +TEST_F(Y4mFrameWriterTest, WriteFrame) { + uint8_t buffer[kFrameLength]; + memset(buffer, 9, kFrameLength); // Write lots of 9s to the buffer. + bool result = frame_writer_->WriteFrame(buffer); + ASSERT_TRUE(result); + result = frame_writer_->WriteFrame(buffer); + ASSERT_TRUE(result); + + frame_writer_->Close(); + EXPECT_EQ(kFileHeader.size() + 2 * kFrameHeader.size() + 2 * kFrameLength, + GetFileSize(temp_filename_)); +} + +TEST_F(Y4mFrameWriterTest, WriteFrameUninitialized) { + uint8_t buffer[kFrameLength]; + Y4mFrameWriterImpl frame_writer(temp_filename_, kFrameWidth, kFrameHeight, + kFrameRate); + EXPECT_FALSE(frame_writer.WriteFrame(buffer)); +} + +} // namespace test +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/test/testsupport/yuv_frame_reader.cc b/third_party/libwebrtc/webrtc/test/testsupport/yuv_frame_reader.cc new file mode 100644 index 0000000000..60f9994b14 --- /dev/null +++ b/third_party/libwebrtc/webrtc/test/testsupport/yuv_frame_reader.cc @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2017 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. + */ + +#include "test/testsupport/frame_reader.h" + +#include "api/video/i420_buffer.h" +#include "test/frame_utils.h" +#include "test/testsupport/fileutils.h" + +namespace webrtc { +namespace test { + +YuvFrameReaderImpl::YuvFrameReaderImpl(std::string input_filename, + int width, + int height) + : input_filename_(input_filename), + frame_length_in_bytes_(0), + width_(width), + height_(height), + number_of_frames_(-1), + input_file_(nullptr) {} + +YuvFrameReaderImpl::~YuvFrameReaderImpl() { + Close(); +} + +bool YuvFrameReaderImpl::Init() { + if (width_ <= 0 || height_ <= 0) { + fprintf(stderr, "Frame width and height must be >0, was %d x %d\n", width_, + height_); + return false; + } + frame_length_in_bytes_ = + width_ * height_ + 2 * ((width_ + 1) / 2) * ((height_ + 1) / 2); + + input_file_ = fopen(input_filename_.c_str(), "rb"); + if (input_file_ == nullptr) { + fprintf(stderr, "Couldn't open input file for reading: %s\n", + input_filename_.c_str()); + return false; + } + // Calculate total number of frames. + size_t source_file_size = GetFileSize(input_filename_); + if (source_file_size <= 0u) { + fprintf(stderr, "Found empty file: %s\n", input_filename_.c_str()); + return false; + } + number_of_frames_ = + static_cast(source_file_size / frame_length_in_bytes_); + return true; +} + +rtc::scoped_refptr YuvFrameReaderImpl::ReadFrame() { + if (input_file_ == nullptr) { + fprintf(stderr, + "YuvFrameReaderImpl is not initialized (input file is NULL)\n"); + return nullptr; + } + rtc::scoped_refptr buffer( + ReadI420Buffer(width_, height_, input_file_)); + if (!buffer && ferror(input_file_)) { + fprintf(stderr, "Error reading from input file: %s\n", + input_filename_.c_str()); + } + return buffer; +} + +void YuvFrameReaderImpl::Close() { + if (input_file_ != nullptr) { + fclose(input_file_); + input_file_ = nullptr; + } +} + +size_t YuvFrameReaderImpl::FrameLength() { + return frame_length_in_bytes_; +} + +int YuvFrameReaderImpl::NumberOfFrames() { + return number_of_frames_; +} + +} // namespace test +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/test/testsupport/yuv_frame_reader_unittest.cc b/third_party/libwebrtc/webrtc/test/testsupport/yuv_frame_reader_unittest.cc new file mode 100644 index 0000000000..dd4b980c71 --- /dev/null +++ b/third_party/libwebrtc/webrtc/test/testsupport/yuv_frame_reader_unittest.cc @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2017 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. + */ + +#include +#include + +#include "api/video/i420_buffer.h" +#include "test/gtest.h" +#include "test/testsupport/fileutils.h" +#include "test/testsupport/frame_reader.h" + +namespace webrtc { +namespace test { + +namespace { +const std::string kInputFileContents = "bazouk"; + +const size_t kFrameWidth = 2; +const size_t kFrameHeight = 2; +const size_t kFrameLength = 3 * kFrameWidth * kFrameHeight / 2; // I420. +} // namespace + +class YuvFrameReaderTest : public testing::Test { + protected: + YuvFrameReaderTest() = default; + ~YuvFrameReaderTest() override = default; + + void SetUp() override { + temp_filename_ = webrtc::test::TempFilename(webrtc::test::OutputPath(), + "yuv_frame_reader_unittest"); + FILE* dummy = fopen(temp_filename_.c_str(), "wb"); + fprintf(dummy, "%s", kInputFileContents.c_str()); + fclose(dummy); + + frame_reader_.reset( + new YuvFrameReaderImpl(temp_filename_, kFrameWidth, kFrameHeight)); + ASSERT_TRUE(frame_reader_->Init()); + } + + void TearDown() override { remove(temp_filename_.c_str()); } + + std::unique_ptr frame_reader_; + std::string temp_filename_; +}; + +TEST_F(YuvFrameReaderTest, InitSuccess) {} + +TEST_F(YuvFrameReaderTest, FrameLength) { + EXPECT_EQ(kFrameLength, frame_reader_->FrameLength()); +} + +TEST_F(YuvFrameReaderTest, NumberOfFrames) { + EXPECT_EQ(1, frame_reader_->NumberOfFrames()); +} + +TEST_F(YuvFrameReaderTest, ReadFrame) { + rtc::scoped_refptr buffer = frame_reader_->ReadFrame(); + ASSERT_TRUE(buffer); + // Expect I420 packed as YUV. + EXPECT_EQ(kInputFileContents[0], buffer->DataY()[0]); + EXPECT_EQ(kInputFileContents[1], buffer->DataY()[1]); + EXPECT_EQ(kInputFileContents[2], buffer->DataY()[2]); + EXPECT_EQ(kInputFileContents[3], buffer->DataY()[3]); + EXPECT_EQ(kInputFileContents[4], buffer->DataU()[0]); + EXPECT_EQ(kInputFileContents[5], buffer->DataV()[0]); + EXPECT_FALSE(frame_reader_->ReadFrame()); // End of file. +} + +TEST_F(YuvFrameReaderTest, ReadFrameUninitialized) { + YuvFrameReaderImpl file_reader(temp_filename_, kFrameWidth, kFrameHeight); + EXPECT_FALSE(file_reader.ReadFrame()); +} + +} // namespace test +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/test/testsupport/yuv_frame_writer.cc b/third_party/libwebrtc/webrtc/test/testsupport/yuv_frame_writer.cc new file mode 100644 index 0000000000..1bd343c513 --- /dev/null +++ b/third_party/libwebrtc/webrtc/test/testsupport/yuv_frame_writer.cc @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2017 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. + */ + +#include "rtc_base/checks.h" +#include "test/testsupport/frame_writer.h" + +namespace webrtc { +namespace test { + +YuvFrameWriterImpl::YuvFrameWriterImpl(std::string output_filename, + int width, + int height) + : output_filename_(output_filename), + frame_length_in_bytes_(0), + width_(width), + height_(height), + output_file_(nullptr) {} + +YuvFrameWriterImpl::~YuvFrameWriterImpl() { + Close(); +} + +bool YuvFrameWriterImpl::Init() { + if (width_ <= 0 || height_ <= 0) { + fprintf(stderr, "Frame width and height must be >0, was %d x %d\n", width_, + height_); + return false; + } + frame_length_in_bytes_ = + width_ * height_ + 2 * ((width_ + 1) / 2) * ((height_ + 1) / 2); + + output_file_ = fopen(output_filename_.c_str(), "wb"); + if (output_file_ == nullptr) { + fprintf(stderr, "Couldn't open output file for writing: %s\n", + output_filename_.c_str()); + return false; + } + return true; +} + +bool YuvFrameWriterImpl::WriteFrame(uint8_t* frame_buffer) { + RTC_DCHECK(frame_buffer); + if (output_file_ == nullptr) { + fprintf(stderr, + "YuvFrameWriterImpl is not initialized (output file is NULL)\n"); + return false; + } + size_t bytes_written = + fwrite(frame_buffer, 1, frame_length_in_bytes_, output_file_); + if (bytes_written != frame_length_in_bytes_) { + fprintf(stderr, "Failed to write %zu bytes to file %s\n", + frame_length_in_bytes_, output_filename_.c_str()); + return false; + } + return true; +} + +void YuvFrameWriterImpl::Close() { + if (output_file_ != nullptr) { + fclose(output_file_); + output_file_ = nullptr; + } +} + +size_t YuvFrameWriterImpl::FrameLength() { + return frame_length_in_bytes_; +} + +} // namespace test +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/test/testsupport/yuv_frame_writer_unittest.cc b/third_party/libwebrtc/webrtc/test/testsupport/yuv_frame_writer_unittest.cc new file mode 100644 index 0000000000..49b595e1da --- /dev/null +++ b/third_party/libwebrtc/webrtc/test/testsupport/yuv_frame_writer_unittest.cc @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2017 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. + */ + +#include + +#include "test/gtest.h" +#include "test/testsupport/fileutils.h" +#include "test/testsupport/frame_writer.h" + +namespace webrtc { +namespace test { + +namespace { +const size_t kFrameWidth = 50; +const size_t kFrameHeight = 20; +const size_t kFrameLength = 3 * kFrameWidth * kFrameHeight / 2; // I420. +} // namespace + +class YuvFrameWriterTest : public testing::Test { + protected: + YuvFrameWriterTest() = default; + ~YuvFrameWriterTest() override = default; + + void SetUp() override { + temp_filename_ = webrtc::test::TempFilename(webrtc::test::OutputPath(), + "yuv_frame_writer_unittest"); + frame_writer_.reset( + new YuvFrameWriterImpl(temp_filename_, kFrameWidth, kFrameHeight)); + ASSERT_TRUE(frame_writer_->Init()); + } + + void TearDown() override { remove(temp_filename_.c_str()); } + + std::unique_ptr frame_writer_; + std::string temp_filename_; +}; + +TEST_F(YuvFrameWriterTest, InitSuccess) {} + +TEST_F(YuvFrameWriterTest, FrameLength) { + EXPECT_EQ(kFrameLength, frame_writer_->FrameLength()); +} + +TEST_F(YuvFrameWriterTest, WriteFrame) { + uint8_t buffer[kFrameLength]; + memset(buffer, 9, kFrameLength); // Write lots of 9s to the buffer. + bool result = frame_writer_->WriteFrame(buffer); + ASSERT_TRUE(result); + + frame_writer_->Close(); + EXPECT_EQ(kFrameLength, GetFileSize(temp_filename_)); +} + +TEST_F(YuvFrameWriterTest, WriteFrameUninitialized) { + uint8_t buffer[kFrameLength]; + YuvFrameWriterImpl frame_writer(temp_filename_, kFrameWidth, kFrameHeight); + EXPECT_FALSE(frame_writer.WriteFrame(buffer)); +} + +} // namespace test +} // namespace webrtc -- cgit v1.2.3