From 9c761283176a36fcce0cce900a4cddbac3fd2c82 Mon Sep 17 00:00:00 2001 From: Marshall Greenblatt Date: Tue, 15 Nov 2016 12:56:02 -0500 Subject: [PATCH] Add CefScopedTempDir and file utility functions for unit tests (issue #1632) --- BUILD.gn | 7 + cef_paths.gypi | 2 + cef_paths2.gypi | 2 + include/capi/cef_file_util_capi.h | 118 +++++ include/cef_file_util.h | 118 +++++ include/wrapper/cef_scoped_temp_dir.h | 118 +++++ libcef/common/file_util_impl.cc | 86 ++++ libcef_dll/libcef_dll.cc | 143 ++++++ libcef_dll/wrapper/cef_scoped_temp_dir.cc | 87 ++++ libcef_dll/wrapper/libcef_dll_wrapper.cc | 120 +++++ tests/unittests/cookie_unittest.cc | 46 +- tests/unittests/download_unittest.cc | 46 +- tests/unittests/file_util.cc | 110 +++++ tests/unittests/file_util.h | 39 ++ tests/unittests/file_util_unittest.cc | 47 ++ tests/unittests/request_context_unittest.cc | 6 +- tests/unittests/resource_manager_unittest.cc | 63 +-- tests/unittests/scoped_temp_dir_unittest.cc | 90 ++++ tests/unittests/thread_helper.cc | 9 +- tests/unittests/thread_helper.h | 7 +- tests/unittests/tracing_unittest.cc | 16 +- tests/unittests/urlrequest_unittest.cc | 453 ++++++++++++++----- 22 files changed, 1539 insertions(+), 194 deletions(-) create mode 100644 include/capi/cef_file_util_capi.h create mode 100644 include/cef_file_util.h create mode 100644 include/wrapper/cef_scoped_temp_dir.h create mode 100644 libcef/common/file_util_impl.cc create mode 100644 libcef_dll/wrapper/cef_scoped_temp_dir.cc create mode 100644 tests/unittests/file_util.cc create mode 100644 tests/unittests/file_util.h create mode 100644 tests/unittests/file_util_unittest.cc create mode 100644 tests/unittests/scoped_temp_dir_unittest.cc diff --git a/BUILD.gn b/BUILD.gn index 7ce2df0d5..c5e0fd073 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -429,6 +429,7 @@ static_library("libcef_static") { "libcef/common/extensions/extensions_client.h", "libcef/common/extensions/extensions_util.cc", "libcef/common/extensions/extensions_util.h", + "libcef/common/file_util_impl.cc", "libcef/common/json_impl.cc", "libcef/common/main_delegate.cc", "libcef/common/main_delegate.h", @@ -1373,6 +1374,9 @@ cef_unittests_sources = [ "tests/unittests/dom_unittest.cc", "tests/unittests/download_unittest.cc", "tests/unittests/draggable_regions_unittest.cc", + "tests/unittests/file_util.cc", + "tests/unittests/file_util.h", + "tests/unittests/file_util_unittest.cc", "tests/unittests/frame_unittest.cc", "tests/unittests/geolocation_unittest.cc", "tests/unittests/image_unittest.cc", @@ -1396,6 +1400,7 @@ cef_unittests_sources = [ "tests/unittests/routing_test_handler.h", "tests/unittests/run_all_unittests.cc", "tests/unittests/scheme_handler_unittest.cc", + "tests/unittests/scoped_temp_dir_unittest.cc", "tests/unittests/stream_unittest.cc", "tests/unittests/stream_resource_handler_unittest.cc", "tests/unittests/string_unittest.cc", @@ -1695,6 +1700,8 @@ if (is_mac) { "tests/unittests/client_app_delegates.cc", "tests/unittests/cookie_unittest.cc", "tests/unittests/dom_unittest.cc", + "tests/unittests/file_util.cc", + "tests/unittests/file_util.h", "tests/unittests/frame_unittest.cc", "tests/unittests/message_router_unittest.cc", "tests/unittests/navigation_unittest.cc", diff --git a/cef_paths.gypi b/cef_paths.gypi index 3fe8480be..60ed7f0e3 100644 --- a/cef_paths.gypi +++ b/cef_paths.gypi @@ -28,6 +28,7 @@ 'include/cef_download_item.h', 'include/cef_drag_data.h', 'include/cef_drag_handler.h', + 'include/cef_file_util.h', 'include/cef_find_handler.h', 'include/cef_focus_handler.h', 'include/cef_frame.h', @@ -113,6 +114,7 @@ 'include/capi/cef_download_item_capi.h', 'include/capi/cef_drag_data_capi.h', 'include/capi/cef_drag_handler_capi.h', + 'include/capi/cef_file_util_capi.h', 'include/capi/cef_find_handler_capi.h', 'include/capi/cef_focus_handler_capi.h', 'include/capi/cef_frame_capi.h', diff --git a/cef_paths2.gypi b/cef_paths2.gypi index ce6d26e04..2985226b4 100644 --- a/cef_paths2.gypi +++ b/cef_paths2.gypi @@ -61,6 +61,7 @@ 'include/wrapper/cef_helpers.h', 'include/wrapper/cef_message_router.h', 'include/wrapper/cef_resource_manager.h', + 'include/wrapper/cef_scoped_temp_dir.h', 'include/wrapper/cef_stream_resource_handler.h', 'include/wrapper/cef_xml_object.h', 'include/wrapper/cef_zip_archive.h', @@ -125,6 +126,7 @@ 'libcef_dll/wrapper/cef_closure_task.cc', 'libcef_dll/wrapper/cef_message_router.cc', 'libcef_dll/wrapper/cef_resource_manager.cc', + 'libcef_dll/wrapper/cef_scoped_temp_dir.cc', 'libcef_dll/wrapper/cef_stream_resource_handler.cc', 'libcef_dll/wrapper/cef_xml_object.cc', 'libcef_dll/wrapper/cef_zip_archive.cc', diff --git a/include/capi/cef_file_util_capi.h b/include/capi/cef_file_util_capi.h new file mode 100644 index 000000000..8f9d088fc --- /dev/null +++ b/include/capi/cef_file_util_capi.h @@ -0,0 +1,118 @@ +// Copyright (c) 2016 Marshall A. Greenblatt. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the name Chromium Embedded +// Framework nor the names of its contributors may be used to endorse +// or promote products derived from this software without specific prior +// written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// --------------------------------------------------------------------------- +// +// This file was generated by the CEF translator tool and should not edited +// by hand. See the translator.README.txt file in the tools directory for +// more information. +// + +#ifndef CEF_INCLUDE_CAPI_CEF_FILE_UTIL_CAPI_H_ +#define CEF_INCLUDE_CAPI_CEF_FILE_UTIL_CAPI_H_ +#pragma once + +#include "include/capi/cef_base_capi.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/// +// Creates a directory and all parent directories if they don't already exist. +// Returns true (1) on successful creation or if the directory already exists. +// The directory is only readable by the current user. Calling this function on +// the browser process UI or IO threads is not allowed. +/// +CEF_EXPORT int cef_create_directory(const cef_string_t* full_path); + +/// +// Get the temporary directory provided by the system. +// +// WARNING: In general, you should use the temp directory variants below instead +// of this function. Those variants will ensure that the proper permissions are +// set so that other users on the system can't edit them while they're open +// (which could lead to security issues). +/// +CEF_EXPORT int cef_get_temp_directory(cef_string_t* temp_dir); + +/// +// Creates a new directory. On Windows if |prefix| is provided the new directory +// name is in the format of "prefixyyyy". Returns true (1) on success and sets +// |new_temp_path| to the full path of the directory that was created. The +// directory is only readable by the current user. Calling this function on the +// browser process UI or IO threads is not allowed. +/// +CEF_EXPORT int cef_create_new_temp_directory(const cef_string_t* prefix, + cef_string_t* new_temp_path); + +/// +// Creates a directory within another directory. Extra characters will be +// appended to |prefix| to ensure that the new directory does not have the same +// name as an existing directory. Returns true (1) on success and sets |new_dir| +// to the full path of the directory that was created. The directory is only +// readable by the current user. Calling this function on the browser process UI +// or IO threads is not allowed. +/// +CEF_EXPORT int cef_create_temp_directory_in_directory( + const cef_string_t* base_dir, const cef_string_t* prefix, + cef_string_t* new_dir); + +/// +// Returns true (1) if the given path exists and is a directory. Calling this +// function on the browser process UI or IO threads is not allowed. +/// +CEF_EXPORT int cef_directory_exists(const cef_string_t* path); + +/// +// Deletes the given path whether it's a file or a directory. If |path| is a +// directory all contents will be deleted. If |recursive| is true (1) any sub- +// directories and their contents will also be deleted (equivalent to executing +// "rm -rf", so use with caution). On POSIX environments if |path| is a symbolic +// link then only the symlink will be deleted. Returns true (1) on successful +// deletion or if |path| does not exist. Calling this function on the browser +// process UI or IO threads is not allowed. +/// +CEF_EXPORT int cef_delete_file(const cef_string_t* path, int recursive); + +/// +// Writes the contents of |src_dir| into a zip archive at |dest_file|. If +// |include_hidden_files| is true (1) files starting with "." will be included. +// Returns true (1) on success. Calling this function on the browser process UI +// or IO threads is not allowed. +/// +CEF_EXPORT int cef_zip_directory(const cef_string_t* src_dir, + const cef_string_t* dest_file, int include_hidden_files); + +#ifdef __cplusplus +} +#endif + +#endif // CEF_INCLUDE_CAPI_CEF_FILE_UTIL_CAPI_H_ diff --git a/include/cef_file_util.h b/include/cef_file_util.h new file mode 100644 index 000000000..d18f886ca --- /dev/null +++ b/include/cef_file_util.h @@ -0,0 +1,118 @@ +// Copyright (c) 2016 Marshall A. Greenblatt. Portions copyright (c) 2012 +// Google Inc. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the name Chromium Embedded +// Framework nor the names of its contributors may be used to endorse +// or promote products derived from this software without specific prior +// written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// --------------------------------------------------------------------------- +// +// The contents of this file must follow a specific format in order to +// support the CEF translator tool. See the translator.README.txt file in the +// tools directory for more information. +// + +#ifndef CEF_INCLUDE_CEF_FILE_UTIL_H_ +#define CEF_INCLUDE_CEF_FILE_UTIL_H_ +#pragma once + +#include "include/cef_base.h" + +/// +// Creates a directory and all parent directories if they don't already exist. +// Returns true on successful creation or if the directory already exists. The +// directory is only readable by the current user. Calling this function on the +// browser process UI or IO threads is not allowed. +/// +/*--cef()--*/ +bool CefCreateDirectory(const CefString& full_path); + +/// +// Get the temporary directory provided by the system. +// +// WARNING: In general, you should use the temp directory variants below instead +// of this function. Those variants will ensure that the proper permissions are +// set so that other users on the system can't edit them while they're open +// (which could lead to security issues). +/// +/*--cef()--*/ +bool CefGetTempDirectory(CefString& temp_dir); + +/// +// Creates a new directory. On Windows if |prefix| is provided the new directory +// name is in the format of "prefixyyyy". Returns true on success and sets +// |new_temp_path| to the full path of the directory that was created. The +// directory is only readable by the current user. Calling this function on the +// browser process UI or IO threads is not allowed. +/// +/*--cef(optional_param=prefix)--*/ +bool CefCreateNewTempDirectory(const CefString& prefix, + CefString& new_temp_path); + +/// +// Creates a directory within another directory. Extra characters will be +// appended to |prefix| to ensure that the new directory does not have the same +// name as an existing directory. Returns true on success and sets |new_dir| to +// the full path of the directory that was created. The directory is only +// readable by the current user. Calling this function on the browser process +// UI or IO threads is not allowed. +/// +/*--cef(optional_param=prefix)--*/ +bool CefCreateTempDirectoryInDirectory(const CefString& base_dir, + const CefString& prefix, + CefString& new_dir); + +/// +// Returns true if the given path exists and is a directory. Calling this +// function on the browser process UI or IO threads is not allowed. +/// +/*--cef()--*/ +bool CefDirectoryExists(const CefString& path); + +/// +// Deletes the given path whether it's a file or a directory. If |path| is a +// directory all contents will be deleted. If |recursive| is true any sub- +// directories and their contents will also be deleted (equivalent to executing +// "rm -rf", so use with caution). On POSIX environments if |path| is a symbolic +// link then only the symlink will be deleted. Returns true on successful +// deletion or if |path| does not exist. Calling this function on the browser +// process UI or IO threads is not allowed. +/// +/*--cef()--*/ +bool CefDeleteFile(const CefString& path, bool recursive); + +/// +// Writes the contents of |src_dir| into a zip archive at |dest_file|. If +// |include_hidden_files| is true files starting with "." will be included. +// Returns true on success. Calling this function on the browser process UI or +// IO threads is not allowed. +/// +/*--cef()--*/ +bool CefZipDirectory(const CefString& src_dir, + const CefString& dest_file, + bool include_hidden_files); + +#endif // CEF_INCLUDE_CEF_FILE_UTIL_H_ diff --git a/include/wrapper/cef_scoped_temp_dir.h b/include/wrapper/cef_scoped_temp_dir.h new file mode 100644 index 000000000..f40fa7d67 --- /dev/null +++ b/include/wrapper/cef_scoped_temp_dir.h @@ -0,0 +1,118 @@ +// Copyright (c) 2016 Marshall A. Greenblatt. Portions copyright (c) 2011 +// Google Inc. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the name Chromium Embedded +// Framework nor the names of its contributors may be used to endorse +// or promote products derived from this software without specific prior +// written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// --------------------------------------------------------------------------- +// +// The contents of this file are only available to applications that link +// against the libcef_dll_wrapper target. +// + +#ifndef CEF_INCLUDE_SCOPED_TEMP_DIR_H_ +#define CEF_INCLUDE_SCOPED_TEMP_DIR_H_ +#pragma once + +#include "include/base/cef_build.h" +#include "include/base/cef_macros.h" +#include "include/cef_base.h" + +/// +// An object representing a temporary / scratch directory that should be cleaned +// up (recursively) when this object goes out of scope. Note that since +// deletion occurs during the destructor, no further error handling is possible +// if the directory fails to be deleted. As a result, deletion is not +// guaranteed by this class. +// +// Multiple calls to the methods which establish a temporary directory +// (CreateUniqueTempDir, CreateUniqueTempDirUnderPath, and Set) must have +// intervening calls to Delete or Take, or the calls will fail. +/// +class CefScopedTempDir { + public: + /// + // No directory is owned/created initially. + /// + CefScopedTempDir(); + + /// + // Recursively delete path. + /// + ~CefScopedTempDir(); + + /// + // Creates a unique directory in TempPath, and takes ownership of it. + // See file_util::CreateNewTemporaryDirectory. + /// + bool CreateUniqueTempDir() WARN_UNUSED_RESULT; + + /// + // Creates a unique directory under a given path, and takes ownership of it. + /// + bool CreateUniqueTempDirUnderPath(const CefString& path) WARN_UNUSED_RESULT; + + /// + // Takes ownership of directory at |path|, creating it if necessary. + // Don't call multiple times unless Take() has been called first. + /// + bool Set(const CefString& path) WARN_UNUSED_RESULT; + + /// + // Deletes the temporary directory wrapped by this object. + /// + bool Delete() WARN_UNUSED_RESULT; + + /// + // Caller takes ownership of the temporary directory so it won't be destroyed + // when this object goes out of scope. + /// + CefString Take(); + + /// + // Returns the path to the created directory. Call one of the + // CreateUniqueTempDir* methods before getting the path. + /// + const CefString& GetPath() const; + + /// + // Returns true if path_ is empty. + /// + bool IsEmpty() const; + + /// + // Returns true if path_ is non-empty and exists. + /// + bool IsValid() const; + + private: + CefString path_; + + DISALLOW_COPY_AND_ASSIGN(CefScopedTempDir); +}; + +#endif // CEF_INCLUDE_SCOPED_TEMP_DIR_H_ diff --git a/libcef/common/file_util_impl.cc b/libcef/common/file_util_impl.cc new file mode 100644 index 000000000..3f97c751a --- /dev/null +++ b/libcef/common/file_util_impl.cc @@ -0,0 +1,86 @@ +// Copyright 2016 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that can +// be found in the LICENSE file. + +#include "include/cef_file_util.h" + +#include "include/cef_task.h" + +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/logging.h" +#include "third_party/zlib/google/zip.h" + +namespace { + +bool AllowFileIO() { + if (CefCurrentlyOn(TID_UI) || CefCurrentlyOn(TID_IO)) { + NOTREACHED() << "file IO is not allowed on the current thread"; + return false; + } + return true; +} + +} // namespace + +bool CefCreateDirectory(const CefString& full_path) { + if (!AllowFileIO()) + return false; + return base::CreateDirectory(full_path); +} + +bool CefGetTempDirectory(CefString& temp_dir) { + if (!AllowFileIO()) + return false; + base::FilePath result; + if (base::GetTempDir(&result)) { + temp_dir = result.value(); + return true; + } + return false; +} + +bool CefCreateNewTempDirectory(const CefString& prefix, + CefString& new_temp_path) { + if (!AllowFileIO()) + return false; + base::FilePath result; + if (base::CreateNewTempDirectory(prefix, &result)) { + new_temp_path = result.value(); + return true; + } + return false; +} + +bool CefCreateTempDirectoryInDirectory(const CefString& base_dir, + const CefString& prefix, + CefString& new_dir) { + if (!AllowFileIO()) + return false; + base::FilePath result; + if (base::CreateTemporaryDirInDir(base_dir, prefix, &result)) { + new_dir = result.value(); + return true; + } + return false; +} + +bool CefDirectoryExists(const CefString& path) { + if (!AllowFileIO()) + return false; + return base::DirectoryExists(path); +} + +bool CefDeleteFile(const CefString& path, bool recursive) { + if (!AllowFileIO()) + return false; + return base::DeleteFile(path, recursive); +} + +bool CefZipDirectory(const CefString& src_dir, + const CefString& dest_file, + bool include_hidden_files) { + if (!AllowFileIO()) + return false; + return zip::Zip(src_dir, dest_file, include_hidden_files); +} diff --git a/libcef_dll/libcef_dll.cc b/libcef_dll/libcef_dll.cc index 45ec3ac7d..5b335758a 100644 --- a/libcef_dll/libcef_dll.cc +++ b/libcef_dll/libcef_dll.cc @@ -12,6 +12,8 @@ #include "include/cef_app.h" #include "include/capi/cef_app_capi.h" +#include "include/cef_file_util.h" +#include "include/capi/cef_file_util_capi.h" #include "include/cef_geolocation.h" #include "include/capi/cef_geolocation_capi.h" #include "include/cef_origin_whitelist.h" @@ -387,6 +389,147 @@ CEF_EXPORT void cef_enable_highdpi_support() { CefEnableHighDPISupport(); } +CEF_EXPORT int cef_create_directory(const cef_string_t* full_path) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: full_path; type: string_byref_const + DCHECK(full_path); + if (!full_path) + return 0; + + // Execute + bool _retval = CefCreateDirectory( + CefString(full_path)); + + // Return type: bool + return _retval; +} + +CEF_EXPORT int cef_get_temp_directory(cef_string_t* temp_dir) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: temp_dir; type: string_byref + DCHECK(temp_dir); + if (!temp_dir) + return 0; + + // Translate param: temp_dir; type: string_byref + CefString temp_dirStr(temp_dir); + + // Execute + bool _retval = CefGetTempDirectory( + temp_dirStr); + + // Return type: bool + return _retval; +} + +CEF_EXPORT int cef_create_new_temp_directory(const cef_string_t* prefix, + cef_string_t* new_temp_path) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: new_temp_path; type: string_byref + DCHECK(new_temp_path); + if (!new_temp_path) + return 0; + // Unverified params: prefix + + // Translate param: new_temp_path; type: string_byref + CefString new_temp_pathStr(new_temp_path); + + // Execute + bool _retval = CefCreateNewTempDirectory( + CefString(prefix), + new_temp_pathStr); + + // Return type: bool + return _retval; +} + +CEF_EXPORT int cef_create_temp_directory_in_directory( + const cef_string_t* base_dir, const cef_string_t* prefix, + cef_string_t* new_dir) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: base_dir; type: string_byref_const + DCHECK(base_dir); + if (!base_dir) + return 0; + // Verify param: new_dir; type: string_byref + DCHECK(new_dir); + if (!new_dir) + return 0; + // Unverified params: prefix + + // Translate param: new_dir; type: string_byref + CefString new_dirStr(new_dir); + + // Execute + bool _retval = CefCreateTempDirectoryInDirectory( + CefString(base_dir), + CefString(prefix), + new_dirStr); + + // Return type: bool + return _retval; +} + +CEF_EXPORT int cef_directory_exists(const cef_string_t* path) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: path; type: string_byref_const + DCHECK(path); + if (!path) + return 0; + + // Execute + bool _retval = CefDirectoryExists( + CefString(path)); + + // Return type: bool + return _retval; +} + +CEF_EXPORT int cef_delete_file(const cef_string_t* path, int recursive) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: path; type: string_byref_const + DCHECK(path); + if (!path) + return 0; + + // Execute + bool _retval = CefDeleteFile( + CefString(path), + recursive?true:false); + + // Return type: bool + return _retval; +} + +CEF_EXPORT int cef_zip_directory(const cef_string_t* src_dir, + const cef_string_t* dest_file, int include_hidden_files) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: src_dir; type: string_byref_const + DCHECK(src_dir); + if (!src_dir) + return 0; + // Verify param: dest_file; type: string_byref_const + DCHECK(dest_file); + if (!dest_file) + return 0; + + // Execute + bool _retval = CefZipDirectory( + CefString(src_dir), + CefString(dest_file), + include_hidden_files?true:false); + + // Return type: bool + return _retval; +} + CEF_EXPORT int cef_get_geolocation( struct _cef_get_geolocation_callback_t* callback) { // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING diff --git a/libcef_dll/wrapper/cef_scoped_temp_dir.cc b/libcef_dll/wrapper/cef_scoped_temp_dir.cc new file mode 100644 index 000000000..d0a55f48c --- /dev/null +++ b/libcef_dll/wrapper/cef_scoped_temp_dir.cc @@ -0,0 +1,87 @@ +// Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright +// 2011 The Chromium Authors. All rights reserved. Use of this source code is +// governed by a BSD-style license that can be found in the LICENSE file. + +#include "include/wrapper/cef_scoped_temp_dir.h" + +#include "include/base/cef_logging.h" +#include "include/cef_file_util.h" + +CefScopedTempDir::CefScopedTempDir() { +} + +CefScopedTempDir::~CefScopedTempDir() { + if (!path_.empty() && !Delete()) + DLOG(WARNING) << "Could not delete temp dir in dtor."; +} + +bool CefScopedTempDir::CreateUniqueTempDir() { + if (!path_.empty()) + return false; + + // This "scoped_dir" prefix is only used on Windows and serves as a template + // for the unique name. + if (!CefCreateNewTempDirectory("scoped_dir", path_)) + return false; + + return true; +} + +bool CefScopedTempDir::CreateUniqueTempDirUnderPath( + const CefString& base_path) { + if (!path_.empty()) + return false; + + // If |base_path| does not exist, create it. + if (!CefCreateDirectory(base_path)) + return false; + + // Create a new, uniquely named directory under |base_path|. + if (!CefCreateTempDirectoryInDirectory(base_path, "scoped_dir_", path_)) + return false; + + return true; +} + +bool CefScopedTempDir::Set(const CefString& path) { + if (!path_.empty()) + return false; + + if (!CefDirectoryExists(path) && !CefCreateDirectory(path)) + return false; + + path_ = path; + return true; +} + +bool CefScopedTempDir::Delete() { + if (path_.empty()) + return false; + + bool ret = CefDeleteFile(path_, true); + if (ret) { + // We only clear the path if deleted the directory. + path_.clear(); + } + + return ret; +} + +CefString CefScopedTempDir::Take() { + CefString ret = path_; + path_.clear(); + return ret; +} + +const CefString& CefScopedTempDir::GetPath() const { + DCHECK(!path_.empty()) << "Did you call CreateUniqueTempDir* before?"; + return path_; +} + +bool CefScopedTempDir::IsEmpty() const { + return path_.empty(); +} + +bool CefScopedTempDir::IsValid() const { + return !path_.empty() && CefDirectoryExists(path_); +} diff --git a/libcef_dll/wrapper/libcef_dll_wrapper.cc b/libcef_dll/wrapper/libcef_dll_wrapper.cc index ac5f6fc88..cdca83231 100644 --- a/libcef_dll/wrapper/libcef_dll_wrapper.cc +++ b/libcef_dll/wrapper/libcef_dll_wrapper.cc @@ -12,6 +12,8 @@ #include "include/cef_app.h" #include "include/capi/cef_app_capi.h" +#include "include/cef_file_util.h" +#include "include/capi/cef_file_util_capi.h" #include "include/cef_geolocation.h" #include "include/capi/cef_geolocation_capi.h" #include "include/cef_origin_whitelist.h" @@ -379,6 +381,124 @@ CEF_GLOBAL void CefEnableHighDPISupport() { cef_enable_highdpi_support(); } +CEF_GLOBAL bool CefCreateDirectory(const CefString& full_path) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: full_path; type: string_byref_const + DCHECK(!full_path.empty()); + if (full_path.empty()) + return false; + + // Execute + int _retval = cef_create_directory( + full_path.GetStruct()); + + // Return type: bool + return _retval?true:false; +} + +CEF_GLOBAL bool CefGetTempDirectory(CefString& temp_dir) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Execute + int _retval = cef_get_temp_directory( + temp_dir.GetWritableStruct()); + + // Return type: bool + return _retval?true:false; +} + +CEF_GLOBAL bool CefCreateNewTempDirectory(const CefString& prefix, + CefString& new_temp_path) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Unverified params: prefix + + // Execute + int _retval = cef_create_new_temp_directory( + prefix.GetStruct(), + new_temp_path.GetWritableStruct()); + + // Return type: bool + return _retval?true:false; +} + +CEF_GLOBAL bool CefCreateTempDirectoryInDirectory(const CefString& base_dir, + const CefString& prefix, CefString& new_dir) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: base_dir; type: string_byref_const + DCHECK(!base_dir.empty()); + if (base_dir.empty()) + return false; + // Unverified params: prefix + + // Execute + int _retval = cef_create_temp_directory_in_directory( + base_dir.GetStruct(), + prefix.GetStruct(), + new_dir.GetWritableStruct()); + + // Return type: bool + return _retval?true:false; +} + +CEF_GLOBAL bool CefDirectoryExists(const CefString& path) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: path; type: string_byref_const + DCHECK(!path.empty()); + if (path.empty()) + return false; + + // Execute + int _retval = cef_directory_exists( + path.GetStruct()); + + // Return type: bool + return _retval?true:false; +} + +CEF_GLOBAL bool CefDeleteFile(const CefString& path, bool recursive) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: path; type: string_byref_const + DCHECK(!path.empty()); + if (path.empty()) + return false; + + // Execute + int _retval = cef_delete_file( + path.GetStruct(), + recursive); + + // Return type: bool + return _retval?true:false; +} + +CEF_GLOBAL bool CefZipDirectory(const CefString& src_dir, + const CefString& dest_file, bool include_hidden_files) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: src_dir; type: string_byref_const + DCHECK(!src_dir.empty()); + if (src_dir.empty()) + return false; + // Verify param: dest_file; type: string_byref_const + DCHECK(!dest_file.empty()); + if (dest_file.empty()) + return false; + + // Execute + int _retval = cef_zip_directory( + src_dir.GetStruct(), + dest_file.GetStruct(), + include_hidden_files); + + // Return type: bool + return _retval?true:false; +} + CEF_GLOBAL bool CefGetGeolocation( CefRefPtr callback) { // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING diff --git a/tests/unittests/cookie_unittest.cc b/tests/unittests/cookie_unittest.cc index 4e06e37d7..ad7a466b3 100644 --- a/tests/unittests/cookie_unittest.cc +++ b/tests/unittests/cookie_unittest.cc @@ -4,7 +4,6 @@ #include -#include "base/files/scoped_temp_dir.h" #include "base/synchronization/waitable_event.h" #include "include/base/cef_bind.h" @@ -13,12 +12,15 @@ #include "include/cef_cookie.h" #include "include/cef_scheme.h" #include "include/wrapper/cef_closure_task.h" +#include "include/wrapper/cef_scoped_temp_dir.h" #include "testing/gtest/include/gtest/gtest.h" #include "tests/unittests/test_handler.h" #include "tests/unittests/test_suite.h" namespace { +const int kCacheDeleteDelay = 50; + const char* kTestUrl = "http://www.test.com/path/to/cookietest/foo.html"; const char* kTestDomain = "www.test.com"; const char* kTestPath = "/path/to/cookietest"; @@ -448,7 +450,7 @@ void TestChangeDirectory(CefRefPtr manager, const CefString& original_dir) { CefCookie cookie; - base::ScopedTempDir temp_dir; + CefScopedTempDir temp_dir; // Create a new temporary directory. EXPECT_TRUE(temp_dir.CreateUniqueTempDir()); @@ -457,7 +459,7 @@ void TestChangeDirectory(CefRefPtr manager, DeleteAllCookies(manager, event); // Set the new temporary directory as the storage location. - EXPECT_TRUE(manager->SetStoragePath(temp_dir.GetPath().value(), false, NULL)); + EXPECT_TRUE(manager->SetStoragePath(temp_dir.GetPath(), false, NULL)); // Wait for the storage location change to complete on the IO thread. WaitForIOThread(); @@ -481,7 +483,7 @@ void TestChangeDirectory(CefRefPtr manager, VerifyNoCookies(manager, event, true); // Set the new temporary directory as the storage location. - EXPECT_TRUE(manager->SetStoragePath(temp_dir.GetPath().value(), false, NULL)); + EXPECT_TRUE(manager->SetStoragePath(temp_dir.GetPath(), false, NULL)); // Wait for the storage location change to complete on the IO thread. WaitForIOThread(); @@ -529,7 +531,7 @@ TEST(CookieTest, DomainCookieInMemory) { // Test creation of a domain cookie. TEST(CookieTest, DomainCookieOnDisk) { - base::ScopedTempDir temp_dir; + CefScopedTempDir temp_dir; // Create a new temporary directory. EXPECT_TRUE(temp_dir.CreateUniqueTempDir()); @@ -539,7 +541,7 @@ TEST(CookieTest, DomainCookieOnDisk) { base::WaitableEvent::InitialState::NOT_SIGNALED); CefRefPtr manager = - CefCookieManager::CreateManager(temp_dir.GetPath().value(), false, + CefCookieManager::CreateManager(temp_dir.GetPath(), false, new TestCompletionCallback(&event)); event.Wait(); EXPECT_TRUE(manager.get()); @@ -549,7 +551,7 @@ TEST(CookieTest, DomainCookieOnDisk) { // The backing store will be closed on the DB thread after the CookieManager // is destroyed. manager = NULL; - WaitForDBThread(); + WaitForDBThreadWithDelay(kCacheDeleteDelay); } // Test creation of a host cookie. @@ -583,7 +585,7 @@ TEST(CookieTest, HostCookieInMemory) { // Test creation of a host cookie. TEST(CookieTest, HostCookieOnDisk) { - base::ScopedTempDir temp_dir; + CefScopedTempDir temp_dir; // Create a new temporary directory. EXPECT_TRUE(temp_dir.CreateUniqueTempDir()); @@ -593,7 +595,7 @@ TEST(CookieTest, HostCookieOnDisk) { base::WaitableEvent::InitialState::NOT_SIGNALED); CefRefPtr manager = - CefCookieManager::CreateManager(temp_dir.GetPath().value(), false, + CefCookieManager::CreateManager(temp_dir.GetPath(), false, new TestCompletionCallback(&event)); event.Wait(); EXPECT_TRUE(manager.get()); @@ -603,7 +605,7 @@ TEST(CookieTest, HostCookieOnDisk) { // The backing store will be closed on the DB thread after the CookieManager // is destroyed. manager = NULL; - WaitForDBThread(); + WaitForDBThreadWithDelay(kCacheDeleteDelay); } // Test creation of multiple cookies. @@ -637,7 +639,7 @@ TEST(CookieTest, MultipleCookiesInMemory) { // Test creation of multiple cookies. TEST(CookieTest, MultipleCookiesOnDisk) { - base::ScopedTempDir temp_dir; + CefScopedTempDir temp_dir; // Create a new temporary directory. EXPECT_TRUE(temp_dir.CreateUniqueTempDir()); @@ -647,7 +649,7 @@ TEST(CookieTest, MultipleCookiesOnDisk) { base::WaitableEvent::InitialState::NOT_SIGNALED); CefRefPtr manager = - CefCookieManager::CreateManager(temp_dir.GetPath().value(), false, + CefCookieManager::CreateManager(temp_dir.GetPath(), false, new TestCompletionCallback(&event)); event.Wait(); EXPECT_TRUE(manager.get()); @@ -657,7 +659,7 @@ TEST(CookieTest, MultipleCookiesOnDisk) { // The backing store will be closed on the DB thread after the CookieManager // is destroyed. manager = NULL; - WaitForDBThread(); + WaitForDBThreadWithDelay(kCacheDeleteDelay); } TEST(CookieTest, AllCookiesGlobal) { @@ -688,7 +690,7 @@ TEST(CookieTest, AllCookiesInMemory) { } TEST(CookieTest, AllCookiesOnDisk) { - base::ScopedTempDir temp_dir; + CefScopedTempDir temp_dir; // Create a new temporary directory. EXPECT_TRUE(temp_dir.CreateUniqueTempDir()); @@ -698,7 +700,7 @@ TEST(CookieTest, AllCookiesOnDisk) { base::WaitableEvent::InitialState::NOT_SIGNALED); CefRefPtr manager = - CefCookieManager::CreateManager(temp_dir.GetPath().value(), false, + CefCookieManager::CreateManager(temp_dir.GetPath(), false, new TestCompletionCallback(&event)); event.Wait(); EXPECT_TRUE(manager.get()); @@ -708,7 +710,7 @@ TEST(CookieTest, AllCookiesOnDisk) { // The backing store will be closed on the DB thread after the CookieManager // is destroyed. manager = NULL; - WaitForDBThread(); + WaitForDBThreadWithDelay(kCacheDeleteDelay); } TEST(CookieTest, ChangeDirectoryGlobal) { @@ -742,7 +744,7 @@ TEST(CookieTest, ChangeDirectoryCreated) { } TEST(CookieTest, SessionCookieNoPersist) { - base::ScopedTempDir temp_dir; + CefScopedTempDir temp_dir; base::WaitableEvent event( base::WaitableEvent::ResetPolicy::AUTOMATIC, base::WaitableEvent::InitialState::NOT_SIGNALED); @@ -752,7 +754,7 @@ TEST(CookieTest, SessionCookieNoPersist) { EXPECT_TRUE(temp_dir.CreateUniqueTempDir()); CefRefPtr manager = - CefCookieManager::CreateManager(temp_dir.GetPath().value(), false, + CefCookieManager::CreateManager(temp_dir.GetPath(), false, new TestCompletionCallback(&event)); event.Wait(); EXPECT_TRUE(manager.get()); @@ -768,7 +770,7 @@ TEST(CookieTest, SessionCookieNoPersist) { event.Wait(); // Create a new manager to read the same cookie store. - manager = CefCookieManager::CreateManager(temp_dir.GetPath().value(), false, + manager = CefCookieManager::CreateManager(temp_dir.GetPath(), false, new TestCompletionCallback(&event)); event.Wait(); EXPECT_TRUE(manager.get()); @@ -778,7 +780,7 @@ TEST(CookieTest, SessionCookieNoPersist) { } TEST(CookieTest, SessionCookieWillPersist) { - base::ScopedTempDir temp_dir; + CefScopedTempDir temp_dir; base::WaitableEvent event( base::WaitableEvent::ResetPolicy::AUTOMATIC, base::WaitableEvent::InitialState::NOT_SIGNALED); @@ -788,7 +790,7 @@ TEST(CookieTest, SessionCookieWillPersist) { EXPECT_TRUE(temp_dir.CreateUniqueTempDir()); CefRefPtr manager = - CefCookieManager::CreateManager(temp_dir.GetPath().value(), true, + CefCookieManager::CreateManager(temp_dir.GetPath(), true, new TestCompletionCallback(&event)); event.Wait(); EXPECT_TRUE(manager.get()); @@ -804,7 +806,7 @@ TEST(CookieTest, SessionCookieWillPersist) { event.Wait(); // Create a new manager to read the same cookie store. - manager = CefCookieManager::CreateManager(temp_dir.GetPath().value(), true, + manager = CefCookieManager::CreateManager(temp_dir.GetPath(), true, new TestCompletionCallback(&event)); event.Wait(); EXPECT_TRUE(manager.get()); diff --git a/tests/unittests/download_unittest.cc b/tests/unittests/download_unittest.cc index d799ff615..9a3e9e459 100644 --- a/tests/unittests/download_unittest.cc +++ b/tests/unittests/download_unittest.cc @@ -2,12 +2,11 @@ // reserved. Use of this source code is governed by a BSD-style license that // can be found in the LICENSE file. -#include "base/files/file_util.h" -#include "base/files/scoped_temp_dir.h" - #include "include/cef_scheme.h" #include "include/wrapper/cef_closure_task.h" +#include "include/wrapper/cef_scoped_temp_dir.h" #include "testing/gtest/include/gtest/gtest.h" +#include "tests/unittests/file_util.h" #include "tests/unittests/test_handler.h" namespace { @@ -158,7 +157,7 @@ class DownloadTestHandler : public TestHandler { // Create a new temporary directory. EXPECT_TRUE(temp_dir_.CreateUniqueTempDir()); - test_path_ = temp_dir_.GetPath().AppendASCII(kTestFileName); + test_path_ = file_util::JoinPath(temp_dir_.GetPath(), kTestFileName); if (test_mode_ == NAVIGATED) { // Add the resource that we'll navigate to. @@ -254,7 +253,7 @@ class DownloadTestHandler : public TestHandler { download_item->GetContentDisposition().ToString().c_str()); EXPECT_STREQ(kTestMimeType, download_item->GetMimeType().ToString().c_str()); - callback->Continue(test_path_.value(), false); + callback->Continue(test_path_, false); if (test_mode_ == NAVIGATED) browser->GetMainFrame()->LoadURL(kTestNavUrl); @@ -291,8 +290,7 @@ class DownloadTestHandler : public TestHandler { std::string full_path = download_item->GetFullPath(); if (!full_path.empty()) { got_full_path_.yes(); - EXPECT_STREQ(CefString(test_path_.value()).ToString().c_str(), - full_path.c_str()); + EXPECT_STREQ(test_path_.c_str(), full_path.c_str()); } if (download_item->IsComplete()) { @@ -315,7 +313,30 @@ class DownloadTestHandler : public TestHandler { ContinuePendingIfReady(); } + void VerifyResultsOnFileThread() { + EXPECT_TRUE(CefCurrentlyOn(TID_FILE)); + + if (test_mode_ != PENDING) { + // Verify the file contents. + std::string contents; + EXPECT_TRUE(file_util::ReadFileToString(test_path_, &contents)); + EXPECT_STREQ(kTestContent, contents.c_str()); + } + + EXPECT_TRUE(temp_dir_.Delete()); + EXPECT_TRUE(temp_dir_.IsEmpty()); + + CefPostTask(TID_UI, base::Bind(&DownloadTestHandler::DestroyTest, this)); + } + void DestroyTest() override { + if (!temp_dir_.IsEmpty()) { + // Clean up temp_dir_ on the FILE thread before destroying the test. + CefPostTask(TID_FILE, + base::Bind(&DownloadTestHandler::VerifyResultsOnFileThread, this)); + return; + } + CefRegisterSchemeHandlerFactory("http", kTestDomain, NULL); EXPECT_TRUE(got_download_request_); @@ -333,15 +354,8 @@ class DownloadTestHandler : public TestHandler { } else { EXPECT_TRUE(got_download_complete_); EXPECT_TRUE(got_full_path_); - - // Verify the file contents. - std::string contents; - EXPECT_TRUE(base::ReadFileToString(test_path_, &contents)); - EXPECT_STREQ(kTestContent, contents.c_str()); } - EXPECT_TRUE(temp_dir_.Delete()); - TestHandler::DestroyTest(); } @@ -351,8 +365,8 @@ class DownloadTestHandler : public TestHandler { // Used with NAVIGATED test mode. CefRefPtr delay_callback_; - base::ScopedTempDir temp_dir_; - base::FilePath test_path_; + CefScopedTempDir temp_dir_; + std::string test_path_; uint32 download_id_; TrackCallback got_download_request_; diff --git a/tests/unittests/file_util.cc b/tests/unittests/file_util.cc new file mode 100644 index 000000000..876ff54d4 --- /dev/null +++ b/tests/unittests/file_util.cc @@ -0,0 +1,110 @@ +// Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright +// 2012 The Chromium Authors. All rights reserved. Use of this source code is +// governed by a BSD-style license that can be found in the LICENSE file. + +#include "tests/unittests/file_util.h" + +#include "include/base/cef_build.h" +#include "include/cef_task.h" + +#include +#include +#include + +namespace file_util { + +namespace { + +bool AllowFileIO() { + if (CefCurrentlyOn(TID_UI) || CefCurrentlyOn(TID_IO)) { + NOTREACHED() << "file IO is not allowed on the current thread"; + return false; + } + return true; +} + +} // namespace + +#if defined(OS_WIN) +const char kPathSep = '\\'; +#else +const char kPathSep = '/'; +#endif + +bool ReadFileToString(const std::string& path, std::string* contents, + size_t max_size) { + if (!AllowFileIO()) + return false; + + if (contents) + contents->clear(); + FILE* file = fopen(path.c_str(), "rb"); + if (!file) + return false; + + const size_t kBufferSize = 1 << 16; + std::unique_ptr buf(new char[kBufferSize]); + size_t len; + size_t size = 0; + bool read_status = true; + + // Many files supplied in |path| have incorrect size (proc files etc). + // Hence, the file is read sequentially as opposed to a one-shot read. + while ((len = fread(buf.get(), 1, kBufferSize, file)) > 0) { + if (contents) + contents->append(buf.get(), std::min(len, max_size - size)); + + if ((max_size - size) < len) { + read_status = false; + break; + } + + size += len; + } + read_status = read_status && !ferror(file); + fclose(file); + + return read_status; +} + +int WriteFile(const std::string& path, const char* data, int size) { + if (!AllowFileIO()) + return -1; + + FILE* file = fopen(path.c_str(), "wb"); + if (!file) + return -1; + + int written = 0; + + do { + size_t write = fwrite(data + written, 1, size - written, file); + if (write == 0) + break; + written += write; + } while (written < size); + + fclose(file); + + return written; +} + +std::string JoinPath(const std::string& path1, const std::string& path2) { + if (path1.empty() && path2.empty()) + return std::string(); + if (path1.empty()) + return path2; + if (path2.empty()) + return path1; + + std::string result = path1; + if (result[result.size() - 1] != kPathSep) + result += kPathSep; + if (path2[0] == kPathSep) + result += path2.substr(1); + else + result += path2; + return result; +} + +} // namespace file_util diff --git a/tests/unittests/file_util.h b/tests/unittests/file_util.h new file mode 100644 index 000000000..83b4ac127 --- /dev/null +++ b/tests/unittests/file_util.h @@ -0,0 +1,39 @@ +// Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright +// 2012 The Chromium Authors. All rights reserved. Use of this source code is +// governed by a BSD-style license that can be found in the LICENSE file. + +#ifndef CEF_TESTS_UNITTESTS_FILE_UTIL_H_ +#define CEF_TESTS_UNITTESTS_FILE_UTIL_H_ +#pragma once + +#include +#include + +namespace file_util { + +// Platform-specific path separator. +extern const char kPathSep; + +// Reads the file at |path| into |contents| and returns true on success and +// false on error. In case of I/O error, |contents| holds the data that could +// be read from the file before the error occurred. When the file size exceeds +// max_size|, the function returns false with |contents| holding the file +// truncated to |max_size|. |contents| may be NULL, in which case this function +// is useful for its side effect of priming the disk cache (could be used for +// unit tests). Calling this function on the browser process UI or IO threads is +// not allowed. +bool ReadFileToString(const std::string& path, std::string* contents, + size_t max_size = std::numeric_limits::max()); + +// Writes the given buffer into the file, overwriting any data that was +// previously there. Returns the number of bytes written, or -1 on error. +// Calling this function on the browser process UI or IO threads is not allowed. +int WriteFile(const std::string& path, const char* data, int size); + +// Combines |path1| and |path2| with the correct platform-specific path +// separator. +std::string JoinPath(const std::string& path1, const std::string& path2); + +} // namespace file_util + +#endif // CEF_TESTS_UNITTESTS_FILE_UTIL_H_ diff --git a/tests/unittests/file_util_unittest.cc b/tests/unittests/file_util_unittest.cc new file mode 100644 index 000000000..cee4f1156 --- /dev/null +++ b/tests/unittests/file_util_unittest.cc @@ -0,0 +1,47 @@ +// Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright +// 2011 The Chromium Authors. All rights reserved. Use of this source code is +// governed by a BSD-style license that can be found in the LICENSE file. + +#include + +#include "include/wrapper/cef_scoped_temp_dir.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "tests/unittests/file_util.h" + +TEST(FileUtil, JoinPath) { + // Should return whichever path component is non-empty. + EXPECT_STREQ("", file_util::JoinPath("", "").c_str()); + EXPECT_STREQ("path1", file_util::JoinPath("path1", "").c_str()); + EXPECT_STREQ("path2", file_util::JoinPath("", "path2").c_str()); + + const std::string& expected = + std::string("path1") + file_util::kPathSep + std::string("path2"); + + // Should always be 1 kPathSep character between paths. + EXPECT_STREQ(expected.c_str(), file_util::JoinPath("path1", "path2").c_str()); + EXPECT_STREQ(expected.c_str(), + file_util::JoinPath(std::string("path1") + file_util::kPathSep, + "path2").c_str()); + EXPECT_STREQ(expected.c_str(), + file_util::JoinPath("path1", + file_util::kPathSep + std::string("path2")).c_str()); + EXPECT_STREQ(expected.c_str(), + file_util::JoinPath(std::string("path1") + file_util::kPathSep, + file_util::kPathSep + std::string("path2")).c_str()); +} + +TEST(FileUtil, WriteAndReadFile) { + CefScopedTempDir dir; + EXPECT_TRUE(dir.CreateUniqueTempDir()); + + const std::string& data = "Test contents to read/write"; + const std::string& path = file_util::JoinPath(dir.GetPath(), "test.txt"); + + EXPECT_EQ(static_cast(data.size()), + file_util::WriteFile(path.c_str(), data.data(), + static_cast(data.size()))); + + std::string read; + EXPECT_TRUE(file_util::ReadFileToString(path.c_str(), &read)); + EXPECT_STREQ(data.c_str(), read.c_str()); +} diff --git a/tests/unittests/request_context_unittest.cc b/tests/unittests/request_context_unittest.cc index 78af5b835..a7bf1b586 100644 --- a/tests/unittests/request_context_unittest.cc +++ b/tests/unittests/request_context_unittest.cc @@ -3,10 +3,10 @@ // can be found in the LICENSE file. #include "include/base/cef_bind.h" -#include "base/files/scoped_temp_dir.h" #include "include/cef_request_context.h" #include "include/cef_request_context_handler.h" #include "include/wrapper/cef_closure_task.h" +#include "include/wrapper/cef_scoped_temp_dir.h" #include "testing/gtest/include/gtest/gtest.h" #include "tests/unittests/test_handler.h" @@ -172,11 +172,11 @@ TEST(RequestContextTest, CreateContextSharedGlobal) { } TEST(RequestContextTest, CreateContextSharedOnDisk) { - base::ScopedTempDir tempdir; + CefScopedTempDir tempdir; EXPECT_TRUE(tempdir.CreateUniqueTempDir()); CefRequestContextSettings settings; - CefString(&settings.cache_path) = tempdir.GetPath().value(); + CefString(&settings.cache_path) = tempdir.GetPath(); CefRefPtr context1 = CefRequestContext::CreateContext(settings, NULL); diff --git a/tests/unittests/resource_manager_unittest.cc b/tests/unittests/resource_manager_unittest.cc index 21b556cb7..fbd2f5de1 100644 --- a/tests/unittests/resource_manager_unittest.cc +++ b/tests/unittests/resource_manager_unittest.cc @@ -4,16 +4,15 @@ #include -#include "base/files/file_util.h" -#include "base/files/scoped_temp_dir.h" - #include "include/base/cef_bind.h" +#include "include/cef_file_util.h" #include "include/wrapper/cef_closure_task.h" #include "include/wrapper/cef_resource_manager.h" +#include "include/wrapper/cef_scoped_temp_dir.h" #include "include/wrapper/cef_stream_resource_handler.h" #include "testing/gtest/include/gtest/gtest.h" +#include "tests/unittests/file_util.h" #include "tests/unittests/routing_test_handler.h" -#include "third_party/zlib/google/zip.h" namespace { @@ -29,9 +28,10 @@ std::string CreateContents(const std::string& message) { ""; } -void WriteFile(const base::FilePath& path, const std::string& contents) { +void WriteFile(const std::string& path, const std::string& contents) { int contents_size = static_cast(contents.size()); - int write_ct = base::WriteFile(path, contents.data(), contents_size); + int write_ct = + file_util::WriteFile(path, contents.data(), contents_size); EXPECT_EQ(contents_size, write_ct); } @@ -689,22 +689,23 @@ TEST(ResourceManagerTest, DirectoryProvider) { state.urls_.push_back(kUrlBase + std::string("/sub/") + kFile3); state.urls_.push_back(kUrlBase + std::string("/") + kFile4); - base::ScopedTempDir scoped_dir; + CefScopedTempDir scoped_dir; EXPECT_TRUE(scoped_dir.CreateUniqueTempDir()); // Write the files to disk. - const base::FilePath& temp_dir = scoped_dir.GetPath(); - WriteFile(temp_dir.AppendASCII(kFile1), CreateContents(success1_message)); - WriteFile(temp_dir.AppendASCII(kFile2), CreateContents(success2_message)); + const std::string& temp_dir = scoped_dir.GetPath(); + WriteFile(file_util::JoinPath(temp_dir, kFile1), + CreateContents(success1_message)); + WriteFile(file_util::JoinPath(temp_dir, kFile2), + CreateContents(success2_message)); // Also include a subdirectory. - const base::FilePath& sub_dir = temp_dir.AppendASCII("sub"); - EXPECT_TRUE(base::CreateDirectory(sub_dir)); - WriteFile(sub_dir.AppendASCII(kFile3), CreateContents(success3_message)); + const std::string& sub_dir = file_util::JoinPath(temp_dir, "sub"); + EXPECT_TRUE(CefCreateDirectory(sub_dir)); + WriteFile(file_util::JoinPath(sub_dir, kFile3), + CreateContents(success3_message)); - state.manager_->AddDirectoryProvider(kUrlBase, - CefString(temp_dir.value()), - 0, std::string()); + state.manager_->AddDirectoryProvider(kUrlBase, temp_dir, 0, std::string()); CefRefPtr handler = new ResourceManagerTestHandler(&state); @@ -747,30 +748,32 @@ TEST(ResourceManagerTest, ArchiveProvider) { state.urls_.push_back(kUrlBase + std::string("/") + kFile4); // Only the first 2 URLs will be handled. - base::ScopedTempDir scoped_dir; + CefScopedTempDir scoped_dir; EXPECT_TRUE(scoped_dir.CreateUniqueTempDir()); - const base::FilePath& temp_dir = scoped_dir.GetPath(); + const std::string& temp_dir = scoped_dir.GetPath(); // Write the files to disk. - const base::FilePath& file_dir = temp_dir.AppendASCII("files"); - EXPECT_TRUE(base::CreateDirectory(file_dir)); - WriteFile(file_dir.AppendASCII(kFile1), CreateContents(success1_message)); - WriteFile(file_dir.AppendASCII(kFile2), CreateContents(success2_message)); + const std::string& file_dir = file_util::JoinPath(temp_dir, "files"); + EXPECT_TRUE(CefCreateDirectory(file_dir)); + WriteFile(file_util::JoinPath(file_dir, kFile1), + CreateContents(success1_message)); + WriteFile(file_util::JoinPath(file_dir, kFile2), + CreateContents(success2_message)); // Also include a subdirectory. - const base::FilePath& sub_dir = file_dir.AppendASCII("sub"); - EXPECT_TRUE(base::CreateDirectory(sub_dir)); - WriteFile(sub_dir.AppendASCII(kFile3), CreateContents(success3_message)); + const std::string& sub_dir = file_util::JoinPath(file_dir, "sub"); + EXPECT_TRUE(CefCreateDirectory(sub_dir)); + WriteFile(file_util::JoinPath(sub_dir, kFile3), + CreateContents(success3_message)); - const base::FilePath& archive_path = temp_dir.AppendASCII("archive.zip"); + const std::string& archive_path = + file_util::JoinPath(temp_dir, "archive.zip"); // Create the archive file. - EXPECT_TRUE(zip::Zip(file_dir, archive_path, false)); + EXPECT_TRUE(CefZipDirectory(file_dir, archive_path, false)); - state.manager_->AddArchiveProvider(kUrlBase, - CefString(archive_path.value()), - std::string(), + state.manager_->AddArchiveProvider(kUrlBase, archive_path, std::string(), 0, std::string()); CefRefPtr handler = diff --git a/tests/unittests/scoped_temp_dir_unittest.cc b/tests/unittests/scoped_temp_dir_unittest.cc new file mode 100644 index 000000000..b2ee6ae30 --- /dev/null +++ b/tests/unittests/scoped_temp_dir_unittest.cc @@ -0,0 +1,90 @@ +// Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright +// 2011 The Chromium Authors. All rights reserved. Use of this source code is +// governed by a BSD-style license that can be found in the LICENSE file. + +#include + +#include "include/cef_file_util.h" +#include "include/wrapper/cef_scoped_temp_dir.h" +#include "testing/gtest/include/gtest/gtest.h" + +TEST(ScopedTempDir, FullPath) { + CefString test_path; + CefCreateNewTempDirectory("scoped_temp_dir", test_path); + + // Against an existing dir, it should get destroyed when leaving scope. + EXPECT_TRUE(CefDirectoryExists(test_path)); + { + CefScopedTempDir dir; + EXPECT_TRUE(dir.Set(test_path)); + EXPECT_TRUE(dir.IsValid()); + } + EXPECT_FALSE(CefDirectoryExists(test_path)); + + { + CefScopedTempDir dir; + EXPECT_TRUE(dir.Set(test_path)); + // Now the dir doesn't exist, so ensure that it gets created. + EXPECT_TRUE(CefDirectoryExists(test_path)); + // When we call Take(), it shouldn't get destroyed when leaving scope. + CefString path = dir.Take(); + EXPECT_STREQ(path.ToString().c_str(), test_path.ToString().c_str()); + EXPECT_FALSE(dir.IsValid()); + } + EXPECT_TRUE(CefDirectoryExists(test_path)); + + // Clean up. + { + CefScopedTempDir dir; + EXPECT_TRUE(dir.Set(test_path)); + } + EXPECT_FALSE(CefDirectoryExists(test_path)); +} + +TEST(ScopedTempDir, TempDir) { + // In this case, just verify that a directory was created and that it's a + // child of TempDir. + CefString test_path; + { + CefScopedTempDir dir; + EXPECT_TRUE(dir.CreateUniqueTempDir()); + test_path = dir.GetPath(); + EXPECT_TRUE(CefDirectoryExists(test_path)); + CefString tmp_dir; + EXPECT_TRUE(CefGetTempDirectory(tmp_dir)); + EXPECT_TRUE(test_path.ToString().find(tmp_dir.ToString()) != + std::string::npos); + } + EXPECT_FALSE(CefDirectoryExists(test_path)); +} + +TEST(ScopedTempDir, UniqueTempDirUnderPath) { + // Create a path which will contain a unique temp path. + CefString base_path; + ASSERT_TRUE(CefCreateNewTempDirectory("base_dir", base_path)); + + CefString test_path; + { + CefScopedTempDir dir; + EXPECT_TRUE(dir.CreateUniqueTempDirUnderPath(base_path)); + test_path = dir.GetPath(); + EXPECT_TRUE(CefDirectoryExists(test_path)); + EXPECT_TRUE(test_path.ToString().find(base_path.ToString()) == 0); + } + EXPECT_FALSE(CefDirectoryExists(test_path)); + CefDeleteFile(base_path, true); +} + +TEST(ScopedTempDir, MultipleInvocations) { + CefScopedTempDir dir; + EXPECT_TRUE(dir.CreateUniqueTempDir()); + EXPECT_FALSE(dir.CreateUniqueTempDir()); + EXPECT_TRUE(dir.Delete()); + EXPECT_TRUE(dir.CreateUniqueTempDir()); + EXPECT_FALSE(dir.CreateUniqueTempDir()); + CefScopedTempDir other_dir; + EXPECT_TRUE(other_dir.Set(dir.Take())); + EXPECT_TRUE(dir.CreateUniqueTempDir()); + EXPECT_FALSE(dir.CreateUniqueTempDir()); + EXPECT_FALSE(other_dir.CreateUniqueTempDir()); +} diff --git a/tests/unittests/thread_helper.cc b/tests/unittests/thread_helper.cc index 8b8dd608a..4084cf799 100644 --- a/tests/unittests/thread_helper.cc +++ b/tests/unittests/thread_helper.cc @@ -10,19 +10,20 @@ void SignalEvent(base::WaitableEvent* event) { event->Signal(); } -void WaitForThread(CefThreadId thread_id) { +void WaitForThread(CefThreadId thread_id, int64 delay_ms) { base::WaitableEvent event( base::WaitableEvent::ResetPolicy::AUTOMATIC, base::WaitableEvent::InitialState::NOT_SIGNALED); - CefPostTask(thread_id, base::Bind(SignalEvent, &event)); + CefPostDelayedTask(thread_id, base::Bind(SignalEvent, &event), delay_ms); event.Wait(); } -void WaitForThread(CefRefPtr task_runner) { +void WaitForThread(CefRefPtr task_runner, int64 delay_ms) { base::WaitableEvent event( base::WaitableEvent::ResetPolicy::AUTOMATIC, base::WaitableEvent::InitialState::NOT_SIGNALED); - task_runner->PostTask(CefCreateClosureTask(base::Bind(SignalEvent, &event))); + task_runner->PostDelayedTask( + CefCreateClosureTask(base::Bind(SignalEvent, &event)), delay_ms); event.Wait(); } diff --git a/tests/unittests/thread_helper.h b/tests/unittests/thread_helper.h index 18b71b8eb..78c1b5a79 100644 --- a/tests/unittests/thread_helper.h +++ b/tests/unittests/thread_helper.h @@ -16,12 +16,15 @@ void SignalEvent(base::WaitableEvent* event); // Post a task to the specified thread and wait for the task to execute as // indication that all previously pending tasks on that thread have completed. -void WaitForThread(CefThreadId thread_id); -void WaitForThread(CefRefPtr task_runner); +void WaitForThread(CefThreadId thread_id, int64 delay_ms = 0); +void WaitForThread(CefRefPtr task_runner, int64 delay_ms = 0); #define WaitForIOThread() WaitForThread(TID_IO) #define WaitForUIThread() WaitForThread(TID_UI) #define WaitForDBThread() WaitForThread(TID_DB) +#define WaitForIOThreadWithDelay(delay_ms) WaitForThread(TID_IO, delay_ms) +#define WaitForUIThreadWithDelay(delay_ms) WaitForThread(TID_UI, delay_ms) +#define WaitForDBThreadWithDelay(delay_ms) WaitForThread(TID_DB, delay_ms) // Assert that execution is occuring on the named thread. #define EXPECT_UI_THREAD() EXPECT_TRUE(CefCurrentlyOn(TID_UI)); diff --git a/tests/unittests/tracing_unittest.cc b/tests/unittests/tracing_unittest.cc index 4c48b3c7f..e702761e7 100644 --- a/tests/unittests/tracing_unittest.cc +++ b/tests/unittests/tracing_unittest.cc @@ -2,14 +2,15 @@ // reserved. Use of this source code is governed by a BSD-style license that // can be found in the LICENSE file. -#include "base/files/file_util.h" #include "base/synchronization/waitable_event.h" #include "include/base/cef_bind.h" +#include "include/cef_file_util.h" #include "include/cef_task.h" #include "include/cef_trace.h" #include "include/wrapper/cef_closure_task.h" #include "testing/gtest/include/gtest/gtest.h" +#include "tests/unittests/file_util.h" #include "tests/unittests/test_handler.h" // Use the CEF version of the TRACE_* macros instead of the Chromium version. @@ -80,11 +81,11 @@ class TracingTestHandler : public CefEndTracingCallback, type_(type) { } - void ReadTracingFile(const base::FilePath& file_path) { + void ReadTracingFile(const std::string& file_path) { EXPECT_FILE_THREAD(); - base::ReadFileToString(file_path, &trace_data_); - base::DeleteFile(file_path, false); + EXPECT_TRUE(file_util::ReadFileToString(file_path, &trace_data_)); + EXPECT_TRUE(CefDeleteFile(file_path, false)); completion_event_.Signal(); } @@ -93,13 +94,8 @@ class TracingTestHandler : public CefEndTracingCallback, void OnEndTracingComplete(const CefString& tracing_file) override { EXPECT_UI_THREAD(); -#if defined(OS_WIN) - base::FilePath file_path(tracing_file.ToWString()); -#else - base::FilePath file_path(tracing_file.ToString()); -#endif CefPostTask(TID_FILE, - base::Bind(&TracingTestHandler::ReadTracingFile, this, file_path)); + base::Bind(&TracingTestHandler::ReadTracingFile, this, tracing_file)); } void RunTracing() { diff --git a/tests/unittests/urlrequest_unittest.cc b/tests/unittests/urlrequest_unittest.cc index bd0358401..e9721ad41 100644 --- a/tests/unittests/urlrequest_unittest.cc +++ b/tests/unittests/urlrequest_unittest.cc @@ -5,8 +5,6 @@ #include #include -#include "base/files/file_util.h" -#include "base/files/scoped_temp_dir.h" #include "base/synchronization/waitable_event.h" #include "include/base/cef_bind.h" @@ -14,8 +12,10 @@ #include "include/cef_task.h" #include "include/cef_urlrequest.h" #include "include/wrapper/cef_closure_task.h" +#include "include/wrapper/cef_scoped_temp_dir.h" #include "testing/gtest/include/gtest/gtest.h" #include "tests/cefclient/renderer/client_app_renderer.h" +#include "tests/unittests/file_util.h" #include "tests/unittests/test_handler.h" #include "tests/unittests/test_suite.h" #include "tests/unittests/test_util.h" @@ -118,10 +118,10 @@ void SetUploadData(CefRefPtr request, } void SetUploadFile(CefRefPtr request, - const base::FilePath& file) { + const std::string& file) { CefRefPtr postData = CefPostData::Create(); CefRefPtr element = CefPostDataElement::Create(); - element->SetToFile(file.value()); + element->SetToFile(file); postData->AddElement(element); request->SetPostData(postData); } @@ -144,16 +144,20 @@ void GetUploadData(CefRefPtr request, } // Set a cookie so that we can test if it's sent with the request. -void SetTestCookie(CefRefPtr request_context, - base::WaitableEvent* event) { +void SetTestCookie(CefRefPtr request_context) { + EXPECT_TRUE(CefCurrentlyOn(TID_FILE)); + class Callback : public CefSetCookieCallback { public: explicit Callback(base::WaitableEvent* event) - : event_(event) {} + : event_(event) { + EXPECT_TRUE(event_); + } void OnComplete(bool success) override { EXPECT_TRUE(success); event_->Signal(); + event_ = nullptr; } private: @@ -162,6 +166,10 @@ void SetTestCookie(CefRefPtr request_context, IMPLEMENT_REFCOUNTING(Callback); }; + base::WaitableEvent event( + base::WaitableEvent::ResetPolicy::MANUAL, + base::WaitableEvent::InitialState::NOT_SIGNALED); + CefCookie cookie; CefString(&cookie.name) = kRequestSendCookieName; CefString(&cookie.value) = "send-cookie-value"; @@ -169,22 +177,25 @@ void SetTestCookie(CefRefPtr request_context, CefString(&cookie.path) = "/"; cookie.has_expires = false; EXPECT_TRUE(request_context->GetDefaultCookieManager(NULL)->SetCookie( - kRequestOrigin, cookie, new Callback(event))); + kRequestOrigin, cookie, new Callback(&event))); // Wait for the Callback. - event->Wait(); + event.TimedWait(base::TimeDelta::FromSeconds(2)); + EXPECT_TRUE(event.IsSignaled()); } // Tests if the save cookie has been set. If set, it will be deleted at the same // time. void GetTestCookie(CefRefPtr request_context, - base::WaitableEvent* event, bool* cookie_exists) { + EXPECT_TRUE(CefCurrentlyOn(TID_FILE)); + class Visitor : public CefCookieVisitor { public: Visitor(base::WaitableEvent* event, bool* cookie_exists) : event_(event), cookie_exists_(cookie_exists) { + EXPECT_TRUE(event_); } ~Visitor() override { event_->Signal(); @@ -208,13 +219,18 @@ void GetTestCookie(CefRefPtr request_context, IMPLEMENT_REFCOUNTING(Visitor); }; + base::WaitableEvent event( + base::WaitableEvent::ResetPolicy::MANUAL, + base::WaitableEvent::InitialState::NOT_SIGNALED); + CefRefPtr cookie_manager = request_context->GetDefaultCookieManager(NULL); cookie_manager->VisitUrlCookies( - kRequestOrigin, true, new Visitor(event, cookie_exists)); + kRequestOrigin, true, new Visitor(&event, cookie_exists)); // Wait for the Visitor. - event->Wait(); + event.TimedWait(base::TimeDelta::FromSeconds(2)); + EXPECT_TRUE(event.IsSignaled()); } @@ -554,12 +570,18 @@ class RequestClient : public CefURLRequestClient { // Executes the tests. class RequestTestRunner : public base::RefCountedThreadSafe { public: - typedef base::Callback TestCallback; + typedef base::Callback TestCallback; + // Delegate methods will be called on the same thread that constructed the + // RequestTestRunner object. class Delegate { public: - // Used to notify the handler when the test can be destroyed. - virtual void DestroyTest(const RequestRunSettings& settings) =0; + // Setup has completed. + virtual void OnRunnerSetupComplete() =0; + + // Run has completed. + virtual void OnRunnerRunComplete() =0; + protected: virtual ~Delegate() {} }; @@ -568,6 +590,10 @@ class RequestTestRunner : public base::RefCountedThreadSafe { bool is_browser_process) : delegate_(delegate), is_browser_process_(is_browser_process) { + owner_task_runner_ = CefTaskRunner::GetForCurrentThread(); + EXPECT_TRUE(owner_task_runner_.get()); + EXPECT_TRUE(owner_task_runner_->BelongsToCurrentThread()); + // Helper macro for registering test callbacks. #define REGISTER_TEST(test_mode, setup_method, run_method) \ RegisterTest(test_mode, \ @@ -588,6 +614,11 @@ class RequestTestRunner : public base::RefCountedThreadSafe { REGISTER_TEST(REQTEST_HEAD, SetupHeadTest, GenericRunTest); } + void Destroy() { + owner_task_runner_ = nullptr; + request_context_ = nullptr; + } + // Called in the browser process to set the request context that will be used // when creating the URL request. void SetRequestContext(CefRefPtr request_context) { @@ -599,30 +630,55 @@ class RequestTestRunner : public base::RefCountedThreadSafe { // Called in both the browser and render process to setup the test. void SetupTest(RequestTestMode test_mode) { + EXPECT_TRUE(owner_task_runner_->BelongsToCurrentThread()); + + const base::Closure& complete_callback = + base::Bind(&RequestTestRunner::SetupComplete, this); TestMap::const_iterator it = test_map_.find(test_mode); if (it != test_map_.end()) { - it->second.setup.Run(); - AddSchemeHandler(); + it->second.setup.Run( + base::Bind(&RequestTestRunner::SetupContinue, this, + complete_callback)); } else { // Unknown test. ADD_FAILURE(); + complete_callback.Run(); } } // Called in either the browser or render process to run the test. void RunTest(RequestTestMode test_mode) { + EXPECT_TRUE(owner_task_runner_->BelongsToCurrentThread()); + + const base::Closure& complete_callback = + base::Bind(&RequestTestRunner::RunComplete, this); TestMap::const_iterator it = test_map_.find(test_mode); if (it != test_map_.end()) { - it->second.run.Run(); + it->second.run.Run(complete_callback); } else { // Unknown test. ADD_FAILURE(); - DestroyTest(); + complete_callback.Run(); } } private: - void SetupGetTest() { + // Continued after |settings_| is populated for the test. + void SetupContinue(const base::Closure& complete_callback) { + if (!owner_task_runner_->BelongsToCurrentThread()) { + owner_task_runner_->PostTask(CefCreateClosureTask( + base::Bind(&RequestTestRunner::SetupContinue, this, + complete_callback))); + return; + } + + if (is_browser_process_) + AddSchemeHandler(); + + complete_callback.Run(); + } + + void SetupGetTestShared() { settings_.request = CefRequest::Create(); settings_.request->SetURL(MakeSchemeURL("GetTest.html")); settings_.request->SetMethod("GET"); @@ -635,30 +691,39 @@ class RequestTestRunner : public base::RefCountedThreadSafe { settings_.response_data = "GET TEST SUCCESS"; } - void SetupGetNoDataTest() { + void SetupGetTest(const base::Closure& complete_callback) { + SetupGetTestShared(); + complete_callback.Run(); + } + + void SetupGetNoDataTest(const base::Closure& complete_callback) { // Start with the normal get test. - SetupGetTest(); + SetupGetTestShared(); // Disable download data notifications. settings_.request->SetFlags(UR_FLAG_NO_DOWNLOAD_DATA); settings_.expect_download_data = false; + + complete_callback.Run(); } - void SetupGetAllowCookiesTest() { + void SetupGetAllowCookiesTest(const base::Closure& complete_callback) { // Start with the normal get test. - SetupGetTest(); + SetupGetTestShared(); // Send cookies. settings_.request->SetFlags(UR_FLAG_ALLOW_CACHED_CREDENTIALS); settings_.expect_save_cookie = true; settings_.expect_send_cookie = true; + + complete_callback.Run(); } - void SetupGetRedirectTest() { + void SetupGetRedirectTest(const base::Closure& complete_callback) { // Start with the normal get test. - SetupGetTest(); + SetupGetTestShared(); // Add a redirect request. settings_.redirect_request = CefRequest::Create(); @@ -673,9 +738,11 @@ class RequestTestRunner : public base::RefCountedThreadSafe { CefResponse::HeaderMap headerMap; headerMap.insert(std::make_pair("Location", settings_.request->GetURL())); settings_.redirect_response->SetHeaderMap(headerMap); + + complete_callback.Run(); } - void SetupGetReferrerTest() { + void SetupGetReferrerTest(const base::Closure& complete_callback) { settings_.request = CefRequest::Create(); settings_.request->SetURL(MakeSchemeURL("GetTest.html")); settings_.request->SetMethod("GET"); @@ -692,9 +759,11 @@ class RequestTestRunner : public base::RefCountedThreadSafe { settings_.response->SetStatusText("OK"); settings_.response_data = "GET TEST SUCCESS"; + + complete_callback.Run(); } - void SetupPostTest() { + void SetupPostTestShared() { settings_.request = CefRequest::Create(); settings_.request->SetURL(MakeSchemeURL("PostTest.html")); settings_.request->SetMethod("POST"); @@ -708,38 +777,58 @@ class RequestTestRunner : public base::RefCountedThreadSafe { settings_.response_data = "POST TEST SUCCESS"; } - void SetupPostFileTest() { + void SetupPostTest(const base::Closure& complete_callback) { + SetupPostTestShared(); + complete_callback.Run(); + } + + void SetupPostFileTest(const base::Closure& complete_callback) { + // This test is only supported in the browser process. + EXPECT_TRUE(is_browser_process_); + settings_.request = CefRequest::Create(); settings_.request->SetURL(MakeSchemeURL("PostFileTest.html")); settings_.request->SetMethod("POST"); - EXPECT_TRUE(post_file_tmpdir_.CreateUniqueTempDir()); - const base::FilePath& path = - post_file_tmpdir_.GetPath().Append(FILE_PATH_LITERAL("example.txt")); - const char content[] = "HELLO FRIEND!"; - int write_ct = base::WriteFile(path, content, sizeof(content) - 1); - EXPECT_EQ(static_cast(sizeof(content) - 1), write_ct); - SetUploadFile(settings_.request, path); - settings_.response = CefResponse::Create(); settings_.response->SetMimeType("text/html"); settings_.response->SetStatus(200); settings_.response->SetStatusText("OK"); settings_.response_data = "POST TEST SUCCESS"; + + CefPostTask(TID_FILE, + base::Bind(&RequestTestRunner::SetupPostFileTestContinue, this, + complete_callback)); } - void SetupPostWithProgressTest() { + void SetupPostFileTestContinue(const base::Closure& complete_callback) { + EXPECT_TRUE(CefCurrentlyOn(TID_FILE)); + + EXPECT_TRUE(post_file_tmpdir_.CreateUniqueTempDir()); + const std::string& path = + file_util::JoinPath(post_file_tmpdir_.GetPath(), "example.txt"); + const char content[] = "HELLO FRIEND!"; + int write_ct = file_util::WriteFile(path, content, sizeof(content) - 1); + EXPECT_EQ(static_cast(sizeof(content) - 1), write_ct); + SetUploadFile(settings_.request, path); + + complete_callback.Run(); + } + + void SetupPostWithProgressTest(const base::Closure& complete_callback) { // Start with the normal post test. - SetupPostTest(); + SetupPostTestShared(); // Enable upload progress notifications. settings_.request->SetFlags(UR_FLAG_REPORT_UPLOAD_PROGRESS); settings_.expect_upload_progress = true; + + complete_callback.Run(); } - void SetupHeadTest() { + void SetupHeadTest(const base::Closure& complete_callback) { settings_.request = CefRequest::Create(); settings_.request->SetURL(MakeSchemeURL("HeadTest.html")); settings_.request->SetMethod("HEAD"); @@ -754,34 +843,37 @@ class RequestTestRunner : public base::RefCountedThreadSafe { settings_.expect_download_progress = false; settings_.expect_download_data = false; + + complete_callback.Run(); } // Generic test runner. - void GenericRunTest() { + void GenericRunTest(const base::Closure& complete_callback) { class Test : public RequestClient::Delegate { public: - Test(scoped_refptr runner, - const RequestRunSettings& settings) - : runner_(runner), - settings_(settings) { + Test(const RequestRunSettings& settings, + const base::Closure& complete_callback) + : settings_(settings), + complete_callback_(complete_callback) { + EXPECT_FALSE(complete_callback_.is_null()); } void OnRequestComplete(CefRefPtr client) override { CefRefPtr expected_request; CefRefPtr expected_response; - if (runner_->settings_.redirect_request.get()) - expected_request = runner_->settings_.redirect_request; + if (settings_.redirect_request.get()) + expected_request = settings_.redirect_request; else - expected_request = runner_->settings_.request; + expected_request = settings_.request; - if (runner_->settings_.redirect_response.get() && - !runner_->settings_.expect_follow_redirect) { + if (settings_.redirect_response.get() && + !settings_.expect_follow_redirect) { // A redirect response was sent but the redirect is not expected to be // followed. - expected_response = runner_->settings_.redirect_response; + expected_response = settings_.redirect_response; } else { - expected_response = runner_->settings_.response; + expected_response = settings_.response; } TestRequestEqual(expected_request, client->request_, false); @@ -805,7 +897,7 @@ class RequestTestRunner : public base::RefCountedThreadSafe { if (settings_.expect_download_progress) { EXPECT_LE(1, client->download_progress_ct_); - EXPECT_EQ(runner_->settings_.response_data.size(), + EXPECT_EQ(settings_.response_data.size(), client->download_total_); } else { EXPECT_EQ(0, client->download_progress_ct_); @@ -814,19 +906,20 @@ class RequestTestRunner : public base::RefCountedThreadSafe { if (settings_.expect_download_data) { EXPECT_LE(1, client->download_data_ct_); - EXPECT_STREQ(runner_->settings_.response_data.c_str(), + EXPECT_STREQ(settings_.response_data.c_str(), client->download_data_.c_str()); } else { EXPECT_EQ(0, client->download_data_ct_); EXPECT_TRUE(client->download_data_.empty()); } - runner_->DestroyTest(); + complete_callback_.Run(); + complete_callback_.Reset(); } private: - scoped_refptr runner_; RequestRunSettings settings_; + base::Closure complete_callback_; }; CefRefPtr request; @@ -837,7 +930,7 @@ class RequestTestRunner : public base::RefCountedThreadSafe { EXPECT_TRUE(request.get()); CefRefPtr client = - new RequestClient(new Test(this, settings_)); + new RequestClient(new Test(settings_, complete_callback)); CefURLRequest::Create(request, client.get(), request_context_); } @@ -850,19 +943,56 @@ class RequestTestRunner : public base::RefCountedThreadSafe { test_map_.insert(std::make_pair(test_mode, entry)); } + void SetupComplete() { + if (!owner_task_runner_->BelongsToCurrentThread()) { + owner_task_runner_->PostTask(CefCreateClosureTask( + base::Bind(&RequestTestRunner::SetupComplete, this))); + return; + } + + delegate_->OnRunnerSetupComplete(); + } + // Destroy the current test. Called when the test is complete. - void DestroyTest() { + void RunComplete() { + if (!post_file_tmpdir_.IsEmpty()) { + EXPECT_TRUE(is_browser_process_); + CefPostTask(TID_FILE, + base::Bind(&RequestTestRunner::RunCompleteDeleteTempDirectory, this)); + return; + } + + // Continue with test completion. + RunCompleteContinue(); + } + + void RunCompleteDeleteTempDirectory() { + EXPECT_TRUE(CefCurrentlyOn(TID_FILE)); + + EXPECT_TRUE(post_file_tmpdir_.Delete()); + EXPECT_TRUE(post_file_tmpdir_.IsEmpty()); + + // Continue with test completion. + RunCompleteContinue(); + } + + void RunCompleteContinue() { + if (!owner_task_runner_->BelongsToCurrentThread()) { + owner_task_runner_->PostTask(CefCreateClosureTask( + base::Bind(&RequestTestRunner::RunCompleteContinue, this))); + return; + } + if (scheme_factory_.get()) { + EXPECT_TRUE(is_browser_process_); + // Remove the factory registration. request_context_->RegisterSchemeHandlerFactory( kRequestScheme, kRequestHost, NULL); scheme_factory_ = NULL; } - if (post_file_tmpdir_.IsValid()) - EXPECT_TRUE(post_file_tmpdir_.Delete()); - - delegate_->DestroyTest(settings_); + delegate_->OnRunnerRunComplete(); } // Return an appropriate scheme URL for the specified |path|. @@ -875,8 +1005,7 @@ class RequestTestRunner : public base::RefCountedThreadSafe { // Add a scheme handler for the current test. Called during test setup. void AddSchemeHandler() { // Scheme handlers are only registered in the browser process. - if (!is_browser_process_) - return; + EXPECT_TRUE(is_browser_process_); if (!scheme_factory_.get()) { // Add the factory registration. @@ -898,6 +1027,12 @@ class RequestTestRunner : public base::RefCountedThreadSafe { Delegate* delegate_; bool is_browser_process_; + + // Primary thread runner for the object that owns us. In the browser process + // this will be the UI thread and in the renderer process this will be the + // RENDERER thread. + CefRefPtr owner_task_runner_; + CefRefPtr request_context_; struct TestEntry { @@ -910,7 +1045,7 @@ class RequestTestRunner : public base::RefCountedThreadSafe { std::string scheme_name_; CefRefPtr scheme_factory_; - base::ScopedTempDir post_file_tmpdir_; + CefScopedTempDir post_file_tmpdir_; public: RequestRunSettings settings_; @@ -920,8 +1055,7 @@ class RequestTestRunner : public base::RefCountedThreadSafe { class RequestRendererTest : public ClientAppRenderer::Delegate, public RequestTestRunner::Delegate { public: - RequestRendererTest() - : test_runner_(new RequestTestRunner(this, false)) { + RequestRendererTest() { } bool OnProcessMessageReceived( @@ -930,18 +1064,20 @@ class RequestRendererTest : public ClientAppRenderer::Delegate, CefProcessId source_process, CefRefPtr message) override { if (message->GetName() == kRequestTestMsg) { + EXPECT_TRUE(CefCurrentlyOn(TID_RENDERER)); + app_ = app; browser_ = browser; - RequestTestMode test_mode = + test_mode_ = static_cast(message->GetArgumentList()->GetInt(0)); + test_runner_ = new RequestTestRunner(this, false); + // Setup the test. This will create the objects that we test against but // not register any scheme handlers (because we're in the render process). - test_runner_->SetupTest(test_mode); + test_runner_->SetupTest(test_mode_); - // Run the test. - test_runner_->RunTest(test_mode); return true; } @@ -949,9 +1085,18 @@ class RequestRendererTest : public ClientAppRenderer::Delegate, return false; } - protected: + private: + void OnRunnerSetupComplete() override { + EXPECT_TRUE(CefCurrentlyOn(TID_RENDERER)); + + // Run the test. + test_runner_->RunTest(test_mode_); + } + // Return from the test. - void DestroyTest(const RequestRunSettings& settings) override { + void OnRunnerRunComplete() override { + EXPECT_TRUE(CefCurrentlyOn(TID_RENDERER)); + // Check if the test has failed. bool result = !TestFailed(); @@ -967,6 +1112,7 @@ class RequestRendererTest : public ClientAppRenderer::Delegate, CefRefPtr app_; CefRefPtr browser_; + RequestTestMode test_mode_; scoped_refptr test_runner_; @@ -977,9 +1123,6 @@ class RequestRendererTest : public ClientAppRenderer::Delegate, class RequestTestHandler : public TestHandler, public RequestTestRunner::Delegate { public: - // Don't hide the DestroyTest method. - using TestHandler::DestroyTest; - RequestTestHandler(RequestTestMode test_mode, ContextTestMode context_mode, bool test_in_browser, @@ -987,11 +1130,40 @@ class RequestTestHandler : public TestHandler, : test_mode_(test_mode), context_mode_(context_mode), test_in_browser_(test_in_browser), - test_url_(test_url), - test_runner_(new RequestTestRunner(this, true)) { + test_url_(test_url) { } void RunTest() override { + // Time out the test after a reasonable period of time. + SetTestTimeout(); + + // Start pre-setup actions. + PreSetupStart(); + } + + void PreSetupStart() { + CefPostTask(TID_FILE, + base::Bind(&RequestTestHandler::PreSetupFileTasks, this)); + } + + void PreSetupFileTasks() { + EXPECT_TRUE(CefCurrentlyOn(TID_FILE)); + + if (context_mode_ == CONTEXT_ONDISK) { + EXPECT_TRUE(context_tmpdir_.CreateUniqueTempDir()); + context_tmpdir_path_ = context_tmpdir_.GetPath(); + EXPECT_FALSE(context_tmpdir_path_.empty()); + } + + CefPostTask(TID_UI, + base::Bind(&RequestTestHandler::PreSetupContinue, this)); + } + + void PreSetupContinue() { + EXPECT_TRUE(CefCurrentlyOn(TID_UI)); + + test_runner_ = new RequestTestRunner(this, true); + // Get or create the request context. if (context_mode_ == CONTEXT_GLOBAL) { CefRefPtr request_context = @@ -999,8 +1171,7 @@ class RequestTestHandler : public TestHandler, EXPECT_TRUE(request_context.get()); test_runner_->SetRequestContext(request_context); - // Continue the test now. - RunTestContinue(); + PreSetupComplete(); } else { // Don't end the test until the temporary request context has been // destroyed. @@ -1009,8 +1180,8 @@ class RequestTestHandler : public TestHandler, CefRequestContextSettings settings; if (context_mode_ == CONTEXT_ONDISK) { - EXPECT_TRUE(context_tmpdir_.CreateUniqueTempDir()); - CefString(&settings.cache_path) = context_tmpdir_.GetPath().value(); + EXPECT_FALSE(context_tmpdir_.IsEmpty()); + CefString(&settings.cache_path) = context_tmpdir_path_; } // Create a new temporary request context. @@ -1026,25 +1197,47 @@ class RequestTestHandler : public TestHandler, // Continue the test once supported schemes has been set. request_context->GetDefaultCookieManager(NULL)->SetSupportedSchemes( - supported_schemes, new SupportedSchemesCompletionCallback(this)); + supported_schemes, + new SupportedSchemesCompletionCallback( + base::Bind(&RequestTestHandler::PreSetupComplete, this))); } } - void RunTestContinue() { + void PreSetupComplete() { if (!CefCurrentlyOn(TID_UI)) { CefPostTask(TID_UI, - base::Bind(&RequestTestHandler::RunTestContinue, this)); + base::Bind(&RequestTestHandler::PreSetupComplete, this)); return; } // Setup the test. This will create the objects that we test against and // register any scheme handlers. test_runner_->SetupTest(test_mode_); + } - base::WaitableEvent event( - base::WaitableEvent::ResetPolicy::AUTOMATIC, - base::WaitableEvent::InitialState::NOT_SIGNALED); - SetTestCookie(test_runner_->GetRequestContext(), &event); + // Browser process setup is complete. + void OnRunnerSetupComplete() override { + // Start post-setup actions. + PostSetupStart(); + } + + void PostSetupStart() { + CefPostTask(TID_FILE, + base::Bind(&RequestTestHandler::PostSetupFileTasks, this)); + } + + void PostSetupFileTasks() { + EXPECT_TRUE(CefCurrentlyOn(TID_FILE)); + + // Don't use WaitableEvent on the UI thread. + SetTestCookie(test_runner_->GetRequestContext()); + + CefPostTask(TID_UI, + base::Bind(&RequestTestHandler::PostSetupComplete, this)); + } + + void PostSetupComplete() { + EXPECT_TRUE(CefCurrentlyOn(TID_UI)); if (test_in_browser_) { // Run the test now. @@ -1056,9 +1249,6 @@ class RequestTestHandler : public TestHandler, // Create a browser to run the test in the renderer process. CreateBrowser(test_url_, test_runner_->GetRequestContext()); } - - // Time out the test after a reasonable period of time. - SetTestTimeout(); } void OnLoadEnd(CefRefPtr browser, @@ -1090,21 +1280,39 @@ class RequestTestHandler : public TestHandler, if (message->GetArgumentList()->GetBool(0)) got_success_.yes(); - // Test is complete. - DestroyTest(test_runner_->settings_); + // Renderer process test is complete. + PostRunStart(); return true; } - void DestroyTest(const RequestRunSettings& settings) override { - base::WaitableEvent event( - base::WaitableEvent::ResetPolicy::AUTOMATIC, - base::WaitableEvent::InitialState::NOT_SIGNALED); + // Browser process test is complete. + void OnRunnerRunComplete() override { + PostRunStart(); + } + void PostRunStart() { + CefPostTask(TID_FILE, + base::Bind(&RequestTestHandler::PostRunFileTasks, this)); + } + + void PostRunFileTasks() { + EXPECT_TRUE(CefCurrentlyOn(TID_FILE)); + + // Don't use WaitableEvent on the UI thread. bool has_save_cookie = false; - GetTestCookie(test_runner_->GetRequestContext(), &event, &has_save_cookie); - EXPECT_EQ(settings.expect_save_cookie, has_save_cookie); + GetTestCookie(test_runner_->GetRequestContext(), &has_save_cookie); + EXPECT_EQ(test_runner_->settings_.expect_save_cookie, has_save_cookie); + CefPostTask(TID_UI, base::Bind(&RequestTestHandler::PostRunComplete, this)); + } + + void PostRunComplete() { + EXPECT_TRUE(CefCurrentlyOn(TID_UI)); + DestroyTest(); + } + + void DestroyTest() override { TestHandler::DestroyTest(); // Need to call TestComplete() explicitly if testing in the browser and @@ -1116,10 +1324,37 @@ class RequestTestHandler : public TestHandler, // Release our reference to the context. Do not access any object members // after this call because |this| might be deleted. - test_runner_->SetRequestContext(NULL); + test_runner_->Destroy(); if (call_test_complete) + OnTestComplete(); + } + + void OnTestComplete() { + if (!CefCurrentlyOn(TID_UI)) { + CefPostTask(TID_UI, + base::Bind(&RequestTestHandler::OnTestComplete, this)); + return; + } + + if (!context_tmpdir_.IsEmpty()) { + // Wait a bit for cache file handles to close after browser or request + // context destruction. + CefPostDelayedTask(TID_FILE, + base::Bind(&RequestTestHandler::PostTestCompleteFileTasks, this), + 100); + } else { TestComplete(); + } + } + + void PostTestCompleteFileTasks() { + EXPECT_TRUE(CefCurrentlyOn(TID_FILE)); + + EXPECT_TRUE(context_tmpdir_.Delete()); + EXPECT_TRUE(context_tmpdir_.IsEmpty()); + + CefPostTask(TID_UI, base::Bind(&RequestTestHandler::TestComplete, this)); } private: @@ -1131,7 +1366,7 @@ class RequestTestHandler : public TestHandler, : test_handler_(test_handler) { } ~RequestContextHandler() override { - test_handler_->TestComplete(); + test_handler_->OnTestComplete(); } private: @@ -1144,15 +1379,18 @@ class RequestTestHandler : public TestHandler, class SupportedSchemesCompletionCallback : public CefCompletionCallback { public: explicit SupportedSchemesCompletionCallback( - CefRefPtr test_handler) - : test_handler_(test_handler) { + const base::Closure& complete_callback) + : complete_callback_(complete_callback) { + EXPECT_FALSE(complete_callback_.is_null()); } + void OnComplete() override { - test_handler_->RunTestContinue(); + complete_callback_.Run(); + complete_callback_.Reset(); } private: - CefRefPtr test_handler_; + base::Closure complete_callback_; IMPLEMENT_REFCOUNTING(SupportedSchemesCompletionCallback); }; @@ -1164,7 +1402,8 @@ class RequestTestHandler : public TestHandler, scoped_refptr test_runner_; - base::ScopedTempDir context_tmpdir_; + CefScopedTempDir context_tmpdir_; + CefString context_tmpdir_path_; public: // Only used when the test runs in the render process.