diff --git a/DEPS b/DEPS index 3b7610a3..30f50f7 100644 --- a/DEPS +++ b/DEPS
@@ -40,11 +40,11 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling Skia # and whatever else without interference from each other. - 'skia_revision': 'b37cb236c3a698b45a7d0deca0df32bb70bbaaa6', + 'skia_revision': '8b8c76506a9a3b07246e6c2770e35dfc413da97a', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. - 'v8_revision': '37fd9c202d53958f1a6d3c7b1bdedddb57771c62', + 'v8_revision': '1ab705a20be45dadd4687b494328f4f745e9bf50', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling swarming_client # and whatever else without interference from each other. @@ -96,7 +96,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling catapult # and whatever else without interference from each other. - 'catapult_revision': '5c71aaccc396cfbd0383a2e09d832aa198e5ae0d', + 'catapult_revision': '5a31d0e078e186c1a1dda91ff452c13023ab7c41', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libFuzzer # and whatever else without interference from each other.
diff --git a/ash/content/screen_orientation_delegate_chromeos.cc b/ash/content/screen_orientation_delegate_chromeos.cc index ab9a4c2..76243b2 100644 --- a/ash/content/screen_orientation_delegate_chromeos.cc +++ b/ash/content/screen_orientation_delegate_chromeos.cc
@@ -7,17 +7,16 @@ #include "ash/common/wm_window.h" #include "ash/display/screen_orientation_controller_chromeos.h" #include "ash/shell.h" -#include "content/public/browser/screen_orientation_provider.h" #include "content/public/browser/web_contents.h" namespace ash { ScreenOrientationDelegateChromeos::ScreenOrientationDelegateChromeos() { - content::ScreenOrientationProvider::SetDelegate(this); + content::WebContents::SetScreenOrientationDelegate(this); } ScreenOrientationDelegateChromeos::~ScreenOrientationDelegateChromeos() { - content::ScreenOrientationProvider::SetDelegate(nullptr); + content::WebContents::SetScreenOrientationDelegate(nullptr); } bool ScreenOrientationDelegateChromeos::FullScreenRequired(
diff --git a/build/android/gradle/android.jinja b/build/android/gradle/android.jinja index b4b9340..5d6949f 100644 --- a/build/android/gradle/android.jinja +++ b/build/android/gradle/android.jinja
@@ -10,6 +10,11 @@ "{{ path }}", {% endfor %} ] + java.filter.exclude( +{% for path in variables.java_excludes %} + "{{ path }}", +{% endfor %} + ) jniLibs.srcDirs = [ {% for path in variables.jni_libs %} "{{ path }}",
diff --git a/build/android/gradle/generate_gradle.py b/build/android/gradle/generate_gradle.py index fbe0966..6e1ecb7e 100755 --- a/build/android/gradle/generate_gradle.py +++ b/build/android/gradle/generate_gradle.py
@@ -7,6 +7,7 @@ import argparse import codecs +import glob import logging import os import re @@ -30,7 +31,6 @@ _DEFAULT_ANDROID_MANIFEST_PATH = os.path.join( host_paths.DIR_SOURCE_ROOT, 'build', 'android', 'AndroidManifest.xml') _FILE_DIR = os.path.dirname(__file__) -_JAVA_SUBDIR = 'symlinked-java' _SRCJARS_SUBDIR = 'extracted-srcjars' _JNI_LIBS_SUBDIR = 'symlinked-libs' _ARMEABI_SUBDIR = 'armeabi' @@ -204,13 +204,12 @@ return jni_libs def _GenJavaDirs(self, entry): - java_dirs = _CreateJavaSourceDir( - constants.GetOutDirectory(), self.EntryOutputDir(entry), - entry.JavaFiles()) + java_dirs, excludes = _CreateJavaSourceDir( + constants.GetOutDirectory(), entry.JavaFiles()) if self.Srcjars(entry): java_dirs.append( os.path.join(self.EntryOutputDir(entry), _SRCJARS_SUBDIR)) - return java_dirs + return java_dirs, excludes def _Relativize(self, entry, paths): return _RebasePath(paths, self.EntryOutputDir(entry)) @@ -238,7 +237,9 @@ 'android_manifest', _DEFAULT_ANDROID_MANIFEST_PATH) variables['android_manifest'] = self._Relativize( entry, android_test_manifest) - variables['java_dirs'] = self._Relativize(entry, self._GenJavaDirs(entry)) + java_dirs, excludes = self._GenJavaDirs(entry) + variables['java_dirs'] = self._Relativize(entry, java_dirs) + variables['java_excludes'] = excludes variables['jni_libs'] = self._Relativize(entry, self._GenJniLibs(entry)) deps = [_ProjectEntry.FromBuildConfigPath(p) for p in entry.Gradle()['dependent_android_projects']] @@ -254,8 +255,8 @@ def _ComputeJavaSourceDirs(java_files): - """Returns the list of source directories for the given files.""" - found_roots = set() + """Returns a dictionary of source dirs with each given files in one.""" + found_roots = {} for path in java_files: path_root = path # Recognize these tokens as top-level. @@ -268,8 +269,68 @@ if basename in ('javax', 'org', 'com'): path_root = os.path.dirname(path_root) break - found_roots.add(path_root) - return list(found_roots) + if path_root not in found_roots: + found_roots[path_root] = [] + found_roots[path_root].append(path) + return found_roots + + +def _ComputeExcludeFilters(wanted_files, unwanted_files, parent_dir): + """Returns exclude patters to exclude unwanted files but keep wanted files. + + - Shortens exclude list by globbing if possible. + - Exclude patterns are relative paths from the parent directory. + """ + excludes = [] + files_to_include = set(wanted_files) + files_to_exclude = set(unwanted_files) + while files_to_exclude: + unwanted_file = files_to_exclude.pop() + target_exclude = os.path.join( + os.path.dirname(unwanted_file), '*.java') + found_files = set(glob.glob(target_exclude)) + valid_files = found_files & files_to_include + if valid_files: + excludes.append(os.path.relpath(unwanted_file, parent_dir)) + else: + excludes.append(os.path.relpath(target_exclude, parent_dir)) + files_to_exclude -= found_files + return excludes + + +def _CreateJavaSourceDir(output_dir, java_files): + """Computes the list of java source directories and exclude patterns. + + 1. Computes the root java source directories from the list of files. + 2. Compute exclude patterns that exclude all extra files only. + 3. Returns the list of java source directories and exclude patterns. + """ + java_dirs = [] + excludes = [] + if java_files: + java_files = _RebasePath(java_files) + computed_dirs = _ComputeJavaSourceDirs(java_files) + java_dirs = computed_dirs.keys() + all_found_java_files = set() + + for directory, files in computed_dirs.iteritems(): + found_java_files = build_utils.FindInDirectory(directory, '*.java') + all_found_java_files.update(found_java_files) + unwanted_java_files = set(found_java_files) - set(files) + if unwanted_java_files: + logging.debug('Directory requires excludes: %s', directory) + excludes.extend( + _ComputeExcludeFilters(files, unwanted_java_files, directory)) + + missing_java_files = set(java_files) - all_found_java_files + # Warn only about non-generated files that are missing. + missing_java_files = [p for p in missing_java_files + if not p.startswith(output_dir)] + if missing_java_files: + logging.warning( + 'Some java files were not found: %s', missing_java_files) + + return java_dirs, excludes def _CreateRelativeSymlink(target_path, link_path): @@ -279,60 +340,6 @@ os.symlink(relpath, link_path) -def _CreateSymlinkTree(entry_output_dir, symlink_dir, desired_files, - parent_dirs): - """Creates a directory tree of symlinks to the given files. - - The idea here is to replicate a directory tree while leaving out files within - it not listed by |desired_files|. - """ - assert _IsSubpathOf(symlink_dir, entry_output_dir) - - for target_path in desired_files: - prefix = next(d for d in parent_dirs if target_path.startswith(d)) - subpath = os.path.relpath(target_path, prefix) - symlinked_path = os.path.join(symlink_dir, subpath) - symlinked_dir = os.path.dirname(symlinked_path) - if not os.path.exists(symlinked_dir): - os.makedirs(symlinked_dir) - _CreateRelativeSymlink(target_path, symlinked_path) - - -def _CreateJavaSourceDir(output_dir, entry_output_dir, java_files): - """Computes and constructs when necessary the list of java source directories. - - 1. Computes the root java source directories from the list of files. - 2. Determines whether there are any .java files in them that are not included - in |java_files|. - 3. If not, returns the list of java source directories. If so, constructs a - tree of symlinks within |entry_output_dir| of all files in |java_files|. - """ - java_dirs = [] - if java_files: - java_files = _RebasePath(java_files) - java_dirs = _ComputeJavaSourceDirs(java_files) - - found_java_files = build_utils.FindInDirectories(java_dirs, '*.java') - unwanted_java_files = set(found_java_files) - set(java_files) - missing_java_files = set(java_files) - set(found_java_files) - # Warn only about non-generated files that are missing. - missing_java_files = [p for p in missing_java_files - if not p.startswith(output_dir)] - - symlink_dir = os.path.join(entry_output_dir, _JAVA_SUBDIR) - shutil.rmtree(symlink_dir, True) - - if unwanted_java_files: - logging.debug('Target requires .java symlinks: %s', entry_output_dir) - _CreateSymlinkTree(entry_output_dir, symlink_dir, java_files, java_dirs) - java_dirs = [symlink_dir] - - if missing_java_files: - logging.warning('Some java files were not found: %s', missing_java_files) - - return java_dirs - - def _CreateJniLibsDir(output_dir, entry_output_dir, so_files): """Creates directory with symlinked .so files if necessary.
diff --git a/chrome/browser/chrome_content_utility_manifest_overlay.json b/chrome/browser/chrome_content_utility_manifest_overlay.json index a6fbc74..0e6f19a 100644 --- a/chrome/browser/chrome_content_utility_manifest_overlay.json +++ b/chrome/browser/chrome_content_utility_manifest_overlay.json
@@ -9,6 +9,7 @@ "chrome::mojom::ResourceUsageReporter", "chrome::mojom::ShellHandler", "extensions::mojom::MediaParser", + "extensions::mojom::RemovableStorageWriter", "extensions::mojom::WiFiCredentialsGetter", "net::interfaces::ProxyResolverFactory", "safe_json::mojom::SafeJsonParser"
diff --git a/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc b/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc index 472707f..d73b3878 100644 --- a/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc +++ b/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc
@@ -688,11 +688,11 @@ // Some permissions take the form of a dictionary. See |kSafePermissionStrings| // for permission strings (and for more documentation). const char* const kSafePermissionDicts[] = { - // Dictionary form of the above "fileSystem" permission string. + // Dictionary forms of the above permission strings. "fileSystem", - - // Just another type of connectivity. + "mediaGalleries", "socket", + "usbDevices", }; // List of safe entries for the "app" dict in manifest.
diff --git a/chrome/browser/extensions/api/image_writer_private/image_writer_utility_client.cc b/chrome/browser/extensions/api/image_writer_private/image_writer_utility_client.cc index e64605a..30ab7cd 100644 --- a/chrome/browser/extensions/api/image_writer_private/image_writer_utility_client.cc +++ b/chrome/browser/extensions/api/image_writer_private/image_writer_utility_client.cc
@@ -2,43 +2,84 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/bind.h" -#include "base/location.h" -#include "base/strings/stringprintf.h" -#include "base/threading/thread_task_runner_handle.h" -#include "build/build_config.h" -#include "chrome/browser/browser_process.h" #include "chrome/browser/extensions/api/image_writer_private/image_writer_utility_client.h" -#include "chrome/common/extensions/chrome_utility_extensions_messages.h" + +#include "base/bind.h" +#include "base/files/file_path.h" +#include "base/location.h" +#include "base/memory/ptr_util.h" +#include "base/optional.h" +#include "base/single_thread_task_runner.h" +#include "base/threading/thread_task_runner_handle.h" +#include "chrome/common/extensions/removable_storage_writer.mojom.h" #include "chrome/grit/generated_resources.h" #include "content/public/browser/browser_thread.h" +#include "content/public/browser/utility_process_mojo_client.h" +#include "mojo/public/cpp/bindings/binding.h" #include "ui/base/l10n/l10n_util.h" -using content::BrowserThread; +class ImageWriterUtilityClient::RemovableStorageWriterClientImpl + : public extensions::mojom::RemovableStorageWriterClient { + public: + RemovableStorageWriterClientImpl( + ImageWriterUtilityClient* owner, + extensions::mojom::RemovableStorageWriterClientPtr* interface) + : binding_(this, interface), image_writer_utility_client_(owner) { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + + binding_.set_connection_error_handler( + base::Bind(&ImageWriterUtilityClient::UtilityProcessError, + image_writer_utility_client_)); + } + + ~RemovableStorageWriterClientImpl() override = default; + + private: + void Progress(int64_t progress) override { + image_writer_utility_client_->OperationProgress(progress); + } + + void Complete(const base::Optional<std::string>& error) override { + if (error) { + image_writer_utility_client_->OperationFailed(error.value()); + } else { + image_writer_utility_client_->OperationSucceeded(); + } + } + + mojo::Binding<extensions::mojom::RemovableStorageWriterClient> binding_; + // |image_writer_utility_client_| owns |this|. + ImageWriterUtilityClient* const image_writer_utility_client_; + + DISALLOW_COPY_AND_ASSIGN(RemovableStorageWriterClientImpl); +}; ImageWriterUtilityClient::ImageWriterUtilityClient() : task_runner_(base::ThreadTaskRunnerHandle::Get()) { } -ImageWriterUtilityClient::~ImageWriterUtilityClient() {} + +ImageWriterUtilityClient::~ImageWriterUtilityClient() = default; void ImageWriterUtilityClient::Write(const ProgressCallback& progress_callback, const SuccessCallback& success_callback, const ErrorCallback& error_callback, const base::FilePath& source, const base::FilePath& target) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - - StartHost(); + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + DCHECK(!removable_storage_writer_client_); progress_callback_ = progress_callback; success_callback_ = success_callback; error_callback_ = error_callback; - if (!Send(new ChromeUtilityMsg_ImageWriter_Write(source, target))) { - DLOG(ERROR) << "Unable to send Write message to Utility Process."; - task_runner_->PostTask( - FROM_HERE, base::Bind(error_callback_, "IPC communication failed")); - } + StartUtilityProcess(); + + extensions::mojom::RemovableStorageWriterClientPtr client; + removable_storage_writer_client_ = + base::MakeUnique<RemovableStorageWriterClientImpl>(this, &client); + + utility_process_mojo_client_->service()->Write(source, target, + std::move(client)); } void ImageWriterUtilityClient::Verify(const ProgressCallback& progress_callback, @@ -46,124 +87,91 @@ const ErrorCallback& error_callback, const base::FilePath& source, const base::FilePath& target) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - - StartHost(); + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + DCHECK(!removable_storage_writer_client_); progress_callback_ = progress_callback; success_callback_ = success_callback; error_callback_ = error_callback; - if (!Send(new ChromeUtilityMsg_ImageWriter_Verify(source, target))) { - DLOG(ERROR) << "Unable to send Verify message to Utility Process."; - task_runner_->PostTask( - FROM_HERE, base::Bind(error_callback_, "IPC communication failed")); - } + StartUtilityProcess(); + + extensions::mojom::RemovableStorageWriterClientPtr client; + removable_storage_writer_client_ = + base::MakeUnique<RemovableStorageWriterClientImpl>(this, &client); + + utility_process_mojo_client_->service()->Verify(source, target, + std::move(client)); } void ImageWriterUtilityClient::Cancel(const CancelCallback& cancel_callback) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); - if (!utility_process_host_) { - // If we haven't connected, there is nothing to cancel. - task_runner_->PostTask(FROM_HERE, cancel_callback); - return; - } - - cancel_callback_ = cancel_callback; - - if (!Send(new ChromeUtilityMsg_ImageWriter_Cancel())) { - DLOG(ERROR) << "Unable to send Cancel message to Utility Process."; - } + ResetRequest(); + task_runner_->PostTask(FROM_HERE, cancel_callback); } void ImageWriterUtilityClient::Shutdown() { - if (utility_process_host_ && - Send(new ChromeUtilityMsg_ImageWriter_Cancel())) { - utility_process_host_->EndBatchMode(); - } + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + + ResetRequest(); + utility_process_mojo_client_.reset(); +} + +void ImageWriterUtilityClient::StartUtilityProcess() { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + + if (utility_process_mojo_client_) + return; + + const base::string16 utility_process_name = + l10n_util::GetStringUTF16(IDS_UTILITY_PROCESS_IMAGE_WRITER_NAME); + + utility_process_mojo_client_.reset( + new content::UtilityProcessMojoClient< + extensions::mojom::RemovableStorageWriter>(utility_process_name)); + utility_process_mojo_client_->set_error_callback( + base::Bind(&ImageWriterUtilityClient::UtilityProcessError, this)); + + utility_process_mojo_client_->set_disable_sandbox(); +#if defined(OS_WIN) + utility_process_mojo_client_->set_run_elevated(); +#endif + + utility_process_mojo_client_->Start(); +} + +void ImageWriterUtilityClient::UtilityProcessError() { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + + OperationFailed("Utility process crashed or failed."); + utility_process_mojo_client_.reset(); +} + +void ImageWriterUtilityClient::OperationProgress(int64_t progress) { + if (progress_callback_) + task_runner_->PostTask(FROM_HERE, base::Bind(progress_callback_, progress)); +} + +void ImageWriterUtilityClient::OperationSucceeded() { + SuccessCallback success_callback = success_callback_; + ResetRequest(); + if (success_callback) + task_runner_->PostTask(FROM_HERE, success_callback); +} + +void ImageWriterUtilityClient::OperationFailed(const std::string& error) { + ErrorCallback error_callback = error_callback_; + ResetRequest(); + if (error_callback) + task_runner_->PostTask(FROM_HERE, base::Bind(error_callback, error)); +} + +void ImageWriterUtilityClient::ResetRequest() { + removable_storage_writer_client_.reset(); // Clear handlers to not hold any reference to the caller. - success_callback_ = base::Closure(); - progress_callback_ = base::Callback<void(int64_t)>(); - error_callback_ = base::Callback<void(const std::string&)>(); - cancel_callback_ = base::Closure(); -} - -void ImageWriterUtilityClient::StartHost() { - if (!utility_process_host_) { - scoped_refptr<base::SequencedTaskRunner> task_runner = - base::ThreadTaskRunnerHandle::Get(); - utility_process_host_ = content::UtilityProcessHost::Create( - this, task_runner.get())->AsWeakPtr(); - utility_process_host_->SetName(l10n_util::GetStringUTF16( - IDS_UTILITY_PROCESS_IMAGE_WRITER_NAME)); - -#if defined(OS_WIN) - utility_process_host_->ElevatePrivileges(); -#else - utility_process_host_->DisableSandbox(); -#endif - utility_process_host_->StartBatchMode(); - } -} - -void ImageWriterUtilityClient::OnProcessCrashed(int exit_code) { - task_runner_->PostTask( - FROM_HERE, - base::Bind(error_callback_, - base::StringPrintf("Utility process crashed with code %08x.", - exit_code))); -} - -void ImageWriterUtilityClient::OnProcessLaunchFailed(int error_code) { - task_runner_->PostTask( - FROM_HERE, - base::Bind(error_callback_, - base::StringPrintf("Process launch failed with code %08x.", - error_code))); -} - -bool ImageWriterUtilityClient::OnMessageReceived(const IPC::Message& message) { - bool handled = true; - IPC_BEGIN_MESSAGE_MAP(ImageWriterUtilityClient, message) - IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ImageWriter_Succeeded, - OnWriteImageSucceeded) - IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ImageWriter_Cancelled, - OnWriteImageCancelled) - IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ImageWriter_Failed, - OnWriteImageFailed) - IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ImageWriter_Progress, - OnWriteImageProgress) - IPC_MESSAGE_UNHANDLED(handled = false) - IPC_END_MESSAGE_MAP() - return handled; -} - -bool ImageWriterUtilityClient::Send(IPC::Message* msg) { - return utility_process_host_ && utility_process_host_->Send(msg); -} - -void ImageWriterUtilityClient::OnWriteImageSucceeded() { - if (!success_callback_.is_null()) { - task_runner_->PostTask(FROM_HERE, success_callback_); - } -} - -void ImageWriterUtilityClient::OnWriteImageCancelled() { - if (!cancel_callback_.is_null()) { - task_runner_->PostTask(FROM_HERE, cancel_callback_); - } -} - -void ImageWriterUtilityClient::OnWriteImageFailed(const std::string& message) { - if (!error_callback_.is_null()) { - task_runner_->PostTask(FROM_HERE, base::Bind(error_callback_, message)); - } -} - -void ImageWriterUtilityClient::OnWriteImageProgress(int64_t progress) { - if (!progress_callback_.is_null()) { - task_runner_->PostTask(FROM_HERE, base::Bind(progress_callback_, progress)); - } + progress_callback_.Reset(); + success_callback_.Reset(); + error_callback_.Reset(); }
diff --git a/chrome/browser/extensions/api/image_writer_private/image_writer_utility_client.h b/chrome/browser/extensions/api/image_writer_private/image_writer_utility_client.h index 62a9a28..0aa0a6d 100644 --- a/chrome/browser/extensions/api/image_writer_private/image_writer_utility_client.h +++ b/chrome/browser/extensions/api/image_writer_private/image_writer_utility_client.h
@@ -7,16 +7,17 @@ #include <stdint.h> +#include "base/callback.h" #include "base/files/file_path.h" #include "base/macros.h" -#include "base/memory/weak_ptr.h" +#include "base/memory/ref_counted.h" #include "base/single_thread_task_runner.h" -#include "base/threading/sequenced_worker_pool.h" -#include "content/public/browser/utility_process_host.h" -#include "content/public/browser/utility_process_host_client.h" +#include "chrome/common/extensions/removable_storage_writer.mojom.h" +#include "content/public/browser/utility_process_mojo_client.h" // Writes a disk image to a device inside the utility process. -class ImageWriterUtilityClient : public content::UtilityProcessHostClient { +class ImageWriterUtilityClient + : public base::RefCountedThreadSafe<ImageWriterUtilityClient> { public: typedef base::Callback<void()> CancelCallback; typedef base::Callback<void()> SuccessCallback; @@ -25,7 +26,7 @@ ImageWriterUtilityClient(); - // Starts the write. + // Starts the write operation. // |progress_callback|: Called periodically with the count of bytes processed. // |success_callback|: Called at successful completion. // |error_callback|: Called with an error message on failure. @@ -37,53 +38,56 @@ const base::FilePath& source, const base::FilePath& target); - // Starts a verification. + // Starts a verify operation. // |progress_callback|: Called periodically with the count of bytes processed. // |success_callback|: Called at successful completion. // |error_callback|: Called with an error message on failure. // |source|: The path to the source file to read data from. - // |target|: The path to the target device to write the image file to. + // |target|: The path to the target device file to verify. virtual void Verify(const ProgressCallback& progress_callback, const SuccessCallback& success_callback, const ErrorCallback& error_callback, const base::FilePath& source, const base::FilePath& target); - // Cancels any pending write or verification. + // Cancels any pending write or verify operation. // |cancel_callback|: Called when the cancel has actually occurred. virtual void Cancel(const CancelCallback& cancel_callback); - // Shuts down the Utility thread that may have been created. + // Shuts down the utility process that may have been created. virtual void Shutdown(); protected: - // It's a reference-counted object, so destructor is not public. - ~ImageWriterUtilityClient() override; + friend class base::RefCountedThreadSafe<ImageWriterUtilityClient>; + + virtual ~ImageWriterUtilityClient(); private: - // Ensures the UtilityProcessHost has been created. - void StartHost(); + class RemovableStorageWriterClientImpl; - // UtilityProcessHostClient implementation. - void OnProcessCrashed(int exit_code) override; - void OnProcessLaunchFailed(int error_code) override; - bool OnMessageReceived(const IPC::Message& message) override; - virtual bool Send(IPC::Message* msg); + // Ensures the utility process has been created. + void StartUtilityProcess(); - // IPC message handlers. - void OnWriteImageSucceeded(); - void OnWriteImageCancelled(); - void OnWriteImageFailed(const std::string& message); - void OnWriteImageProgress(int64_t progress); + void UtilityProcessError(); - CancelCallback cancel_callback_; + void OperationProgress(int64_t progress); + void OperationSucceeded(); + void OperationFailed(const std::string& error); + + void ResetRequest(); + ProgressCallback progress_callback_; SuccessCallback success_callback_; ErrorCallback error_callback_; - base::WeakPtr<content::UtilityProcessHost> utility_process_host_; + const scoped_refptr<base::SingleThreadTaskRunner> task_runner_; - scoped_refptr<base::SingleThreadTaskRunner> task_runner_; + std::unique_ptr<content::UtilityProcessMojoClient< + extensions::mojom::RemovableStorageWriter>> + utility_process_mojo_client_; + + std::unique_ptr<RemovableStorageWriterClientImpl> + removable_storage_writer_client_; DISALLOW_COPY_AND_ASSIGN(ImageWriterUtilityClient); };
diff --git a/chrome/browser/extensions/api/image_writer_private/image_writer_utility_client_browsertest.cc b/chrome/browser/extensions/api/image_writer_private/image_writer_utility_client_browsertest.cc new file mode 100644 index 0000000..2f082c3 --- /dev/null +++ b/chrome/browser/extensions/api/image_writer_private/image_writer_utility_client_browsertest.cc
@@ -0,0 +1,316 @@ +// Copyright 2017 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 "chrome/browser/extensions/api/image_writer_private/image_writer_utility_client.h" + +#include "base/bind.h" +#include "base/callback.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/files/scoped_temp_dir.h" +#include "base/macros.h" +#include "base/run_loop.h" +#include "chrome/common/extensions/removable_storage_writer.mojom.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "content/public/browser/browser_thread.h" + +constexpr int64_t kTestFileSize = 1 << 15; // 32 kB + +class ImageWriterUtilityClientTest : public InProcessBrowserTest { + public: + ImageWriterUtilityClientTest() { + test_device_ = base::FilePath().AppendASCII( + extensions::mojom::RemovableStorageWriter::kTestDevice); + EXPECT_TRUE(temp_dir_.CreateUniqueTempDir()); + } + + void FillImageFileWithPattern(char pattern) { + EXPECT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.GetPath(), &image_)); + + base::RunLoop run_loop; + content::BrowserThread::PostBlockingPoolTaskAndReply( + FROM_HERE, + base::Bind(&ImageWriterUtilityClientTest::FillFile, image_, pattern), + run_loop.QuitClosure()); + + run_loop.Run(); + } + + void FillDeviceFileWithPattern(char pattern) { + device_ = image_.ReplaceExtension(FILE_PATH_LITERAL("out")); + + base::RunLoop run_loop; + content::BrowserThread::PostBlockingPoolTaskAndReply( + FROM_HERE, + base::Bind(&ImageWriterUtilityClientTest::FillFile, device_, pattern), + run_loop.QuitClosure()); + + run_loop.Run(); + } + + enum RunOption { WRITE, VERIFY, CANCEL }; + + void RunWriteTest(RunOption option = WRITE) { + base::RunLoop run_loop; + quit_closure_ = run_loop.QuitClosure(); + + verify_ = (option == VERIFY); + cancel_ = (option == CANCEL); + + content::BrowserThread::PostTask( + content::BrowserThread::IO, FROM_HERE, + base::Bind(&ImageWriterUtilityClientTest::StartWriteTest, + base::Unretained(this))); + run_loop.Run(); + + EXPECT_TRUE(quit_called_); + } + + void RunVerifyTest(RunOption option = VERIFY) { + base::RunLoop run_loop; + quit_closure_ = run_loop.QuitClosure(); + + ASSERT_NE(option, WRITE); // Verify tests do not WRITE. + cancel_ = (option == CANCEL); + + content::BrowserThread::PostTask( + content::BrowserThread::IO, FROM_HERE, + base::Bind(&ImageWriterUtilityClientTest::StartVerifyTest, + base::Unretained(this))); + run_loop.Run(); + + EXPECT_TRUE(quit_called_); + } + + bool success() const { return success_; } + + const std::string& error() const { return error_; } + + private: + void StartWriteTest() { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + + if (!image_writer_utility_client_) + image_writer_utility_client_ = new ImageWriterUtilityClient(); + success_ = false; + progress_ = 0; + + image_writer_utility_client_->Write( + base::Bind(&ImageWriterUtilityClientTest::Progress, + base::Unretained(this)), + base::Bind(&ImageWriterUtilityClientTest::Success, + base::Unretained(this)), + base::Bind(&ImageWriterUtilityClientTest::Failure, + base::Unretained(this)), + image_, test_device_); + } + + void Progress(int64_t progress) { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + + progress_ = progress; + if (!cancel_) + return; + + image_writer_utility_client_->Cancel(base::Bind( + &ImageWriterUtilityClientTest::Cancelled, base::Unretained(this))); + } + + void Success() { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + + EXPECT_EQ(kTestFileSize, progress_); + EXPECT_FALSE(cancel_); + success_ = !cancel_; + + if (verify_) { + StartVerifyTest(); + return; + } + + content::BrowserThread::PostTask( + content::BrowserThread::IO, FROM_HERE, + base::Bind(&ImageWriterUtilityClientTest::Shutdown, + base::Unretained(this))); + } + + void StartVerifyTest() { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + + if (!image_writer_utility_client_) + image_writer_utility_client_ = new ImageWriterUtilityClient(); + success_ = false; + progress_ = 0; + + image_writer_utility_client_->Verify( + base::Bind(&ImageWriterUtilityClientTest::Progress, + base::Unretained(this)), + base::Bind(&ImageWriterUtilityClientTest::Verified, + base::Unretained(this)), + base::Bind(&ImageWriterUtilityClientTest::Failure, + base::Unretained(this)), + image_, test_device_); + } + + void Failure(const std::string& error) { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + + EXPECT_FALSE(error.empty()); + success_ = false; + error_ = error; + + content::BrowserThread::PostTask( + content::BrowserThread::IO, FROM_HERE, + base::Bind(&ImageWriterUtilityClientTest::Shutdown, + base::Unretained(this))); + } + + void Verified() { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + + EXPECT_EQ(kTestFileSize, progress_); + EXPECT_FALSE(cancel_); + success_ = !cancel_; + + content::BrowserThread::PostTask( + content::BrowserThread::IO, FROM_HERE, + base::Bind(&ImageWriterUtilityClientTest::Shutdown, + base::Unretained(this))); + } + + void Cancelled() { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + + EXPECT_TRUE(cancel_); + success_ = cancel_; + + quit_called_ = true; + content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, + quit_closure_); + } + + void Shutdown() { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + + image_writer_utility_client_->Shutdown(); + + quit_called_ = true; + content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, + quit_closure_); + } + + static void FillFile(const base::FilePath& path, char pattern) { + const std::vector<char> fill(kTestFileSize, pattern); + EXPECT_TRUE(base::WriteFile(path, fill.data(), kTestFileSize)); + } + + base::ScopedTempDir temp_dir_; + base::FilePath test_device_; + base::FilePath device_; + base::FilePath image_; + + base::Closure quit_closure_; + bool quit_called_ = false; + + scoped_refptr<ImageWriterUtilityClient> image_writer_utility_client_; + int64_t progress_ = 0; + bool success_ = false; + bool verify_ = false; + bool cancel_ = false; + std::string error_; + + DISALLOW_COPY_AND_ASSIGN(ImageWriterUtilityClientTest); +}; + +IN_PROC_BROWSER_TEST_F(ImageWriterUtilityClientTest, WriteNoImage) { + RunWriteTest(); + + EXPECT_FALSE(success()); + EXPECT_FALSE(error().empty()); +} + +IN_PROC_BROWSER_TEST_F(ImageWriterUtilityClientTest, WriteNoDevice) { + FillImageFileWithPattern(0); + + RunWriteTest(); + + EXPECT_FALSE(success()); + EXPECT_FALSE(error().empty()); +} + +IN_PROC_BROWSER_TEST_F(ImageWriterUtilityClientTest, Write) { + FillImageFileWithPattern('i'); + FillDeviceFileWithPattern(0); + + RunWriteTest(); + + EXPECT_TRUE(success()); + EXPECT_TRUE(error().empty()); +} + +IN_PROC_BROWSER_TEST_F(ImageWriterUtilityClientTest, WriteVerify) { + FillImageFileWithPattern('m'); + FillDeviceFileWithPattern(0); + + RunWriteTest(VERIFY); + + EXPECT_TRUE(success()); + EXPECT_TRUE(error().empty()); +} + +IN_PROC_BROWSER_TEST_F(ImageWriterUtilityClientTest, WriteCancel) { + FillImageFileWithPattern('a'); + FillDeviceFileWithPattern(0); + + RunWriteTest(CANCEL); + + EXPECT_TRUE(success()); + EXPECT_TRUE(error().empty()); +} + +IN_PROC_BROWSER_TEST_F(ImageWriterUtilityClientTest, VerifyNoImage) { + RunVerifyTest(); + + EXPECT_FALSE(success()); + EXPECT_FALSE(error().empty()); +} + +IN_PROC_BROWSER_TEST_F(ImageWriterUtilityClientTest, VerifyNoDevice) { + FillImageFileWithPattern(0); + + RunVerifyTest(); + + EXPECT_FALSE(success()); + EXPECT_FALSE(error().empty()); +} + +IN_PROC_BROWSER_TEST_F(ImageWriterUtilityClientTest, VerifyFailure) { + FillImageFileWithPattern('g'); + FillDeviceFileWithPattern(0); + + RunVerifyTest(); + + EXPECT_FALSE(success()); + EXPECT_FALSE(error().empty()); +} + +IN_PROC_BROWSER_TEST_F(ImageWriterUtilityClientTest, Verify) { + FillImageFileWithPattern('e'); + FillDeviceFileWithPattern('e'); + + RunVerifyTest(); + + EXPECT_TRUE(success()); + EXPECT_TRUE(error().empty()); +} + +IN_PROC_BROWSER_TEST_F(ImageWriterUtilityClientTest, VerifyCancel) { + FillImageFileWithPattern('s'); + FillDeviceFileWithPattern('s'); + + RunVerifyTest(CANCEL); + + EXPECT_TRUE(success()); + EXPECT_TRUE(error().empty()); +}
diff --git a/chrome/browser/ntp_snippets/content_suggestions_service_factory.cc b/chrome/browser/ntp_snippets/content_suggestions_service_factory.cc index 903d743..87cc283 100644 --- a/chrome/browser/ntp_snippets/content_suggestions_service_factory.cc +++ b/chrome/browser/ntp_snippets/content_suggestions_service_factory.cc
@@ -95,6 +95,18 @@ using syncer::SyncService; using translate::LanguageModel; +// For now, ContentSuggestionsService must only be instantiated on Android. +// See also crbug.com/688366. +#if defined(OS_ANDROID) +#define CONTENT_SUGGESTIONS_ENABLED 1 +#else +#define CONTENT_SUGGESTIONS_ENABLED 0 +#endif // OS_ANDROID + +// The actual #if that does the work is below in BuildServiceInstanceFor. This +// one is just required to avoid "unused code" compiler errors. +#if CONTENT_SUGGESTIONS_ENABLED + namespace { #if defined(OS_ANDROID) @@ -124,6 +136,7 @@ pref_service, base::MakeUnique<base::DefaultClock>()); service->RegisterProvider(std::move(provider)); } + #endif // OS_ANDROID void RegisterBookmarkProvider(BookmarkModel* bookmark_model, @@ -150,6 +163,7 @@ service, physical_web_data_source, pref_service); service->RegisterProvider(std::move(provider)); } + #endif // OS_ANDROID void RegisterArticleProvider(SigninManagerBase* signin_manager, @@ -213,6 +227,8 @@ } // namespace +#endif // CONTENT_SUGGESTIONS_ENABLED + // static ContentSuggestionsServiceFactory* ContentSuggestionsServiceFactory::GetInstance() { @@ -251,6 +267,8 @@ KeyedService* ContentSuggestionsServiceFactory::BuildServiceInstanceFor( content::BrowserContext* context) const { +#if CONTENT_SUGGESTIONS_ENABLED + using State = ContentSuggestionsService::State; Profile* profile = Profile::FromBrowserContext(context); DCHECK(!profile->IsOffTheRecord()); @@ -329,4 +347,8 @@ } return service; + +#else + return nullptr; +#endif // CONTENT_SUGGESTIONS_ENABLED }
diff --git a/chrome/browser/ntp_snippets/content_suggestions_service_factory_browsertest.cc b/chrome/browser/ntp_snippets/content_suggestions_service_factory_browsertest.cc new file mode 100644 index 0000000..4878af5e --- /dev/null +++ b/chrome/browser/ntp_snippets/content_suggestions_service_factory_browsertest.cc
@@ -0,0 +1,22 @@ +// Copyright 2017 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 "chrome/browser/ntp_snippets/content_suggestions_service_factory.h" + +#include "chrome/browser/ui/browser.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "testing/gtest/include/gtest/gtest.h" + +using ContentSuggestionsServiceFactoryTest = InProcessBrowserTest; + +IN_PROC_BROWSER_TEST_F(ContentSuggestionsServiceFactoryTest, + CreatesServiceOnlyOnAndroid) { + ntp_snippets::ContentSuggestionsService* service = + ContentSuggestionsServiceFactory::GetForProfile(browser()->profile()); +#if defined(OS_ANDROID) + EXPECT_TRUE(service); +#else + EXPECT_FALSE(service); +#endif +}
diff --git a/chrome/browser/profiles/profile_avatar_icon_util.cc b/chrome/browser/profiles/profile_avatar_icon_util.cc index 6e1a5c38..01bb8b1 100644 --- a/chrome/browser/profiles/profile_avatar_icon_util.cc +++ b/chrome/browser/profiles/profile_avatar_icon_util.cc
@@ -369,6 +369,10 @@ return IDR_PROFILE_AVATAR_PLACEHOLDER_LARGE; } +std::string GetPlaceholderAvatarIconUrl() { + return "chrome://theme/IDR_PROFILE_AVATAR_PLACEHOLDER_LARGE"; +} + const IconResourceInfo* GetDefaultAvatarIconResourceInfo(size_t index) { CHECK_LT(index, kDefaultAvatarIconsCount); static const IconResourceInfo resource_info[kDefaultAvatarIconsCount] = {
diff --git a/chrome/browser/profiles/profile_avatar_icon_util.h b/chrome/browser/profiles/profile_avatar_icon_util.h index 7d023ea..07ceab6 100644 --- a/chrome/browser/profiles/profile_avatar_icon_util.h +++ b/chrome/browser/profiles/profile_avatar_icon_util.h
@@ -90,6 +90,9 @@ // Gets the resource ID of the placeholder avatar icon. int GetPlaceholderAvatarIconResourceID(); +// Returns a URL for the placeholder avatar icon. +std::string GetPlaceholderAvatarIconUrl(); + // Gets the resource ID of the default avatar icon at |index|. int GetDefaultAvatarIconResourceIDAtIndex(size_t index);
diff --git a/chrome/browser/signin/mutable_profile_oauth2_token_service_delegate.cc b/chrome/browser/signin/mutable_profile_oauth2_token_service_delegate.cc index 572980d..acc89315 100644 --- a/chrome/browser/signin/mutable_profile_oauth2_token_service_delegate.cc +++ b/chrome/browser/signin/mutable_profile_oauth2_token_service_delegate.cc
@@ -41,6 +41,23 @@ return prefixed_account_id.substr(kAccountIdPrefixLength); } +OAuth2TokenServiceDelegate::LoadCredentialsState +LoadCredentialsStateFromTokenResult(TokenServiceTable::Result token_result) { + switch (token_result) { + case TokenServiceTable::TOKEN_DB_RESULT_SQL_INVALID_STATEMENT: + case TokenServiceTable::TOKEN_DB_RESULT_BAD_ENTRY: + return OAuth2TokenServiceDelegate:: + LOAD_CREDENTIALS_FINISHED_WITH_DB_ERRORS; + case TokenServiceTable::TOKEN_DB_RESULT_DECRYPT_ERROR: + return OAuth2TokenServiceDelegate:: + LOAD_CREDENTIALS_FINISHED_WITH_DECRYPT_ERRORS; + case TokenServiceTable::TOKEN_DB_RESULT_SUCCESS: + return OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS; + } + NOTREACHED(); + return OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_UNKNOWN; +} + } // namespace // This class sends a request to GAIA to revoke the given refresh token from @@ -129,6 +146,7 @@ SigninErrorController* signin_error_controller, AccountTrackerService* account_tracker_service) : web_data_service_request_(0), + load_credentials_state_(LOAD_CREDENTIALS_NOT_STARTED), backoff_entry_(&backoff_policy_), backoff_error_(GoogleServiceAuthError::NONE), client_(client), @@ -244,18 +262,40 @@ return client_->GetURLRequestContext(); } +OAuth2TokenServiceDelegate::LoadCredentialsState +MutableProfileOAuth2TokenServiceDelegate::GetLoadCredentialsState() const { + return load_credentials_state_; +} + void MutableProfileOAuth2TokenServiceDelegate::LoadCredentials( const std::string& primary_account_id) { + if (load_credentials_state_ == LOAD_CREDENTIALS_IN_PROGRESS) { + VLOG(1) << "Load credentials operation already in progress"; + return; + } + + load_credentials_state_ = LOAD_CREDENTIALS_IN_PROGRESS; if (primary_account_id.empty()) { + load_credentials_state_ = LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS; FireRefreshTokensLoaded(); return; } + ValidateAccountId(primary_account_id); DCHECK(loading_primary_account_id_.empty()); DCHECK_EQ(0, web_data_service_request_); refresh_tokens_.clear(); + scoped_refptr<TokenWebData> token_web_data = client_->GetDatabase(); + if (!token_web_data) { + // This case only exists in unit tests that do not care about loading + // credentials. + load_credentials_state_ = LOAD_CREDENTIALS_FINISHED_WITH_UNKNOWN_ERRORS; + FireRefreshTokensLoaded(); + return; + } + // If the account_id is an email address, then canonicalize it. This // is to support legacy account_ids, and will not be needed after // switching to gaia-ids. @@ -265,9 +305,7 @@ loading_primary_account_id_ = primary_account_id; } - scoped_refptr<TokenWebData> token_web_data = client_->GetDatabase(); - if (token_web_data.get()) - web_data_service_request_ = token_web_data->GetAllTokens(this); + web_data_service_request_ = token_web_data->GetAllTokens(this); } void MutableProfileOAuth2TokenServiceDelegate::OnWebDataServiceRequestDone( @@ -287,11 +325,15 @@ if (result) { DCHECK(result->GetType() == TOKEN_RESULT); - const WDResult<std::map<std::string, std::string>>* token_result = - static_cast<const WDResult<std::map<std::string, std::string>>*>( - result.get()); - LoadAllCredentialsIntoMemory(token_result->GetValue()); + const WDResult<TokenResult>* token_result = + static_cast<const WDResult<TokenResult>*>(result.get()); + LoadAllCredentialsIntoMemory(token_result->GetValue().tokens); + load_credentials_state_ = + LoadCredentialsStateFromTokenResult(token_result->GetValue().db_result); + } else { + load_credentials_state_ = LOAD_CREDENTIALS_FINISHED_WITH_UNKNOWN_ERRORS; } + FireRefreshTokensLoaded(); // Make sure that we have an entry for |loading_primary_account_id_| in the // map. The entry could be missing if there is a corruption in the token DB @@ -400,8 +442,6 @@ UpdateCredentials(loading_primary_account_id_, old_login_token); } } - - FireRefreshTokensLoaded(); } void MutableProfileOAuth2TokenServiceDelegate::UpdateCredentials(
diff --git a/chrome/browser/signin/mutable_profile_oauth2_token_service_delegate.h b/chrome/browser/signin/mutable_profile_oauth2_token_service_delegate.h index 119b5e76..2e5e5ab 100644 --- a/chrome/browser/signin/mutable_profile_oauth2_token_service_delegate.h +++ b/chrome/browser/signin/mutable_profile_oauth2_token_service_delegate.h
@@ -54,6 +54,7 @@ // Overridden from OAuth2TokenServiceDelegate. void Shutdown() override; + LoadCredentialsState GetLoadCredentialsState() const override; // Overridden from NetworkChangeObserver. void OnNetworkChanged(net::NetworkChangeNotifier::ConnectionType type) @@ -151,6 +152,9 @@ // credentials. This member is empty otherwise. std::string loading_primary_account_id_; + // The state of the load credentials operation. + LoadCredentialsState load_credentials_state_; + ScopedVector<RevokeServerRefreshToken> server_revokes_; // Used to verify that certain methods are called only on the thread on which
diff --git a/chrome/browser/signin/mutable_profile_oauth2_token_service_delegate_unittest.cc b/chrome/browser/signin/mutable_profile_oauth2_token_service_delegate_unittest.cc index bd7e94b..421737b 100644 --- a/chrome/browser/signin/mutable_profile_oauth2_token_service_delegate_unittest.cc +++ b/chrome/browser/signin/mutable_profile_oauth2_token_service_delegate_unittest.cc
@@ -282,6 +282,18 @@ } TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, + LoadCredentialsStateEmptyPrimaryAccountId) { + // Ensure DB is clean. + oauth2_service_delegate_->RevokeAllCredentials(); + + EXPECT_EQ(OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_NOT_STARTED, + oauth2_service_delegate_->GetLoadCredentialsState()); + oauth2_service_delegate_->LoadCredentials(""); + EXPECT_EQ(OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS, + oauth2_service_delegate_->GetLoadCredentialsState()); +} + +TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, PersistenceLoadCredentials) { switches::EnableAccountConsistencyForTesting( base::CommandLine::ForCurrentProcess()); @@ -290,8 +302,14 @@ oauth2_service_delegate_->RevokeAllCredentials(); ResetObserverCounts(); // Perform a load from an empty DB. + EXPECT_EQ(OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_NOT_STARTED, + oauth2_service_delegate_->GetLoadCredentialsState()); oauth2_service_delegate_->LoadCredentials("account_id"); + EXPECT_EQ(OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_IN_PROGRESS, + oauth2_service_delegate_->GetLoadCredentialsState()); base::RunLoop().RunUntilIdle(); + EXPECT_EQ(OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS, + oauth2_service_delegate_->GetLoadCredentialsState()); EXPECT_EQ(1, start_batch_changes_); EXPECT_EQ(1, end_batch_changes_); ExpectOneTokensLoadedNotification(); @@ -310,7 +328,11 @@ ResetObserverCounts(); oauth2_service_delegate_->LoadCredentials("account_id"); + EXPECT_EQ(OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_IN_PROGRESS, + oauth2_service_delegate_->GetLoadCredentialsState()); base::RunLoop().RunUntilIdle(); + EXPECT_EQ(OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS, + oauth2_service_delegate_->GetLoadCredentialsState()); EXPECT_EQ(2, token_available_count_); EXPECT_EQ(0, token_revoked_count_); EXPECT_EQ(1, tokens_loaded_count_);
diff --git a/chrome/browser/ui/cocoa/bookmarks/bookmark_folder_target_unittest.mm b/chrome/browser/ui/cocoa/bookmarks/bookmark_folder_target_unittest.mm index 7053de70..def8165 100644 --- a/chrome/browser/ui/cocoa/bookmarks/bookmark_folder_target_unittest.mm +++ b/chrome/browser/ui/cocoa/bookmarks/bookmark_folder_target_unittest.mm
@@ -27,7 +27,7 @@ // We need a mechanism to clear the invocation handlers to break a // retain cycle (see below; search for "retain cycle"). - (void)clearRecordersAndExpectations { - [recorders removeAllObjects]; + [stubs removeAllObjects]; [expectations removeAllObjects]; }
diff --git a/chrome/browser/ui/webui/signin/sync_confirmation_handler.cc b/chrome/browser/ui/webui/signin/sync_confirmation_handler.cc index c054fcc..1bc5644 100644 --- a/chrome/browser/ui/webui/signin/sync_confirmation_handler.cc +++ b/chrome/browser/ui/webui/signin/sync_confirmation_handler.cc
@@ -76,14 +76,19 @@ } void SyncConfirmationHandler::SetUserImageURL(const std::string& picture_url) { + std::string picture_url_to_load; GURL url; - if (profiles::GetImageURLWithThumbnailSize(GURL(picture_url), - kProfileImageSize, - &url)) { - base::StringValue picture_url_value(url.spec()); - web_ui()->CallJavascriptFunctionUnsafe("sync.confirmation.setUserImageURL", - picture_url_value); + if (picture_url != AccountTrackerService::kNoPictureURLFound && + profiles::GetImageURLWithThumbnailSize(GURL(picture_url), + kProfileImageSize, &url)) { + picture_url_to_load = url.spec(); + } else { + // Use the placeholder avatar icon until the account picture URL is fetched. + picture_url_to_load = profiles::GetPlaceholderAvatarIconUrl(); } + base::StringValue picture_url_value(picture_url_to_load); + web_ui()->CallJavascriptFunctionUnsafe("sync.confirmation.setUserImageURL", + picture_url_value); } void SyncConfirmationHandler::OnAccountUpdated(const AccountInfo& info) { @@ -91,8 +96,11 @@ return; Profile* profile = Profile::FromWebUI(web_ui()); - AccountTrackerServiceFactory::GetForProfile(profile)->RemoveObserver(this); + SigninManager* signin_manager = SigninManagerFactory::GetForProfile(profile); + if (info.account_id != signin_manager->GetAuthenticatedAccountId()) + return; + AccountTrackerServiceFactory::GetForProfile(profile)->RemoveObserver(this); SetUserImageURL(info.picture_url); } @@ -113,18 +121,23 @@ return; Profile* profile = browser->profile(); - std::vector<AccountInfo> accounts = - AccountTrackerServiceFactory::GetForProfile(profile)->GetAccounts(); - - if (accounts.empty()) + std::string account_id = + SigninManagerFactory::GetForProfile(profile)->GetAuthenticatedAccountId(); + if (account_id.empty()) { + // No account is signed in, so there is nothing to be displayed in the sync + // confirmation dialog. return; + } + AccountTrackerService* account_tracker = + AccountTrackerServiceFactory::GetForProfile(profile); + AccountInfo account_info = account_tracker->GetAccountInfo(account_id); - AccountInfo primary_account_info = accounts[0]; - - if (!primary_account_info.IsValid()) - AccountTrackerServiceFactory::GetForProfile(profile)->AddObserver(this); - else - SetUserImageURL(primary_account_info.picture_url); + if (!account_info.IsValid()) { + SetUserImageURL(AccountTrackerService::kNoPictureURLFound); + account_tracker->AddObserver(this); + } else { + SetUserImageURL(account_info.picture_url); + } signin::SetInitializedModalHeight(web_ui(), args);
diff --git a/chrome/browser/ui/webui/signin/sync_confirmation_handler_unittest.cc b/chrome/browser/ui/webui/signin/sync_confirmation_handler_unittest.cc index d65124c..3ecf60e 100644 --- a/chrome/browser/ui/webui/signin/sync_confirmation_handler_unittest.cc +++ b/chrome/browser/ui/webui/signin/sync_confirmation_handler_unittest.cc
@@ -175,7 +175,7 @@ base::ListValue args; args.Set(0, new base::FundamentalValue(kDefaultDialogHeight)); handler()->HandleInitializedWithSize(&args); - EXPECT_EQ(1U, web_ui()->call_data().size()); + EXPECT_EQ(2U, web_ui()->call_data().size()); account_fetcher_service()->FakeUserInfoFetchSuccess( "gaia", @@ -187,20 +187,30 @@ "locale", "http://picture.example.com/picture.jpg"); - EXPECT_EQ(2U, web_ui()->call_data().size()); + EXPECT_EQ(3U, web_ui()->call_data().size()); // When the primary account isn't yet ready when the dialog is shown, - // clearFocus is called before setUserImageURL. - EXPECT_EQ("sync.confirmation.clearFocus", - web_ui()->call_data()[0]->function_name()); - + // setUserImageURL is called with the default placeholder image. EXPECT_EQ("sync.confirmation.setUserImageURL", - web_ui()->call_data()[1]->function_name()); + web_ui()->call_data()[0]->function_name()); EXPECT_TRUE( - web_ui()->call_data()[1]->arg1()->IsType(base::Value::Type::STRING)); + web_ui()->call_data()[0]->arg1()->IsType(base::Value::Type::STRING)); std::string passed_picture_url; EXPECT_TRUE( - web_ui()->call_data()[1]->arg1()->GetAsString(&passed_picture_url)); + web_ui()->call_data()[0]->arg1()->GetAsString(&passed_picture_url)); + EXPECT_EQ(profiles::GetPlaceholderAvatarIconUrl(), passed_picture_url); + + // When the primary account isn't yet ready when the dialog is shown, + // clearFocus is called before the second call to setUserImageURL. + EXPECT_EQ("sync.confirmation.clearFocus", + web_ui()->call_data()[1]->function_name()); + + EXPECT_EQ("sync.confirmation.setUserImageURL", + web_ui()->call_data()[2]->function_name()); + EXPECT_TRUE( + web_ui()->call_data()[2]->arg1()->IsType(base::Value::Type::STRING)); + EXPECT_TRUE( + web_ui()->call_data()[2]->arg1()->GetAsString(&passed_picture_url)); std::string original_picture_url = AccountTrackerServiceFactory::GetForProfile(profile())-> @@ -212,6 +222,35 @@ EXPECT_EQ(picture_url_with_size.spec(), passed_picture_url); } +TEST_F(SyncConfirmationHandlerTest, + TestSetImageIgnoredIfSecondaryAccountUpdated) { + base::ListValue args; + args.Set(0, new base::FundamentalValue(kDefaultDialogHeight)); + handler()->HandleInitializedWithSize(&args); + EXPECT_EQ(2U, web_ui()->call_data().size()); + + AccountTrackerServiceFactory::GetForProfile(profile())->SeedAccountInfo( + "bar_gaia", "bar@example.com"); + account_fetcher_service()->FakeUserInfoFetchSuccess( + "bar_gaia", "bar@example.com", "bar_gaia", "", "bar_full_name", + "bar_given_name", "bar_locale", + "http://picture.example.com/bar_picture.jpg"); + + // Updating the account info of a secondary account should not update the + // image of the sync confirmation dialog. + EXPECT_EQ(2U, web_ui()->call_data().size()); + + account_fetcher_service()->FakeUserInfoFetchSuccess( + "gaia", "foo@example.com", "gaia", "", "full_name", "given_name", + "locale", "http://picture.example.com/picture.jpg"); + + // Updating the account info of the primary account should update the + // image of the sync confirmation dialog. + EXPECT_EQ(3U, web_ui()->call_data().size()); + EXPECT_EQ("sync.confirmation.setUserImageURL", + web_ui()->call_data()[2]->function_name()); +} + TEST_F(SyncConfirmationHandlerTest, TestHandleUndo) { EXPECT_FALSE(sync()->IsFirstSetupComplete()); EXPECT_TRUE(sync()->IsFirstSetupInProgress());
diff --git a/chrome/common/extensions/BUILD.gn b/chrome/common/extensions/BUILD.gn index 2be3aaa..fc461066 100644 --- a/chrome/common/extensions/BUILD.gn +++ b/chrome/common/extensions/BUILD.gn
@@ -26,6 +26,7 @@ mojom("mojo_bindings") { sources = [ "media_parser.mojom", + "removable_storage_writer.mojom", ] if (is_win) {
diff --git a/chrome/common/extensions/chrome_utility_extensions_messages.h b/chrome/common/extensions/chrome_utility_extensions_messages.h index 038da59..c5a2c5b 100644 --- a/chrome/common/extensions/chrome_utility_extensions_messages.h +++ b/chrome/common/extensions/chrome_utility_extensions_messages.h
@@ -89,23 +89,6 @@ int64_t /* request_id */, std::string /* bytes */) -// Requests that the utility process write the contents of the source file to -// the removable drive listed in the target file. The target will be restricted -// to removable drives by the utility process. -IPC_MESSAGE_CONTROL2(ChromeUtilityMsg_ImageWriter_Write, - base::FilePath /* source file */, - base::FilePath /* target file */) - -// Requests that the utility process verify that the contents of the source file -// was written to the target. As above the target will be restricted to -// removable drives by the utility process. -IPC_MESSAGE_CONTROL2(ChromeUtilityMsg_ImageWriter_Verify, - base::FilePath /* source file */, - base::FilePath /* target file */) - -// Cancels a pending write or verify operation. -IPC_MESSAGE_CONTROL0(ChromeUtilityMsg_ImageWriter_Cancel) - //------------------------------------------------------------------------------ // Utility process host messages: // These are messages from the utility process to the browser. @@ -141,17 +124,3 @@ int64_t /* request_id */, int64_t /* start_byte */, int64_t /* length */) - -// Reply when a write or verify operation succeeds. -IPC_MESSAGE_CONTROL0(ChromeUtilityHostMsg_ImageWriter_Succeeded) - -// Reply when a write or verify operation has been fully cancelled. -IPC_MESSAGE_CONTROL0(ChromeUtilityHostMsg_ImageWriter_Cancelled) - -// Reply when a write or verify operation fails to complete. -IPC_MESSAGE_CONTROL1(ChromeUtilityHostMsg_ImageWriter_Failed, - std::string /* message */) - -// Periodic status update about the progress of an operation. -IPC_MESSAGE_CONTROL1(ChromeUtilityHostMsg_ImageWriter_Progress, - int64_t /* number of bytes processed */)
diff --git a/chrome/common/extensions/removable_storage_writer.mojom b/chrome/common/extensions/removable_storage_writer.mojom new file mode 100644 index 0000000..f50ff43 --- /dev/null +++ b/chrome/common/extensions/removable_storage_writer.mojom
@@ -0,0 +1,34 @@ +// Copyright 2017 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. + +// Removable storage writer interface provided by the utility process +// and exposed by mojo policy to the chrome browser process. + +module extensions.mojom; + +import "mojo/common/file_path.mojom"; + +interface RemovableStorageWriter { + const string kTestDevice = "chrome://test-removable-storage-writer"; + + // Writes the content of the source file to the target. The target + // file is restricted to removable drives by the utility process. + Write(mojo.common.mojom.FilePath source, + mojo.common.mojom.FilePath target, + RemovableStorageWriterClient client); + + // Verifies that the contents of the source file was written to the + // target file. Again, the target is restricted to removable drives + // by the utility process. + Verify(mojo.common.mojom.FilePath source, + mojo.common.mojom.FilePath target, + RemovableStorageWriterClient client); +}; + +interface RemovableStorageWriterClient { + // Interface to the client used to report write or verify operation + // progress and completion status. + Progress(int64 progress); + Complete(string? error); +};
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index ee3c2272..417fafa 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -1654,6 +1654,7 @@ "../browser/net/sdch_browsertest.cc", "../browser/net/spdyproxy/chrome_data_use_group_browsertest.cc", "../browser/net/websocket_browsertest.cc", + "../browser/ntp_snippets/content_suggestions_service_factory_browsertest.cc", "../browser/page_load_metrics/observers/https_engagement_metrics/https_engagement_page_load_metrics_observer_browsertest.cc", "../browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.cc", "../browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.h", @@ -2562,6 +2563,7 @@ } if (is_mac || is_win) { sources += [ + "../browser/extensions/api/image_writer_private/image_writer_utility_client_browsertest.cc", "../browser/extensions/api/networking_private/networking_private_apitest.cc", "../browser/extensions/api/networking_private/networking_private_service_client_apitest.cc", "../browser/media_galleries/fileapi/itunes_data_provider_browsertest.cc",
diff --git a/chrome/utility/BUILD.gn b/chrome/utility/BUILD.gn index 3f660b4..637a2336 100644 --- a/chrome/utility/BUILD.gn +++ b/chrome/utility/BUILD.gn
@@ -12,8 +12,6 @@ sources = [ "chrome_content_utility_client.cc", "chrome_content_utility_client.h", - "chrome_content_utility_ipc_whitelist.cc", - "chrome_content_utility_ipc_whitelist.h", "cloud_print/bitmap_image.cc", "cloud_print/bitmap_image.h", "cloud_print/pwg_encoder.cc",
diff --git a/chrome/utility/OWNERS b/chrome/utility/OWNERS deleted file mode 100644 index 724accc..0000000 --- a/chrome/utility/OWNERS +++ /dev/null
@@ -1,3 +0,0 @@ -# For security review of utility process message whitelisting -per-file chrome_content_utility_ipc_whitelist.cc=set noparent -per-file chrome_content_utility_ipc_whitelist.cc=file://ipc/SECURITY_OWNERS
diff --git a/chrome/utility/chrome_content_utility_client.cc b/chrome/utility/chrome_content_utility_client.cc index 8fcd114..7e5cd72 100644 --- a/chrome/utility/chrome_content_utility_client.cc +++ b/chrome/utility/chrome_content_utility_client.cc
@@ -17,7 +17,6 @@ #include "chrome/common/file_patcher.mojom.h" #include "chrome/common/safe_browsing/zip_analyzer.h" #include "chrome/common/safe_browsing/zip_analyzer_results.h" -#include "chrome/utility/chrome_content_utility_ipc_whitelist.h" #include "chrome/utility/utility_message_handler.h" #include "components/safe_json/utility/safe_json_parser_mojo_impl.h" #include "content/public/child/image_decoder_utils.h" @@ -51,7 +50,6 @@ #if BUILDFLAG(ENABLE_EXTENSIONS) #include "chrome/utility/extensions/extensions_handler.h" -#include "chrome/utility/image_writer/image_writer_handler.h" #endif #if BUILDFLAG(ENABLE_PRINT_PREVIEW) || \ @@ -149,10 +147,9 @@ } // namespace ChromeContentUtilityClient::ChromeContentUtilityClient() - : filter_messages_(false) { + : utility_process_running_elevated_(false) { #if BUILDFLAG(ENABLE_EXTENSIONS) handlers_.push_back(new extensions::ExtensionsHandler()); - handlers_.push_back(new image_writer::ImageWriterHandler()); #endif #if BUILDFLAG(ENABLE_PRINT_PREVIEW) || \ @@ -165,30 +162,22 @@ #endif } -ChromeContentUtilityClient::~ChromeContentUtilityClient() { -} +ChromeContentUtilityClient::~ChromeContentUtilityClient() = default; void ChromeContentUtilityClient::UtilityThreadStarted() { #if BUILDFLAG(ENABLE_EXTENSIONS) extensions::UtilityHandler::UtilityThreadStarted(); #endif - if (kMessageWhitelistSize > 0) { - base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); - if (command_line->HasSwitch(switches::kUtilityProcessRunningElevated)) { - message_id_whitelist_.insert(kMessageWhitelist, - kMessageWhitelist + kMessageWhitelistSize); - filter_messages_ = true; - } - } + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + if (command_line->HasSwitch(switches::kUtilityProcessRunningElevated)) + utility_process_running_elevated_ = true; } bool ChromeContentUtilityClient::OnMessageReceived( const IPC::Message& message) { - if (filter_messages_ && - !base::ContainsKey(message_id_whitelist_, message.type())) { + if (utility_process_running_elevated_) return false; - } bool handled = true; IPC_BEGIN_MESSAGE_MAP(ChromeContentUtilityClient, message) @@ -224,17 +213,14 @@ void ChromeContentUtilityClient::ExposeInterfacesToBrowser( service_manager::InterfaceRegistry* registry) { - const bool running_elevated = - base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kUtilityProcessRunningElevated); #if BUILDFLAG(ENABLE_EXTENSIONS) ChromeContentUtilityClient* utility_client = this; extensions::ExtensionsHandler::ExposeInterfacesToBrowser( - registry, utility_client, running_elevated); + registry, utility_client, utility_process_running_elevated_); #endif // If our process runs with elevated privileges, only add elevated Mojo // services to the interface registry. - if (running_elevated) + if (utility_process_running_elevated_) return; registry->AddInterface(base::Bind(&FilePatcherImpl::Create));
diff --git a/chrome/utility/chrome_content_utility_client.h b/chrome/utility/chrome_content_utility_client.h index d24f0b11..0a659e1 100644 --- a/chrome/utility/chrome_content_utility_client.h +++ b/chrome/utility/chrome_content_utility_client.h
@@ -63,10 +63,7 @@ typedef ScopedVector<UtilityMessageHandler> Handlers; Handlers handlers_; - // Flag to enable whitelisting. - bool filter_messages_; - // A list of message_ids to filter. - std::set<int> message_id_whitelist_; + bool utility_process_running_elevated_; DISALLOW_COPY_AND_ASSIGN(ChromeContentUtilityClient); };
diff --git a/chrome/utility/chrome_content_utility_ipc_whitelist.cc b/chrome/utility/chrome_content_utility_ipc_whitelist.cc deleted file mode 100644 index c9fc8c6..0000000 --- a/chrome/utility/chrome_content_utility_ipc_whitelist.cc +++ /dev/null
@@ -1,25 +0,0 @@ -// Copyright 2014 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 "chrome/utility/chrome_content_utility_ipc_whitelist.h" - -#include "base/macros.h" -#include "build/build_config.h" -#include "extensions/features/features.h" - -#if BUILDFLAG(ENABLE_EXTENSIONS) -#include "chrome/common/extensions/chrome_utility_extensions_messages.h" -#endif - -#if BUILDFLAG(ENABLE_EXTENSIONS) -const uint32_t kMessageWhitelist[] = { - ChromeUtilityMsg_ImageWriter_Cancel::ID, - ChromeUtilityMsg_ImageWriter_Write::ID, - ChromeUtilityMsg_ImageWriter_Verify::ID}; -const size_t kMessageWhitelistSize = arraysize(kMessageWhitelist); -#else -// Note: Zero-size arrays are not valid C++. -const uint32_t kMessageWhitelist[] = {0}; -const size_t kMessageWhitelistSize = 0; -#endif // BUILDFLAG(ENABLE_EXTENSIONS)
diff --git a/chrome/utility/chrome_content_utility_ipc_whitelist.h b/chrome/utility/chrome_content_utility_ipc_whitelist.h deleted file mode 100644 index 518cddcc..0000000 --- a/chrome/utility/chrome_content_utility_ipc_whitelist.h +++ /dev/null
@@ -1,18 +0,0 @@ -// Copyright 2014 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 CHROME_UTILITY_CHROME_CONTENT_UTILITY_IPC_WHITELIST_H_ -#define CHROME_UTILITY_CHROME_CONTENT_UTILITY_IPC_WHITELIST_H_ - -#include <stddef.h> -#include <stdint.h> - -// This array contains the list of IPC messages that the utility process will -// accept when running with elevated privileges. When new messages need to run -// with elevated privileges, add them here and be sure to add a security -// reviewer. -extern const uint32_t kMessageWhitelist[]; -extern const size_t kMessageWhitelistSize; - -#endif // CHROME_UTILITY_CHROME_CONTENT_UTILITY_IPC_WHITELIST_H_
diff --git a/chrome/utility/extensions/extensions_handler.cc b/chrome/utility/extensions/extensions_handler.cc index c6e2a74b..cef009a 100644 --- a/chrome/utility/extensions/extensions_handler.cc +++ b/chrome/utility/extensions/extensions_handler.cc
@@ -7,14 +7,17 @@ #include <utility> #include "base/command_line.h" +#include "base/files/file_path.h" #include "base/path_service.h" #include "build/build_config.h" #include "chrome/common/chrome_utility_messages.h" #include "chrome/common/extensions/chrome_extensions_client.h" #include "chrome/common/extensions/chrome_utility_extensions_messages.h" #include "chrome/common/extensions/media_parser.mojom.h" +#include "chrome/common/extensions/removable_storage_writer.mojom.h" #include "chrome/common/media_galleries/metadata_types.h" #include "chrome/utility/chrome_content_utility_client.h" +#include "chrome/utility/image_writer/image_writer_handler.h" #include "chrome/utility/media_galleries/ipc_data_source.h" #include "chrome/utility/media_galleries/media_metadata_parser.h" #include "content/public/common/content_paths.h" @@ -103,6 +106,37 @@ DISALLOW_COPY_AND_ASSIGN(MediaParserImpl); }; +class RemovableStorageWriterImpl + : public extensions::mojom::RemovableStorageWriter { + public: + RemovableStorageWriterImpl() = default; + ~RemovableStorageWriterImpl() override = default; + + static void Create(extensions::mojom::RemovableStorageWriterRequest request) { + mojo::MakeStrongBinding(base::MakeUnique<RemovableStorageWriterImpl>(), + std::move(request)); + } + + private: + void Write( + const base::FilePath& source, + const base::FilePath& target, + extensions::mojom::RemovableStorageWriterClientPtr client) override { + writer_.Write(source, target, std::move(client)); + } + + void Verify( + const base::FilePath& source, + const base::FilePath& target, + extensions::mojom::RemovableStorageWriterClientPtr client) override { + writer_.Verify(source, target, std::move(client)); + } + + image_writer::ImageWriterHandler writer_; + + DISALLOW_COPY_AND_ASSIGN(RemovableStorageWriterImpl); +}; + #if defined(OS_WIN) class WiFiCredentialsGetterImpl : public extensions::mojom::WiFiCredentialsGetter { @@ -169,12 +203,16 @@ // services to the interface registry. if (running_elevated) { #if defined(OS_WIN) + registry->AddInterface(base::Bind(&RemovableStorageWriterImpl::Create)); registry->AddInterface(base::Bind(&WiFiCredentialsGetterImpl::Create)); #endif return; } registry->AddInterface(base::Bind(&MediaParserImpl::Create, utility_client)); +#if !defined(OS_WIN) + registry->AddInterface(base::Bind(&RemovableStorageWriterImpl::Create)); +#endif } bool ExtensionsHandler::OnMessageReceived(const IPC::Message& message) {
diff --git a/chrome/utility/image_writer/image_writer_handler.cc b/chrome/utility/image_writer/image_writer_handler.cc index 3adf42e..d63edb7 100644 --- a/chrome/utility/image_writer/image_writer_handler.cc +++ b/chrome/utility/image_writer/image_writer_handler.cc
@@ -4,55 +4,48 @@ #include "chrome/utility/image_writer/image_writer_handler.h" +#include "base/bind.h" #include "base/files/file_path.h" -#include "chrome/common/extensions/chrome_utility_extensions_messages.h" +#include "base/optional.h" +#include "chrome/common/extensions/removable_storage_writer.mojom.h" #include "chrome/utility/image_writer/error_messages.h" -#include "content/public/utility/utility_thread.h" + +namespace { + +bool IsTestDevice(const base::FilePath& device) { + return device.AsUTF8Unsafe() == + extensions::mojom::RemovableStorageWriter::kTestDevice; +} + +base::FilePath MakeTestDevicePath(const base::FilePath& image) { + return image.ReplaceExtension(FILE_PATH_LITERAL("out")); +} + +} // namespace namespace image_writer { -ImageWriterHandler::ImageWriterHandler() {} -ImageWriterHandler::~ImageWriterHandler() {} +ImageWriterHandler::ImageWriterHandler() = default; -void ImageWriterHandler::SendSucceeded() { - Send(new ChromeUtilityHostMsg_ImageWriter_Succeeded()); - content::UtilityThread::Get()->ReleaseProcessIfNeeded(); -} +ImageWriterHandler::~ImageWriterHandler() = default; -void ImageWriterHandler::SendCancelled() { - Send(new ChromeUtilityHostMsg_ImageWriter_Cancelled()); - content::UtilityThread::Get()->ReleaseProcessIfNeeded(); -} +void ImageWriterHandler::Write( + const base::FilePath& image, + const base::FilePath& device, + extensions::mojom::RemovableStorageWriterClientPtr client) { + client_ = std::move(client); + client_.set_connection_error_handler( + base::Bind(&ImageWriterHandler::Cancel, base::Unretained(this))); -void ImageWriterHandler::SendFailed(const std::string& message) { - Send(new ChromeUtilityHostMsg_ImageWriter_Failed(message)); - content::UtilityThread::Get()->ReleaseProcessIfNeeded(); -} + base::FilePath target_device = device; + const bool test_mode = IsTestDevice(device); + if (test_mode) + target_device = MakeTestDevicePath(image); -void ImageWriterHandler::SendProgress(int64_t progress) { - Send(new ChromeUtilityHostMsg_ImageWriter_Progress(progress)); -} - -void ImageWriterHandler::Send(IPC::Message* msg) { - content::UtilityThread::Get()->Send(msg); -} - -bool ImageWriterHandler::OnMessageReceived(const IPC::Message& message) { - bool handled = true; - IPC_BEGIN_MESSAGE_MAP(ImageWriterHandler, message) - IPC_MESSAGE_HANDLER(ChromeUtilityMsg_ImageWriter_Write, OnWriteStart) - IPC_MESSAGE_HANDLER(ChromeUtilityMsg_ImageWriter_Verify, OnVerifyStart) - IPC_MESSAGE_HANDLER(ChromeUtilityMsg_ImageWriter_Cancel, OnCancel) - IPC_MESSAGE_UNHANDLED(handled = false) - IPC_END_MESSAGE_MAP() - return handled; -} - -void ImageWriterHandler::OnWriteStart(const base::FilePath& image, - const base::FilePath& device) { + // https://crbug.com/352442 if (!image_writer_.get() || image != image_writer_->GetImagePath() || - device != image_writer_->GetDevicePath()) { - image_writer_.reset(new ImageWriter(this, image, device)); + target_device != image_writer_->GetDevicePath()) { + image_writer_.reset(new ImageWriter(this, image, target_device)); } if (image_writer_->IsRunning()) { @@ -60,6 +53,11 @@ return; } + if (test_mode) { + image_writer_->Write(); + return; + } + if (!image_writer_->IsValidDevice()) { SendFailed(error::kInvalidDevice); return; @@ -69,11 +67,23 @@ base::Bind(&ImageWriter::Write, image_writer_->AsWeakPtr())); } -void ImageWriterHandler::OnVerifyStart(const base::FilePath& image, - const base::FilePath& device) { +void ImageWriterHandler::Verify( + const base::FilePath& image, + const base::FilePath& device, + extensions::mojom::RemovableStorageWriterClientPtr client) { + client_ = std::move(client); + client_.set_connection_error_handler( + base::Bind(&ImageWriterHandler::Cancel, base::Unretained(this))); + + base::FilePath target_device = device; + const bool test_mode = IsTestDevice(device); + if (test_mode) + target_device = MakeTestDevicePath(image); + + // https://crbug.com/352442 if (!image_writer_.get() || image != image_writer_->GetImagePath() || - device != image_writer_->GetDevicePath()) { - image_writer_.reset(new ImageWriter(this, image, device)); + target_device != image_writer_->GetDevicePath()) { + image_writer_.reset(new ImageWriter(this, image, target_device)); } if (image_writer_->IsRunning()) { @@ -81,6 +91,11 @@ return; } + if (test_mode) { + image_writer_->Verify(); + return; + } + if (!image_writer_->IsValidDevice()) { SendFailed(error::kInvalidDevice); return; @@ -89,12 +104,24 @@ image_writer_->Verify(); } -void ImageWriterHandler::OnCancel() { - if (image_writer_.get()) { +void ImageWriterHandler::SendProgress(int64_t progress) { + client_->Progress(progress); +} + +void ImageWriterHandler::SendSucceeded() { + client_->Complete(base::nullopt); + client_.reset(); +} + +void ImageWriterHandler::SendFailed(const std::string& error) { + client_->Complete(error); + client_.reset(); +} + +void ImageWriterHandler::Cancel() { + if (image_writer_) image_writer_->Cancel(); - } else { - SendCancelled(); - } + client_.reset(); } } // namespace image_writer
diff --git a/chrome/utility/image_writer/image_writer_handler.h b/chrome/utility/image_writer/image_writer_handler.h index 4543dec..d61fc91 100644 --- a/chrome/utility/image_writer/image_writer_handler.h +++ b/chrome/utility/image_writer/image_writer_handler.h
@@ -10,9 +10,8 @@ #include <memory> #include <string> +#include "chrome/common/extensions/removable_storage_writer.mojom.h" #include "chrome/utility/image_writer/image_writer.h" -#include "chrome/utility/utility_message_handler.h" -#include "ipc/ipc_message.h" namespace base { class FilePath; @@ -20,31 +19,29 @@ namespace image_writer { -// A handler for messages related to writing images. This class is added as a -// handler in ChromeContentUtilityClient. -class ImageWriterHandler : public UtilityMessageHandler { +class ImageWriterHandler { public: ImageWriterHandler(); - ~ImageWriterHandler() override; + ~ImageWriterHandler(); - // Methods for sending the different messages back to the browser process. - // Generally should be called by chrome::image_writer::ImageWriter. - virtual void SendSucceeded(); - virtual void SendCancelled(); - virtual void SendFailed(const std::string& message); + void Write(const base::FilePath& image, + const base::FilePath& device, + extensions::mojom::RemovableStorageWriterClientPtr client); + void Verify(const base::FilePath& image, + const base::FilePath& device, + extensions::mojom::RemovableStorageWriterClientPtr client); + + // Methods for sending the different messages back to the |client_|. + // Generally should be called by image_writer::ImageWriter. virtual void SendProgress(int64_t progress); + virtual void SendSucceeded(); + virtual void SendFailed(const std::string& error); + virtual void SendCancelled() {} private: - bool OnMessageReceived(const IPC::Message& message) override; + void Cancel(); - // Small wrapper for sending on the UtilityProcess. - void Send(IPC::Message* msg); - - // Message handlers. - void OnWriteStart(const base::FilePath& image, const base::FilePath& device); - void OnVerifyStart(const base::FilePath& image, const base::FilePath& device); - void OnCancel(); - + extensions::mojom::RemovableStorageWriterClientPtr client_; std::unique_ptr<ImageWriter> image_writer_; };
diff --git a/chrome/utility/image_writer/image_writer_unittest.cc b/chrome/utility/image_writer/image_writer_unittest.cc index 8db4959a..d935c36 100644 --- a/chrome/utility/image_writer/image_writer_unittest.cc +++ b/chrome/utility/image_writer/image_writer_unittest.cc
@@ -62,7 +62,6 @@ MOCK_METHOD1(SendProgress, void(int64_t)); MOCK_METHOD1(SendFailed, void(const std::string& message)); MOCK_METHOD0(SendSucceeded, void()); - MOCK_METHOD1(OnMessageReceived, bool(const IPC::Message& message)); }; // This Mock has the additional feature that it will start verification when
diff --git a/components/signin/core/browser/about_signin_internals.cc b/components/signin/core/browser/about_signin_internals.cc index ec21740..d925e37 100644 --- a/components/signin/core/browser/about_signin_internals.cc +++ b/components/signin/core/browser/about_signin_internals.cc
@@ -28,6 +28,7 @@ #include "components/signin/core/browser/signin_manager.h" #include "components/signin/core/common/profile_management_switches.h" #include "components/signin/core/common/signin_switches.h" +#include "google_apis/gaia/oauth2_token_service_delegate.h" #include "net/base/backoff_entry.h" using base::Time; @@ -88,6 +89,30 @@ return std::string(); } +std::string TokenServiceLoadCredentialsStateToLabel( + OAuth2TokenServiceDelegate::LoadCredentialsState state) { + switch (state) { + case OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_UNKNOWN: + return "Unknown"; + case OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_NOT_STARTED: + return "Load credentials not started"; + case OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_IN_PROGRESS: + return "Load credentials in progress"; + case OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS: + return "Load credentials finished with success"; + case OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_FINISHED_WITH_DB_ERRORS: + return "Load credentials failed with database errors"; + case OAuth2TokenServiceDelegate:: + LOAD_CREDENTIALS_FINISHED_WITH_DECRYPT_ERRORS: + return "Load credentials failed with decrypt errors"; + case OAuth2TokenServiceDelegate:: + LOAD_CREDENTIALS_FINISHED_WITH_UNKNOWN_ERRORS: + return "Load credentials failed with unknown errors"; + } + NOTREACHED(); + return std::string(); +} + #if !defined (OS_CHROMEOS) std::string SigninStatusFieldToLabel(TimedSigninStatusField field) { switch (field) { @@ -334,6 +359,10 @@ NotifyObservers(); } +void AboutSigninInternals::OnRefreshTokensLoaded() { + NotifyObservers(); +} + void AboutSigninInternals::OnTokenRemoved( const std::string& account_id, const OAuth2TokenService::ScopeSet& scopes) { @@ -514,6 +543,10 @@ switches::IsEnableAccountConsistency() == true ? "On" : "Off"); AddSectionEntry(basic_info, "Signin Status", signin_manager->IsAuthenticated() ? "Signed In" : "Not Signed In"); + OAuth2TokenServiceDelegate::LoadCredentialsState load_tokens_state = + token_service->GetDelegate()->GetLoadCredentialsState(); + AddSectionEntry(basic_info, "TokenService Status", + TokenServiceLoadCredentialsStateToLabel(load_tokens_state)); // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is // fixed.
diff --git a/components/signin/core/browser/about_signin_internals.h b/components/signin/core/browser/about_signin_internals.h index 3caeb6eb..8e42feab 100644 --- a/components/signin/core/browser/about_signin_internals.h +++ b/components/signin/core/browser/about_signin_internals.h
@@ -37,6 +37,7 @@ class AboutSigninInternals : public KeyedService, public signin_internals_util::SigninDiagnosticsObserver, + public OAuth2TokenService::Observer, public OAuth2TokenService::DiagnosticsObserver, public GaiaCookieManagerService::Observer, SigninManagerBase::Observer, @@ -184,6 +185,9 @@ void OnTokenRemoved(const std::string& account_id, const OAuth2TokenService::ScopeSet& scopes) override; + // OAuth2TokenServiceDelegate::Observer implementations. + void OnRefreshTokensLoaded() override; + // SigninManagerBase::Observer implementations. void GoogleSigninFailed(const GoogleServiceAuthError& error) override; void GoogleSigninSucceeded(const std::string& account_id,
diff --git a/components/signin/core/browser/signin_internals_util.h b/components/signin/core/browser/signin_internals_util.h index 3dda94c..03e5c6fa8 100644 --- a/components/signin/core/browser/signin_internals_util.h +++ b/components/signin/core/browser/signin_internals_util.h
@@ -69,13 +69,7 @@ // Credentials and signin related changes. virtual void NotifySigninValueChanged(const TimedSigninStatusField& field, const std::string& value) {} - // OAuth tokens related changes. - virtual void NotifyTokenReceivedSuccess(const std::string& token_name, - const std::string& token, - bool update_time) {} - virtual void NotifyTokenReceivedFailure(const std::string& token_name, - const std::string& error) {} - virtual void NotifyClearStoredToken(const std::string& token_name) {}}; +}; // Gets the first 6 hex characters of the SHA256 hash of the passed in string. // These are enough to perform equality checks across a single users tokens,
diff --git a/components/signin/core/browser/webdata/token_service_table.cc b/components/signin/core/browser/webdata/token_service_table.cc index b367f8f..8c9075c9 100644 --- a/components/signin/core/browser/webdata/token_service_table.cc +++ b/components/signin/core/browser/webdata/token_service_table.cc
@@ -97,7 +97,7 @@ return s.Run(); } -bool TokenServiceTable::GetAllTokens( +TokenServiceTable::Result TokenServiceTable::GetAllTokens( std::map<std::string, std::string>* tokens) { sql::Statement s(db_->GetUniqueStatement( "SELECT service, encrypted_token FROM token_service")); @@ -107,10 +107,10 @@ if (!s.is_valid()) { LOG(ERROR) << "Failed to load tokens (invalid SQL statement)."; - return false; + return TOKEN_DB_RESULT_SQL_INVALID_STATEMENT; } - bool read_all_tokens_result = true; + Result read_all_tokens_result = TOKEN_DB_RESULT_SUCCESS; while (s.Step()) { ReadOneTokenResult read_token_result = READ_ONE_TOKEN_MAX_VALUE; @@ -129,12 +129,12 @@ // may fail (see http://crbug.com/686485). LOG(ERROR) << "Failed to decrypt token for service " << service; read_token_result = READ_ONE_TOKEN_DB_SUCCESS_DECRYPT_FAILED; - read_all_tokens_result = false; + read_all_tokens_result = TOKEN_DB_RESULT_DECRYPT_ERROR; } } else { LOG(ERROR) << "Bad token entry for service " << service; read_token_result = READ_ONE_TOKEN_DB_FAILED_BAD_ENTRY; - read_all_tokens_result = false; + read_all_tokens_result = TOKEN_DB_RESULT_BAD_ENTRY; } DCHECK_LT(read_token_result, READ_ONE_TOKEN_MAX_VALUE); UMA_HISTOGRAM_ENUMERATION("Signin.TokenTable.ReadTokenFromDBResult",
diff --git a/components/signin/core/browser/webdata/token_service_table.h b/components/signin/core/browser/webdata/token_service_table.h index 70cf20e..b6d750f 100644 --- a/components/signin/core/browser/webdata/token_service_table.h +++ b/components/signin/core/browser/webdata/token_service_table.h
@@ -16,6 +16,13 @@ class TokenServiceTable : public WebDatabaseTable { public: + enum Result { + TOKEN_DB_RESULT_SQL_INVALID_STATEMENT, + TOKEN_DB_RESULT_BAD_ENTRY, + TOKEN_DB_RESULT_DECRYPT_ERROR, + TOKEN_DB_RESULT_SUCCESS + }; + TokenServiceTable() {} ~TokenServiceTable() override {} @@ -36,7 +43,7 @@ // Retrieves all tokens previously set with SetTokenForService. // Returns true if there were tokens and we decrypted them, // false if there was a failure somehow - bool GetAllTokens(std::map<std::string, std::string>* tokens); + Result GetAllTokens(std::map<std::string, std::string>* tokens); // Store a token in the token_service table. Stored encrypted. May cause // a mac keychain popup.
diff --git a/components/signin/core/browser/webdata/token_web_data.cc b/components/signin/core/browser/webdata/token_web_data.cc index 0c642552..d8c405f 100644 --- a/components/signin/core/browser/webdata/token_web_data.cc +++ b/components/signin/core/browser/webdata/token_web_data.cc
@@ -47,10 +47,10 @@ } std::unique_ptr<WDTypedResult> GetAllTokens(WebDatabase* db) { - std::map<std::string, std::string> map; - TokenServiceTable::FromWebDatabase(db)->GetAllTokens(&map); - return base::MakeUnique<WDResult<std::map<std::string, std::string>>>( - TOKEN_RESULT, map); + TokenResult result; + result.db_result = + TokenServiceTable::FromWebDatabase(db)->GetAllTokens(&result.tokens); + return base::MakeUnique<WDResult<TokenResult>>(TOKEN_RESULT, result); } protected: @@ -62,6 +62,11 @@ friend class base::DeleteHelper<TokenWebDataBackend>; }; +TokenResult::TokenResult() + : db_result(TokenServiceTable::TOKEN_DB_RESULT_SQL_INVALID_STATEMENT) {} +TokenResult::TokenResult(const TokenResult& other) = default; +TokenResult::~TokenResult(){}; + TokenWebData::TokenWebData( scoped_refptr<WebDatabaseService> wdbs, scoped_refptr<base::SingleThreadTaskRunner> ui_thread,
diff --git a/components/signin/core/browser/webdata/token_web_data.h b/components/signin/core/browser/webdata/token_web_data.h index ceec9cd..f47f371 100644 --- a/components/signin/core/browser/webdata/token_web_data.h +++ b/components/signin/core/browser/webdata/token_web_data.h
@@ -18,6 +18,7 @@ #include "base/location.h" #include "base/macros.h" #include "base/memory/ref_counted.h" +#include "components/signin/core/browser/webdata/token_service_table.h" #include "components/webdata/common/web_data_results.h" #include "components/webdata/common/web_data_service_base.h" #include "components/webdata/common/web_data_service_consumer.h" @@ -31,6 +32,16 @@ class WebDatabaseService; class WebDataServiceConsumer; +// The result of a get tokens operation. +struct TokenResult { + TokenResult(); + TokenResult(const TokenResult& other); + ~TokenResult(); + + TokenServiceTable::Result db_result; + std::map<std::string, std::string> tokens; +}; + // TokenWebData is a data repository for storage of authentication tokens. class TokenWebData : public WebDataServiceBase {
diff --git a/components/webdata/common/web_data_results.h b/components/webdata/common/web_data_results.h index aea8fce..1577866 100644 --- a/components/webdata/common/web_data_results.h +++ b/components/webdata/common/web_data_results.h
@@ -25,7 +25,7 @@ PASSWORD_IE7_RESULT, // WDResult<IE7PasswordInfo> #endif // WEB_APP_IMAGES, // WDResult<WDAppImagesResult> - TOKEN_RESULT, // WDResult<std::vector<std::string>> + TOKEN_RESULT, // WDResult<TokenResult> AUTOFILL_VALUE_RESULT, // WDResult<std::vector<base::string16>> AUTOFILL_CHANGES, // WDResult<std::vector<AutofillChange>> AUTOFILL_PROFILE_RESULT, // WDResult<AutofillProfile>
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn index 73d3316..0a0561c 100644 --- a/content/browser/BUILD.gn +++ b/content/browser/BUILD.gn
@@ -76,6 +76,7 @@ "//device/geolocation/public/interfaces", "//device/nfc:mojo_bindings", "//device/power_save_blocker", + "//device/screen_orientation/public/interfaces", "//device/sensors", "//device/vibration", "//device/vr", @@ -1209,6 +1210,8 @@ "resource_context_impl.h", "screen_orientation/screen_orientation_delegate_win.cc", "screen_orientation/screen_orientation_delegate_win.h", + "screen_orientation/screen_orientation_provider.cc", + "screen_orientation/screen_orientation_provider.h", "service_manager/merge_dictionary.cc", "service_manager/merge_dictionary.h", "service_manager/service_manager_context.cc", @@ -1798,7 +1801,6 @@ deps -= [ "//device/battery" ] deps += [ "//content/public/android:jni", - "//device/screen_orientation/public/interfaces", "//media", "//media/capture/content/android", "//media/capture/video/android",
diff --git a/content/browser/android/content_view_core_impl.cc b/content/browser/android/content_view_core_impl.cc index 4b5bbdf0..4cfea45b 100644 --- a/content/browser/android/content_view_core_impl.cc +++ b/content/browser/android/content_view_core_impl.cc
@@ -43,7 +43,6 @@ #include "content/public/browser/favicon_status.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_view_host.h" -#include "content/public/browser/screen_orientation_provider.h" #include "content/public/browser/ssl_host_state_delegate.h" #include "content/public/browser/web_contents.h" #include "content/public/common/content_client.h" @@ -1375,9 +1374,7 @@ if (rwhv) rwhv->UpdateScreenInfo(GetViewAndroid()); - static_cast<WebContentsImpl*>(web_contents()) - ->GetScreenOrientationProvider() - ->OnOrientationChange(); + static_cast<WebContentsImpl*>(web_contents())->OnScreenOrientationChange(); } jint ContentViewCoreImpl::GetCurrentRenderProcessId(
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc index bf849b7..bfab0fb 100644 --- a/content/browser/browser_main_loop.cc +++ b/content/browser/browser_main_loop.cc
@@ -127,7 +127,6 @@ #include "content/browser/media/android/browser_media_player_manager.h" #include "content/browser/renderer_host/context_provider_factory_impl_android.h" #include "content/browser/screen_orientation/screen_orientation_delegate_android.h" -#include "content/public/browser/screen_orientation_provider.h" #include "media/base/android/media_client_android.h" #include "ui/android/screen_android.h" #include "ui/display/screen.h" @@ -796,7 +795,6 @@ "BrowserMainLoop::Subsystem:ScreenOrientationProvider"); screen_orientation_delegate_.reset( new ScreenOrientationDelegateAndroid()); - ScreenOrientationProvider::SetDelegate(screen_orientation_delegate_.get()); } #endif
diff --git a/content/browser/notifications/OWNERS b/content/browser/notifications/OWNERS index 0563faf7..818507f3 100644 --- a/content/browser/notifications/OWNERS +++ b/content/browser/notifications/OWNERS
@@ -1,3 +1,7 @@ +# This OWNERS file also covers: +# +# //content/test/mock_platform_notification_service.* + johnme@chromium.org mkwst@chromium.org mvanouwerkerk@chromium.org
diff --git a/content/browser/notifications/platform_notification_context_unittest.cc b/content/browser/notifications/platform_notification_context_unittest.cc index 4aee64b..945fd16a 100644 --- a/content/browser/notifications/platform_notification_context_unittest.cc +++ b/content/browser/notifications/platform_notification_context_unittest.cc
@@ -15,8 +15,11 @@ #include "content/browser/service_worker/service_worker_context_wrapper.h" #include "content/common/service_worker/service_worker_types.h" #include "content/public/browser/notification_database_data.h" +#include "content/public/common/notification_resources.h" #include "content/public/test/test_browser_context.h" #include "content/public/test/test_browser_thread_bundle.h" +#include "content/test/mock_platform_notification_service.h" +#include "content/test/test_content_browser_client.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" @@ -25,6 +28,19 @@ // Fake Service Worker registration id to use in tests requiring one. const int64_t kFakeServiceWorkerRegistrationId = 42; +class NotificationBrowserClient : public TestContentBrowserClient { + public: + NotificationBrowserClient() + : platform_notification_service_(new MockPlatformNotificationService()) {} + + PlatformNotificationService* GetPlatformNotificationService() override { + return platform_notification_service_.get(); + } + + private: + std::unique_ptr<PlatformNotificationService> platform_notification_service_; +}; + class PlatformNotificationContextTest : public ::testing::Test { public: PlatformNotificationContextTest() @@ -484,4 +500,69 @@ } } +TEST_F(PlatformNotificationContextTest, SynchronizeNotifications) { + NotificationBrowserClient notification_browser_client; + SetBrowserClientForTesting(¬ification_browser_client); + + scoped_refptr<PlatformNotificationContextImpl> context = + CreatePlatformNotificationContext(); + + GURL origin("https://example.com"); + NotificationDatabaseData notification_database_data; + notification_database_data.service_worker_registration_id = + kFakeServiceWorkerRegistrationId; + PlatformNotificationData notification_data; + content::NotificationResources notification_resources; + + context->WriteNotificationData( + origin, notification_database_data, + base::Bind(&PlatformNotificationContextTest::DidWriteNotificationData, + base::Unretained(this))); + + base::RunLoop().RunUntilIdle(); + ASSERT_TRUE(success()); + EXPECT_FALSE(notification_id().empty()); + + PlatformNotificationService* service = + notification_browser_client.GetPlatformNotificationService(); + + service->DisplayPersistentNotification(browser_context(), notification_id(), + origin, origin, notification_data, + notification_resources); + + std::vector<NotificationDatabaseData> notification_database_datas; + context->ReadAllNotificationDataForServiceWorkerRegistration( + origin, kFakeServiceWorkerRegistrationId, + base::Bind(&PlatformNotificationContextTest::DidReadAllNotificationDatas, + base::Unretained(this), ¬ification_database_datas)); + + base::RunLoop().RunUntilIdle(); + + ASSERT_TRUE(success()); + ASSERT_EQ(1u, notification_database_datas.size()); + + // Delete the notification from the display service without removing it from + // the database. It should automatically synchronize on the next read. + service->ClosePersistentNotification(browser_context(), notification_id()); + context->ReadAllNotificationDataForServiceWorkerRegistration( + origin, kFakeServiceWorkerRegistrationId, + base::Bind(&PlatformNotificationContextTest::DidReadAllNotificationDatas, + base::Unretained(this), ¬ification_database_datas)); + base::RunLoop().RunUntilIdle(); + + ASSERT_TRUE(success()); + ASSERT_EQ(0u, notification_database_datas.size()); + + context->ReadNotificationData( + notification_id(), origin, + base::Bind(&PlatformNotificationContextTest::DidReadNotificationData, + base::Unretained(this))); + + base::RunLoop().RunUntilIdle(); + + // The notification was removed, so we shouldn't be able to read it from + // the database anymore. + EXPECT_FALSE(success()); +} + } // namespace content
diff --git a/content/browser/screen_orientation/screen_orientation_delegate_android.cc b/content/browser/screen_orientation/screen_orientation_delegate_android.cc index 6a0dec1..d534d30 100644 --- a/content/browser/screen_orientation/screen_orientation_delegate_android.cc +++ b/content/browser/screen_orientation/screen_orientation_delegate_android.cc
@@ -5,6 +5,7 @@ #include "content/browser/screen_orientation/screen_orientation_delegate_android.h" #include "content/browser/android/content_view_core_impl.h" +#include "content/browser/screen_orientation/screen_orientation_provider.h" #include "jni/ScreenOrientationProvider_jni.h" #include "ui/android/window_android.h" #include "ui/gfx/native_widget_types.h" @@ -12,9 +13,11 @@ namespace content { ScreenOrientationDelegateAndroid::ScreenOrientationDelegateAndroid() { + ScreenOrientationProvider::SetDelegate(this); } ScreenOrientationDelegateAndroid::~ScreenOrientationDelegateAndroid() { + ScreenOrientationProvider::SetDelegate(nullptr); } // static
diff --git a/content/browser/screen_orientation/screen_orientation_delegate_win.cc b/content/browser/screen_orientation/screen_orientation_delegate_win.cc index d4bc93f..75413de 100644 --- a/content/browser/screen_orientation/screen_orientation_delegate_win.cc +++ b/content/browser/screen_orientation/screen_orientation_delegate_win.cc
@@ -6,7 +6,7 @@ #include <windows.h> -#include "content/public/browser/screen_orientation_provider.h" +#include "content/browser/screen_orientation/screen_orientation_provider.h" namespace {
diff --git a/content/public/browser/screen_orientation_provider.cc b/content/browser/screen_orientation/screen_orientation_provider.cc similarity index 88% rename from content/public/browser/screen_orientation_provider.cc rename to content/browser/screen_orientation/screen_orientation_provider.cc index db08e33..1ab00dfa 100644 --- a/content/public/browser/screen_orientation_provider.cc +++ b/content/browser/screen_orientation/screen_orientation_provider.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/public/browser/screen_orientation_provider.h" +#include "content/browser/screen_orientation/screen_orientation_provider.h" #include "base/callback_helpers.h" #include "content/browser/renderer_host/render_view_host_impl.h" @@ -24,8 +24,7 @@ lock_applied_(false), bindings_(web_contents, this) {} -ScreenOrientationProvider::~ScreenOrientationProvider() { -} +ScreenOrientationProvider::~ScreenOrientationProvider() = default; void ScreenOrientationProvider::LockOrientation( blink::WebScreenOrientationLockType orientation, @@ -121,7 +120,8 @@ } void ScreenOrientationProvider::DidToggleFullscreenModeForTab( - bool entered_fullscreen, bool will_cause_resize) { + bool entered_fullscreen, + bool will_cause_resize) { if (!lock_applied_ || !delegate_) return; @@ -144,7 +144,7 @@ } blink::WebScreenOrientationLockType - ScreenOrientationProvider::GetNaturalLockType() const { +ScreenOrientationProvider::GetNaturalLockType() const { RenderWidgetHost* rwh = web_contents()->GetRenderViewHost()->GetWidget(); if (!rwh) return blink::WebScreenOrientationLockDefault; @@ -187,26 +187,26 @@ switch (lock) { case blink::WebScreenOrientationLockPortraitPrimary: return screen_info.orientation_type == - SCREEN_ORIENTATION_VALUES_PORTRAIT_PRIMARY; + SCREEN_ORIENTATION_VALUES_PORTRAIT_PRIMARY; case blink::WebScreenOrientationLockPortraitSecondary: return screen_info.orientation_type == - SCREEN_ORIENTATION_VALUES_PORTRAIT_SECONDARY; + SCREEN_ORIENTATION_VALUES_PORTRAIT_SECONDARY; case blink::WebScreenOrientationLockLandscapePrimary: return screen_info.orientation_type == - SCREEN_ORIENTATION_VALUES_LANDSCAPE_PRIMARY; + SCREEN_ORIENTATION_VALUES_LANDSCAPE_PRIMARY; case blink::WebScreenOrientationLockLandscapeSecondary: return screen_info.orientation_type == - SCREEN_ORIENTATION_VALUES_LANDSCAPE_SECONDARY; + SCREEN_ORIENTATION_VALUES_LANDSCAPE_SECONDARY; case blink::WebScreenOrientationLockLandscape: return screen_info.orientation_type == - SCREEN_ORIENTATION_VALUES_LANDSCAPE_PRIMARY || - screen_info.orientation_type == - SCREEN_ORIENTATION_VALUES_LANDSCAPE_SECONDARY; + SCREEN_ORIENTATION_VALUES_LANDSCAPE_PRIMARY || + screen_info.orientation_type == + SCREEN_ORIENTATION_VALUES_LANDSCAPE_SECONDARY; case blink::WebScreenOrientationLockPortrait: return screen_info.orientation_type == - SCREEN_ORIENTATION_VALUES_PORTRAIT_PRIMARY || - screen_info.orientation_type == - SCREEN_ORIENTATION_VALUES_PORTRAIT_SECONDARY; + SCREEN_ORIENTATION_VALUES_PORTRAIT_PRIMARY || + screen_info.orientation_type == + SCREEN_ORIENTATION_VALUES_PORTRAIT_SECONDARY; case blink::WebScreenOrientationLockAny: return true; case blink::WebScreenOrientationLockNatural: @@ -219,4 +219,4 @@ return false; } -} // namespace content +} // namespace content
diff --git a/content/public/browser/screen_orientation_provider.h b/content/browser/screen_orientation/screen_orientation_provider.h similarity index 92% rename from content/public/browser/screen_orientation_provider.h rename to content/browser/screen_orientation/screen_orientation_provider.h index 78cbe55..0898070 100644 --- a/content/public/browser/screen_orientation_provider.h +++ b/content/browser/screen_orientation/screen_orientation_provider.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CONTENT_PUBLIC_BROWSER_SCREEN_ORIENTATION_PROVIDER_H_ -#define CONTENT_PUBLIC_BROWSER_SCREEN_ORIENTATION_PROVIDER_H_ +#ifndef CONTENT_BROWSER_SCREEN_ORIENTATION_SCREEN_ORIENTATION_PROVIDER_H_ +#define CONTENT_BROWSER_SCREEN_ORIENTATION_SCREEN_ORIENTATION_PROVIDER_H_ #include "base/callback.h" #include "base/compiler_specific.h" @@ -80,6 +80,6 @@ DISALLOW_COPY_AND_ASSIGN(ScreenOrientationProvider); }; -} // namespace content +} // namespace content -#endif // CONTENT_PUBLIC_BROWSER_SCREEN_ORIENTATION_PROVIDER_H_ +#endif // CONTENT_BROWSER_SCREEN_ORIENTATION_SCREEN_ORIENTATION_PROVIDER_H_
diff --git a/content/browser/screen_orientation/screen_orientation_provider_unittest.cc b/content/browser/screen_orientation/screen_orientation_provider_unittest.cc index ba44f6f0..2a7a4e5 100644 --- a/content/browser/screen_orientation/screen_orientation_provider_unittest.cc +++ b/content/browser/screen_orientation/screen_orientation_provider_unittest.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/public/browser/screen_orientation_provider.h" +#include "content/browser/screen_orientation/screen_orientation_provider.h" #include "base/optional.h" #include "base/run_loop.h" @@ -100,14 +100,14 @@ void CallLockAndGetResult( blink::WebScreenOrientationLockType orientation, base::Optional<ScreenOrientationLockResult>* out_result) { - contents()->GetScreenOrientationProvider()->LockOrientation( + contents()->GetScreenOrientationProviderForTesting()->LockOrientation( orientation, base::Bind(&LockResultCallback, out_result)); base::RunLoop().RunUntilIdle(); } void CallUnlock() { - contents()->GetScreenOrientationProvider()->UnlockOrientation(); + contents()->GetScreenOrientationProviderForTesting()->UnlockOrientation(); } private:
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc index 01a81011..b17fddd 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc
@@ -74,6 +74,7 @@ #include "content/browser/renderer_host/render_widget_host_input_event_router.h" #include "content/browser/renderer_host/render_widget_host_view_base.h" #include "content/browser/renderer_host/text_input_manager.h" +#include "content/browser/screen_orientation/screen_orientation_provider.h" #include "content/browser/site_instance_impl.h" #include "content/browser/web_contents/web_contents_view_child_frame.h" #include "content/browser/web_contents/web_contents_view_guest.h" @@ -107,7 +108,6 @@ #include "content/public/browser/notification_types.h" #include "content/public/browser/render_widget_host_iterator.h" #include "content/public/browser/resource_request_details.h" -#include "content/public/browser/screen_orientation_provider.h" #include "content/public/browser/security_style_explanations.h" #include "content/public/browser/ssl_status.h" #include "content/public/browser/storage_partition.h" @@ -324,6 +324,11 @@ return FromRenderFrameHost(frame_tree_node->current_frame_host()); } +void WebContents::SetScreenOrientationDelegate( + ScreenOrientationDelegate* delegate) { + ScreenOrientationProvider::SetDelegate(delegate); +} + // WebContentsImpl::DestructionObserver ---------------------------------------- class WebContentsImpl::DestructionObserver : public WebContentsObserver { @@ -929,9 +934,9 @@ return view_.get(); } -ScreenOrientationProvider* WebContentsImpl::GetScreenOrientationProvider() - const { - return screen_orientation_provider_.get(); +void WebContentsImpl::OnScreenOrientationChange() { + DCHECK(screen_orientation_provider_); + return screen_orientation_provider_->OnOrientationChange(); } SkColor WebContentsImpl::GetThemeColor() const {
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h index 4ece254c..0eaec3a 100644 --- a/content/browser/web_contents/web_contents_impl.h +++ b/content/browser/web_contents/web_contents_impl.h
@@ -209,7 +209,11 @@ WebContentsView* GetView() const; - ScreenOrientationProvider* GetScreenOrientationProvider() const; + void OnScreenOrientationChange(); + + ScreenOrientationProvider* GetScreenOrientationProviderForTesting() const { + return screen_orientation_provider_.get(); + } bool should_normally_be_visible() { return should_normally_be_visible_; }
diff --git a/content/public/browser/BUILD.gn b/content/public/browser/BUILD.gn index dfaa1bb..973f205 100644 --- a/content/public/browser/BUILD.gn +++ b/content/public/browser/BUILD.gn
@@ -217,8 +217,6 @@ "resource_throttle.h", "save_page_type.h", "screen_orientation_delegate.h", - "screen_orientation_provider.cc", - "screen_orientation_provider.h", "security_style_explanation.h", "security_style_explanations.cc", "security_style_explanations.h", @@ -290,7 +288,6 @@ ] public_deps = [ - "//device/screen_orientation/public/interfaces", "//mojo/public/cpp/bindings", "//mojo/public/cpp/system", "//services/service_manager/public/cpp",
diff --git a/content/public/browser/web_contents.h b/content/public/browser/web_contents.h index ffcb4b1..5a509ef 100644 --- a/content/public/browser/web_contents.h +++ b/content/public/browser/web_contents.h
@@ -21,6 +21,7 @@ #include "content/public/browser/navigation_controller.h" #include "content/public/browser/page_navigator.h" #include "content/public/browser/save_page_type.h" +#include "content/public/browser/screen_orientation_delegate.h" #include "content/public/browser/site_instance.h" #include "content/public/browser/web_ui.h" #include "content/public/common/stop_find_action.h" @@ -191,6 +192,10 @@ CONTENT_EXPORT static WebContents* FromFrameTreeNodeId( int frame_tree_node_id); + // Sets delegate for platform specific screen orientation functionality. + CONTENT_EXPORT static void SetScreenOrientationDelegate( + ScreenOrientationDelegate* delegate); + ~WebContents() override {} // Intrinsic tab state -------------------------------------------------------
diff --git a/content/renderer/input/main_thread_event_queue.cc b/content/renderer/input/main_thread_event_queue.cc index fdee19f..383b87a 100644 --- a/content/renderer/input/main_thread_event_queue.cc +++ b/content/renderer/input/main_thread_event_queue.cc
@@ -4,7 +4,9 @@ #include "content/renderer/input/main_thread_event_queue.h" +#include "base/metrics/field_trial.h" #include "base/metrics/histogram_macros.h" +#include "base/strings/string_number_conversions.h" #include "content/common/input/event_with_latency_info.h" #include "content/common/input_messages.h" @@ -73,7 +75,26 @@ handle_raf_aligned_mouse_input_( base::FeatureList::IsEnabled(features::kRafAlignedMouseInputEvents)), main_task_runner_(main_task_runner), - renderer_scheduler_(renderer_scheduler) {} + renderer_scheduler_(renderer_scheduler) { + if (enable_non_blocking_due_to_main_thread_responsiveness_flag_) { + std::string group = base::FieldTrialList::FindFullName( + "MainThreadResponsivenessScrollIntervention"); + + // The group name will be of the form Enabled$THRESHOLD_MS. Trim the prefix + // "Enabled", and parse the threshold. + int threshold_ms = 0; + std::string prefix = "Enabled"; + group.erase(0, prefix.length()); + base::StringToInt(group, &threshold_ms); + + if (threshold_ms <= 0) { + enable_non_blocking_due_to_main_thread_responsiveness_flag_ = false; + } else { + main_thread_responsiveness_threshold_ = + base::TimeDelta::FromMilliseconds(threshold_ms); + } + } +} MainThreadEventQueue::~MainThreadEventQueue() {} @@ -123,7 +144,8 @@ if (enable_non_blocking_due_to_main_thread_responsiveness_flag_ && touch_event->dispatchType == blink::WebInputEvent::Blocking) { bool passive_due_to_unresponsive_main = - renderer_scheduler_->MainThreadSeemsUnresponsive(); + renderer_scheduler_->MainThreadSeemsUnresponsive( + main_thread_responsiveness_threshold_); if (passive_due_to_unresponsive_main) { touch_event->dispatchType = blink::WebInputEvent:: ListenersForcedNonBlockingDueToMainThreadResponsiveness;
diff --git a/content/renderer/input/main_thread_event_queue.h b/content/renderer/input/main_thread_event_queue.h index 09c16117..46e43cc 100644 --- a/content/renderer/input/main_thread_event_queue.h +++ b/content/renderer/input/main_thread_event_queue.h
@@ -149,12 +149,14 @@ bool IsRafAlignedEvent(const blink::WebInputEvent& event); friend class MainThreadEventQueueTest; + friend class MainThreadEventQueueInitializationTest; int routing_id_; MainThreadEventQueueClient* client_; std::unique_ptr<EventWithDispatchType> in_flight_event_; bool last_touch_start_forced_nonblocking_due_to_fling_; bool enable_fling_passive_listener_flag_; bool enable_non_blocking_due_to_main_thread_responsiveness_flag_; + base::TimeDelta main_thread_responsiveness_threshold_; bool handle_raf_aligned_touch_input_; bool handle_raf_aligned_mouse_input_;
diff --git a/content/renderer/input/main_thread_event_queue_unittest.cc b/content/renderer/input/main_thread_event_queue_unittest.cc index f7ccb8c..cda96df 100644 --- a/content/renderer/input/main_thread_event_queue_unittest.cc +++ b/content/renderer/input/main_thread_event_queue_unittest.cc
@@ -9,6 +9,7 @@ #include <vector> #include "base/macros.h" +#include "base/metrics/field_trial.h" #include "base/strings/string_util.h" #include "base/test/histogram_tester.h" #include "base/test/scoped_feature_list.h" @@ -711,4 +712,68 @@ testing::Range(0u, (kRafAlignedEnabledTouch | kRafAlignedEnabledMouse) + 1)); +class DummyMainThreadEventQueueClient : public MainThreadEventQueueClient { + void HandleEventOnMainThread(int routing_id, + const blink::WebCoalescedInputEvent* event, + const ui::LatencyInfo& latency, + InputEventDispatchType dispatch_type) override {} + + void SendInputEventAck(int routing_id, + blink::WebInputEvent::Type type, + InputEventAckState ack_result, + uint32_t touch_event_id) override {} + + void NeedsMainFrame(int routing_id) override {} +}; + +class MainThreadEventQueueInitializationTest + : public testing::Test { + public: + MainThreadEventQueueInitializationTest() + : field_trial_list_(new base::FieldTrialList(nullptr)) {} + + base::TimeDelta main_thread_responsiveness_threshold() { + return queue_->main_thread_responsiveness_threshold_; + } + + bool enable_non_blocking_due_to_main_thread_responsiveness_flag() { + return queue_->enable_non_blocking_due_to_main_thread_responsiveness_flag_; + } + + protected: + scoped_refptr<MainThreadEventQueue> queue_; + base::test::ScopedFeatureList feature_list_; + blink::scheduler::MockRendererScheduler renderer_scheduler_; + scoped_refptr<base::TestSimpleTaskRunner> main_task_runner_; + std::unique_ptr<base::FieldTrialList> field_trial_list_; + DummyMainThreadEventQueueClient dummy_main_thread_event_queue_client_; +}; + +TEST_F(MainThreadEventQueueInitializationTest, + MainThreadResponsivenessThresholdEnabled) { + feature_list_.InitFromCommandLine( + features::kMainThreadBusyScrollIntervention.name, ""); + + base::FieldTrialList::CreateFieldTrial( + "MainThreadResponsivenessScrollIntervention", "Enabled123"); + queue_ = new MainThreadEventQueue(kTestRoutingID, + &dummy_main_thread_event_queue_client_, + main_task_runner_, &renderer_scheduler_); + EXPECT_TRUE(enable_non_blocking_due_to_main_thread_responsiveness_flag()); + EXPECT_EQ(base::TimeDelta::FromMilliseconds(123), + main_thread_responsiveness_threshold()); +} + +TEST_F(MainThreadEventQueueInitializationTest, + MainThreadResponsivenessThresholdDisabled) { + base::FieldTrialList::CreateFieldTrial( + "MainThreadResponsivenessScrollIntervention", "Control"); + queue_ = new MainThreadEventQueue(kTestRoutingID, + &dummy_main_thread_event_queue_client_, + main_task_runner_, &renderer_scheduler_); + EXPECT_FALSE(enable_non_blocking_due_to_main_thread_responsiveness_flag()); + EXPECT_EQ(base::TimeDelta::FromMilliseconds(0), + main_thread_responsiveness_threshold()); +} + } // namespace content
diff --git a/content/renderer/render_process_impl.cc b/content/renderer/render_process_impl.cc index db042b9..824306e 100644 --- a/content/renderer/render_process_impl.cc +++ b/content/renderer/render_process_impl.cc
@@ -14,6 +14,7 @@ #include <stddef.h> +#include <algorithm> #include <vector> #include "base/bind.h" @@ -87,13 +88,10 @@ using StandbyThreadPolicy = base::SchedulerWorkerPoolParams::StandbyThreadPolicy; using ThreadPriority = base::ThreadPriority; - constexpr size_t kMaxNumThreadsInBackgroundPool = 1; - constexpr size_t kMaxNumThreadsInBackgroundFileIOPool = 1; + constexpr int kMaxNumThreadsInBackgroundPool = 1; + constexpr int kMaxNumThreadsInBackgroundFileIOPool = 1; constexpr int kMaxNumThreadsInForegroundPoolLowerBound = 2; - constexpr int kMaxNumThreadsInForegroundPoolUpperBound = 4; - constexpr double kMaxNumThreadsInForegroundPoolCoresMultiplier = 1; - constexpr int kMaxNumThreadsInForegroundPoolOffset = 0; - constexpr size_t kMaxNumThreadsInForegroundFileIOPool = 1; + constexpr int kMaxNumThreadsInForegroundFileIOPool = 1; constexpr auto kSuggestedReclaimTime = base::TimeDelta::FromSeconds(30); std::vector<base::SchedulerWorkerPoolParams> params_vector; @@ -107,11 +105,8 @@ kSuggestedReclaimTime); params_vector.emplace_back("RendererForeground", ThreadPriority::NORMAL, StandbyThreadPolicy::LAZY, - base::RecommendedMaxNumberOfThreadsInPool( - kMaxNumThreadsInForegroundPoolLowerBound, - kMaxNumThreadsInForegroundPoolUpperBound, - kMaxNumThreadsInForegroundPoolCoresMultiplier, - kMaxNumThreadsInForegroundPoolOffset), + std::max(kMaxNumThreadsInForegroundPoolLowerBound, + base::SysInfo::NumberOfProcessors()), kSuggestedReclaimTime); params_vector.emplace_back("RendererForegroundFileIO", ThreadPriority::NORMAL, StandbyThreadPolicy::LAZY,
diff --git a/content/shell/browser/layout_test/layout_test_notification_manager.cc b/content/shell/browser/layout_test/layout_test_notification_manager.cc index cfac368..791e3700 100644 --- a/content/shell/browser/layout_test/layout_test_notification_manager.cc +++ b/content/shell/browser/layout_test/layout_test_notification_manager.cc
@@ -4,189 +4,16 @@ #include "content/shell/browser/layout_test/layout_test_notification_manager.h" -#include "base/guid.h" -#include "base/strings/nullable_string16.h" -#include "base/strings/utf_string_conversions.h" -#include "content/public/browser/browser_thread.h" -#include "content/public/browser/desktop_notification_delegate.h" -#include "content/public/browser/notification_event_dispatcher.h" #include "content/public/browser/permission_type.h" -#include "content/public/common/persistent_notification_status.h" -#include "content/public/common/platform_notification_data.h" #include "content/shell/browser/layout_test/layout_test_browser_context.h" #include "content/shell/browser/layout_test/layout_test_content_browser_client.h" #include "content/shell/browser/layout_test/layout_test_permission_manager.h" namespace content { -namespace { -// The Web Notification layout tests don't care about the lifetime of the -// Service Worker when a notificationclick event has been dispatched. -void OnEventDispatchComplete(PersistentNotificationStatus status) {} - -} // namespace - -LayoutTestNotificationManager::LayoutTestNotificationManager() - : weak_factory_(this) {} - +LayoutTestNotificationManager::LayoutTestNotificationManager() {} LayoutTestNotificationManager::~LayoutTestNotificationManager() {} -void LayoutTestNotificationManager::DisplayNotification( - BrowserContext* browser_context, - const std::string& notification_id, - const GURL& origin, - const PlatformNotificationData& notification_data, - const NotificationResources& notification_resources, - std::unique_ptr<DesktopNotificationDelegate> delegate, - base::Closure* cancel_callback) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - DCHECK(cancel_callback); - - *cancel_callback = base::Bind(&LayoutTestNotificationManager::Close, - weak_factory_.GetWeakPtr(), notification_id); - - ReplaceNotificationIfNeeded(notification_id); - - non_persistent_notifications_[notification_id] = std::move(delegate); - non_persistent_notifications_[notification_id]->NotificationDisplayed(); - - notification_id_map_[base::UTF16ToUTF8(notification_data.title)] = - notification_id; -} - -void LayoutTestNotificationManager::DisplayPersistentNotification( - BrowserContext* browser_context, - const std::string& notification_id, - const GURL& service_worker_scope, - const GURL& origin, - const PlatformNotificationData& notification_data, - const NotificationResources& notification_resources) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - - ReplaceNotificationIfNeeded(notification_id); - - PersistentNotification notification; - notification.browser_context = browser_context; - notification.origin = origin; - - persistent_notifications_[notification_id] = notification; - - notification_id_map_[base::UTF16ToUTF8(notification_data.title)] = - notification_id; -} - -void LayoutTestNotificationManager::ClosePersistentNotification( - BrowserContext* browser_context, - const std::string& notification_id) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - - persistent_notifications_.erase(notification_id); -} - -bool LayoutTestNotificationManager::GetDisplayedNotifications( - BrowserContext* browser_context, - std::set<std::string>* displayed_notifications) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - DCHECK(displayed_notifications); - - // Notifications will never outlive the lifetime of running layout tests. - return false; -} - -void LayoutTestNotificationManager::SimulateClick( - const std::string& title, - int action_index, - const base::NullableString16& reply) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - - const auto notification_id_iter = notification_id_map_.find(title); - if (notification_id_iter == notification_id_map_.end()) - return; - - const std::string& notification_id = notification_id_iter->second; - - const auto persistent_iter = persistent_notifications_.find(notification_id); - const auto non_persistent_iter = - non_persistent_notifications_.find(notification_id); - - if (persistent_iter != persistent_notifications_.end()) { - DCHECK(non_persistent_iter == non_persistent_notifications_.end()); - - const PersistentNotification& notification = persistent_iter->second; - content::NotificationEventDispatcher::GetInstance() - ->DispatchNotificationClickEvent( - notification.browser_context, notification_id, notification.origin, - action_index, reply, base::Bind(&OnEventDispatchComplete)); - } else if (non_persistent_iter != non_persistent_notifications_.end()) { - DCHECK_EQ(action_index, -1) << "Action buttons are only supported for " - "persistent notifications"; - - non_persistent_iter->second->NotificationClick(); - } -} - -void LayoutTestNotificationManager::SimulateClose(const std::string& title, - bool by_user) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - - const auto notification_id_iter = notification_id_map_.find(title); - if (notification_id_iter == notification_id_map_.end()) - return; - - const std::string& notification_id = notification_id_iter->second; - - const auto& persistent_iter = persistent_notifications_.find(notification_id); - if (persistent_iter == persistent_notifications_.end()) - return; - - const PersistentNotification& notification = persistent_iter->second; - content::NotificationEventDispatcher::GetInstance() - ->DispatchNotificationCloseEvent( - notification.browser_context, notification_id, notification.origin, - by_user, base::Bind(&OnEventDispatchComplete)); -} - -blink::mojom::PermissionStatus -LayoutTestNotificationManager::CheckPermissionOnUIThread( - BrowserContext* browser_context, - const GURL& origin, - int render_process_id) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - return CheckPermission(origin); -} - -blink::mojom::PermissionStatus -LayoutTestNotificationManager::CheckPermissionOnIOThread( - ResourceContext* resource_context, - const GURL& origin, - int render_process_id) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - return CheckPermission(origin); -} - -void LayoutTestNotificationManager::Close(const std::string& notification_id) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - auto iterator = non_persistent_notifications_.find(notification_id); - if (iterator == non_persistent_notifications_.end()) - return; - - iterator->second->NotificationClosed(); -} - -void LayoutTestNotificationManager::ReplaceNotificationIfNeeded( - const std::string& notification_id) { - const auto persistent_iter = persistent_notifications_.find(notification_id); - const auto non_persistent_iter = - non_persistent_notifications_.find(notification_id); - - if (persistent_iter != persistent_notifications_.end()) { - DCHECK(non_persistent_iter == non_persistent_notifications_.end()); - persistent_notifications_.erase(persistent_iter); - } else if (non_persistent_iter != non_persistent_notifications_.end()) { - non_persistent_iter->second->NotificationClosed(); - non_persistent_notifications_.erase(non_persistent_iter); - } -} blink::mojom::PermissionStatus LayoutTestNotificationManager::CheckPermission(const GURL& origin) {
diff --git a/content/shell/browser/layout_test/layout_test_notification_manager.h b/content/shell/browser/layout_test/layout_test_notification_manager.h index 25ce88f..658d0f7 100644 --- a/content/shell/browser/layout_test/layout_test_notification_manager.h +++ b/content/shell/browser/layout_test/layout_test_notification_manager.h
@@ -5,14 +5,10 @@ #ifndef CONTENT_SHELL_BROWSER_LAYOUT_TEST_LAYOUT_TEST_NOTIFICATION_MANAGER_H_ #define CONTENT_SHELL_BROWSER_LAYOUT_TEST_LAYOUT_TEST_NOTIFICATION_MANAGER_H_ -#include <stdint.h> #include <string> -#include <unordered_map> -#include "base/callback.h" #include "base/macros.h" -#include "base/memory/weak_ptr.h" -#include "content/public/browser/platform_notification_service.h" +#include "content/test/mock_platform_notification_service.h" #include "third_party/WebKit/public/platform/modules/permissions/permission_status.mojom.h" #include "url/gurl.h" @@ -22,87 +18,15 @@ namespace content { -class DesktopNotificationDelegate; -struct NotificationResources; -struct PlatformNotificationData; - // Responsible for tracking active notifications and allowed origins for the // Web Notification API when running layout tests. -class LayoutTestNotificationManager : public PlatformNotificationService { +class LayoutTestNotificationManager : public MockPlatformNotificationService { public: LayoutTestNotificationManager(); ~LayoutTestNotificationManager() override; - // Simulates a click on the notification titled |title|. |action_index| - // indicates which action was clicked, or -1 if the main notification body was - // clicked. |reply| indicates the user reply, if any. - // Must be called on the UI thread. - void SimulateClick(const std::string& title, - int action_index, - const base::NullableString16& reply); - - // Simulates the closing a notification titled |title|. Must be called on - // the UI thread. - void SimulateClose(const std::string& title, bool by_user); - - // PlatformNotificationService implementation. - blink::mojom::PermissionStatus CheckPermissionOnUIThread( - BrowserContext* browser_context, - const GURL& origin, - int render_process_id) override; - blink::mojom::PermissionStatus CheckPermissionOnIOThread( - ResourceContext* resource_context, - const GURL& origin, - int render_process_id) override; - void DisplayNotification( - BrowserContext* browser_context, - const std::string& notification_id, - const GURL& origin, - const PlatformNotificationData& notification_data, - const NotificationResources& notification_resources, - std::unique_ptr<DesktopNotificationDelegate> delegate, - base::Closure* cancel_callback) override; - void DisplayPersistentNotification( - BrowserContext* browser_context, - const std::string& notification_id, - const GURL& service_worker_scope, - const GURL& origin, - const PlatformNotificationData& notification_data, - const NotificationResources& notification_resources) override; - void ClosePersistentNotification(BrowserContext* browser_context, - const std::string& notification_id) override; - bool GetDisplayedNotifications( - BrowserContext* browser_context, - std::set<std::string>* displayed_notifications) override; - private: - // Structure to represent the information of a persistent notification. - struct PersistentNotification { - BrowserContext* browser_context = nullptr; - GURL origin; - }; - - // Closes the notification titled |title|. Must be called on the UI thread. - void Close(const std::string& title); - - // Fakes replacing the notification identified by |notification_id|. Both - // persistent and non-persistent notifications will be considered for this. - void ReplaceNotificationIfNeeded(const std::string& notification_id); - - // Checks if |origin| has permission to display notifications. May be called - // on both the IO and the UI threads. - blink::mojom::PermissionStatus CheckPermission(const GURL& origin); - - std::unordered_map<std::string, PersistentNotification> - persistent_notifications_; - std::unordered_map<std::string, std::unique_ptr<DesktopNotificationDelegate>> - non_persistent_notifications_; - - // Mapping of titles to notification ids giving test a usable identifier. - std::unordered_map<std::string, std::string> notification_id_map_; - - base::WeakPtrFactory<LayoutTestNotificationManager> weak_factory_; - + blink::mojom::PermissionStatus CheckPermission(const GURL& origin) override; DISALLOW_COPY_AND_ASSIGN(LayoutTestNotificationManager); };
diff --git a/content/shell/browser/layout_test/layout_test_permission_manager.cc b/content/shell/browser/layout_test/layout_test_permission_manager.cc index 15da4cc..42b772a9 100644 --- a/content/shell/browser/layout_test/layout_test_permission_manager.cc +++ b/content/shell/browser/layout_test/layout_test_permission_manager.cc
@@ -15,7 +15,6 @@ #include "content/public/browser/permission_type.h" #include "content/public/browser/web_contents.h" #include "content/shell/browser/layout_test/layout_test_content_browser_client.h" -#include "content/shell/browser/layout_test/layout_test_notification_manager.h" namespace content {
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn index d39cc9c..4c248de 100644 --- a/content/test/BUILD.gn +++ b/content/test/BUILD.gn
@@ -164,6 +164,8 @@ "mock_leveldb_database.h", "mock_permission_manager.cc", "mock_permission_manager.h", + "mock_platform_notification_service.cc", + "mock_platform_notification_service.h", "mock_render_process.cc", "mock_render_process.h", "mock_ssl_host_state_delegate.cc",
diff --git a/content/test/OWNERS b/content/test/OWNERS index 0f32f6b..ad071c80c 100644 --- a/content/test/OWNERS +++ b/content/test/OWNERS
@@ -4,6 +4,9 @@ # For WebRTC files. tommi@chromium.org +# For Notification related files. +per-file mock_platform_notification_service.*=file://content/browser/notifications/OWNERS + # Anyone can add rules to include new test files. per-file BUILD.gn=*
diff --git a/content/test/mock_platform_notification_service.cc b/content/test/mock_platform_notification_service.cc new file mode 100644 index 0000000..f1fa04a --- /dev/null +++ b/content/test/mock_platform_notification_service.cc
@@ -0,0 +1,196 @@ +// Copyright 2014 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 "content/test/mock_platform_notification_service.h" + +#include "base/guid.h" +#include "base/strings/nullable_string16.h" +#include "base/strings/utf_string_conversions.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/desktop_notification_delegate.h" +#include "content/public/browser/notification_event_dispatcher.h" +#include "content/public/browser/permission_type.h" +#include "content/public/common/persistent_notification_status.h" +#include "content/public/common/platform_notification_data.h" + +namespace content { +namespace { + +// The Web Notification layout tests don't care about the lifetime of the +// Service Worker when a notificationclick event has been dispatched. +void OnEventDispatchComplete(PersistentNotificationStatus status) {} + +} // namespace + +MockPlatformNotificationService::MockPlatformNotificationService() + : weak_factory_(this) {} + +MockPlatformNotificationService::~MockPlatformNotificationService() {} + +void MockPlatformNotificationService::DisplayNotification( + BrowserContext* browser_context, + const std::string& notification_id, + const GURL& origin, + const PlatformNotificationData& notification_data, + const NotificationResources& notification_resources, + std::unique_ptr<DesktopNotificationDelegate> delegate, + base::Closure* cancel_callback) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + DCHECK(cancel_callback); + + *cancel_callback = base::Bind(&MockPlatformNotificationService::Close, + weak_factory_.GetWeakPtr(), notification_id); + + ReplaceNotificationIfNeeded(notification_id); + + non_persistent_notifications_[notification_id] = std::move(delegate); + non_persistent_notifications_[notification_id]->NotificationDisplayed(); + + notification_id_map_[base::UTF16ToUTF8(notification_data.title)] = + notification_id; +} + +void MockPlatformNotificationService::DisplayPersistentNotification( + BrowserContext* browser_context, + const std::string& notification_id, + const GURL& service_worker_scope, + const GURL& origin, + const PlatformNotificationData& notification_data, + const NotificationResources& notification_resources) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + ReplaceNotificationIfNeeded(notification_id); + + PersistentNotification notification; + notification.browser_context = browser_context; + notification.origin = origin; + + persistent_notifications_[notification_id] = notification; + + notification_id_map_[base::UTF16ToUTF8(notification_data.title)] = + notification_id; +} + +void MockPlatformNotificationService::ClosePersistentNotification( + BrowserContext* browser_context, + const std::string& notification_id) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + persistent_notifications_.erase(notification_id); +} + +bool MockPlatformNotificationService::GetDisplayedNotifications( + BrowserContext* browser_context, + std::set<std::string>* displayed_notifications) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + DCHECK(displayed_notifications); + + for (const auto& kv : persistent_notifications_) + displayed_notifications->insert(kv.first); + + return true; +} + +void MockPlatformNotificationService::SimulateClick( + const std::string& title, + int action_index, + const base::NullableString16& reply) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + const auto notification_id_iter = notification_id_map_.find(title); + if (notification_id_iter == notification_id_map_.end()) + return; + + const std::string& notification_id = notification_id_iter->second; + + const auto persistent_iter = persistent_notifications_.find(notification_id); + const auto non_persistent_iter = + non_persistent_notifications_.find(notification_id); + + if (persistent_iter != persistent_notifications_.end()) { + DCHECK(non_persistent_iter == non_persistent_notifications_.end()); + + const PersistentNotification& notification = persistent_iter->second; + content::NotificationEventDispatcher::GetInstance() + ->DispatchNotificationClickEvent( + notification.browser_context, notification_id, notification.origin, + action_index, reply, base::Bind(&OnEventDispatchComplete)); + } else if (non_persistent_iter != non_persistent_notifications_.end()) { + DCHECK_EQ(action_index, -1) << "Action buttons are only supported for " + "persistent notifications"; + + non_persistent_iter->second->NotificationClick(); + } +} + +void MockPlatformNotificationService::SimulateClose(const std::string& title, + bool by_user) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + const auto notification_id_iter = notification_id_map_.find(title); + if (notification_id_iter == notification_id_map_.end()) + return; + + const std::string& notification_id = notification_id_iter->second; + + const auto& persistent_iter = persistent_notifications_.find(notification_id); + if (persistent_iter == persistent_notifications_.end()) + return; + + const PersistentNotification& notification = persistent_iter->second; + content::NotificationEventDispatcher::GetInstance() + ->DispatchNotificationCloseEvent( + notification.browser_context, notification_id, notification.origin, + by_user, base::Bind(&OnEventDispatchComplete)); +} + +blink::mojom::PermissionStatus +MockPlatformNotificationService::CheckPermissionOnUIThread( + BrowserContext* browser_context, + const GURL& origin, + int render_process_id) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + return CheckPermission(origin); +} + +blink::mojom::PermissionStatus +MockPlatformNotificationService::CheckPermissionOnIOThread( + ResourceContext* resource_context, + const GURL& origin, + int render_process_id) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + return CheckPermission(origin); +} + +void MockPlatformNotificationService::Close( + const std::string& notification_id) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + auto iterator = non_persistent_notifications_.find(notification_id); + if (iterator == non_persistent_notifications_.end()) + return; + + iterator->second->NotificationClosed(); +} + +void MockPlatformNotificationService::ReplaceNotificationIfNeeded( + const std::string& notification_id) { + const auto persistent_iter = persistent_notifications_.find(notification_id); + const auto non_persistent_iter = + non_persistent_notifications_.find(notification_id); + + if (persistent_iter != persistent_notifications_.end()) { + DCHECK(non_persistent_iter == non_persistent_notifications_.end()); + persistent_notifications_.erase(persistent_iter); + } else if (non_persistent_iter != non_persistent_notifications_.end()) { + non_persistent_iter->second->NotificationClosed(); + non_persistent_notifications_.erase(non_persistent_iter); + } +} + +blink::mojom::PermissionStatus MockPlatformNotificationService::CheckPermission( + const GURL& origin) { + return blink::mojom::PermissionStatus::GRANTED; +} + +} // namespace content
diff --git a/content/test/mock_platform_notification_service.h b/content/test/mock_platform_notification_service.h new file mode 100644 index 0000000..790053ba --- /dev/null +++ b/content/test/mock_platform_notification_service.h
@@ -0,0 +1,112 @@ +// Copyright 2014 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 CONTENT_TEST_MOCK_PLATFORM_NOTIFICATION_SERVICE_H_ +#define CONTENT_TEST_MOCK_PLATFORM_NOTIFICATION_SERVICE_H_ + +#include <stdint.h> +#include <string> +#include <unordered_map> + +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "content/public/browser/platform_notification_service.h" +#include "third_party/WebKit/public/platform/modules/permissions/permission_status.mojom.h" +#include "url/gurl.h" + +namespace base { + class NullableString16; +} + +namespace content { + +class DesktopNotificationDelegate; +struct NotificationResources; +struct PlatformNotificationData; + +// Responsible for tracking active notifications and allowed origins for the +// Web Notification API when running layout and content tests. +class MockPlatformNotificationService : public PlatformNotificationService { + public: + MockPlatformNotificationService(); + ~MockPlatformNotificationService() override; + + // Simulates a click on the notification titled |title|. |action_index| + // indicates which action was clicked, or -1 if the main notification body was + // clicked. |reply| indicates the user reply, if any. + // Must be called on the UI thread. + void SimulateClick(const std::string& title, + int action_index, + const base::NullableString16& reply); + + // Simulates the closing a notification titled |title|. Must be called on + // the UI thread. + void SimulateClose(const std::string& title, bool by_user); + + // PlatformNotificationService implementation. + blink::mojom::PermissionStatus CheckPermissionOnUIThread( + BrowserContext* browser_context, + const GURL& origin, + int render_process_id) override; + blink::mojom::PermissionStatus CheckPermissionOnIOThread( + ResourceContext* resource_context, + const GURL& origin, + int render_process_id) override; + void DisplayNotification( + BrowserContext* browser_context, + const std::string& notification_id, + const GURL& origin, + const PlatformNotificationData& notification_data, + const NotificationResources& notification_resources, + std::unique_ptr<DesktopNotificationDelegate> delegate, + base::Closure* cancel_callback) override; + void DisplayPersistentNotification( + BrowserContext* browser_context, + const std::string& notification_id, + const GURL& service_worker_scope, + const GURL& origin, + const PlatformNotificationData& notification_data, + const NotificationResources& notification_resources) override; + void ClosePersistentNotification(BrowserContext* browser_context, + const std::string& notification_id) override; + bool GetDisplayedNotifications( + BrowserContext* browser_context, + std::set<std::string>* displayed_notifications) override; + + protected: + // Checks if |origin| has permission to display notifications. May be called + // on both the IO and the UI threads. + virtual blink::mojom::PermissionStatus CheckPermission(const GURL& origin); + + private: + // Structure to represent the information of a persistent notification. + struct PersistentNotification { + BrowserContext* browser_context = nullptr; + GURL origin; + }; + + // Closes the notification titled |title|. Must be called on the UI thread. + void Close(const std::string& title); + + // Fakes replacing the notification identified by |notification_id|. Both + // persistent and non-persistent notifications will be considered for this. + void ReplaceNotificationIfNeeded(const std::string& notification_id); + + std::unordered_map<std::string, PersistentNotification> + persistent_notifications_; + std::unordered_map<std::string, std::unique_ptr<DesktopNotificationDelegate>> + non_persistent_notifications_; + + // Mapping of titles to notification ids giving test a usable identifier. + std::unordered_map<std::string, std::string> notification_id_map_; + + base::WeakPtrFactory<MockPlatformNotificationService> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(MockPlatformNotificationService); +}; + +} // content + +#endif // CONTENT_TEST_MOCK_PLATFORM_NOTIFICATION_SERVICE_H_
diff --git a/docs/android_studio.md b/docs/android_studio.md index 2a10aa7a..2815238 100644 --- a/docs/android_studio.md +++ b/docs/android_studio.md
@@ -4,7 +4,8 @@ ## Usage -Make sure you have followed [android build instructions](android_build_instructions.md) already. +Make sure you have followed +[android build instructions](android_build_instructions.md) already. ```shell build/android/gradle/generate_gradle.py @@ -26,16 +27,19 @@ For first-time Android Studio users: * Avoid running the setup wizard. - * The wizard will force you to download unwanted SDK components to `//third_party/android_tools`. + * The wizard will force you to download unwanted SDK components to + `//third_party/android_tools`. * To skip it, select "Cancel" when it comes up. To import the project: -* Use "Import Project", and select the directory containing the generated project, by default `out-gn/Debug/gradle`. +* Use "Import Project", and select the directory containing the generated + project, by default `out-gn/Debug/gradle`. You need to re-run `generate_gradle.py` whenever `BUILD.gn` files change. -* After regenerating, Android Studio should prompt you to "Sync". If it doesn't, use: +* After regenerating, Android Studio should prompt you to "Sync". If it + doesn't, use: * Help -> Find Action -> "Sync Project with Gradle Files" @@ -45,17 +49,13 @@ targets. Each `android_apk` and `android_library` target produces a separate Gradle sub-project. -### Symlinks and .srcjars +### Excluded files and .srcjars Gradle supports source directories but not source files. However, some -`java/src/` directories in Chromium are split amonst multiple GN targets. To -accommodate this, the script detects such targets and creates a `symlinked-java/` -directory to point gradle at. Be warned that creating new files from Android -Studio within these symlink-based projects will cause new files to be created in -the generated `symlinked-java/` rather than the source tree where you want it. - -*** note -** TLDR:** Always create new files outside of Android Studio. +directories in Chromium are split amonst multiple GN targets. To accommodate +this, the script detects such targets and creates exclude patterns to exclude +files not in the current target. You may still see them when editing, but they +are excluded in gradle tasks. *** Most generated .java files in GN are stored as `.srcjars`. Android Studio does @@ -69,11 +69,14 @@ ## Android Studio Tips -* Configuration instructions can be found [here](http://tools.android.com/tech-docs/configuration). One suggestions: - * Launch it with more RAM: `STUDIO_VM_OPTIONS=-Xmx2048m /opt/android-studio-stable/bin/studio-launcher.sh` +* Configuration instructions can be found + [here](http://tools.android.com/tech-docs/configuration). One suggestions: + * Launch it with more RAM: + `STUDIO_VM_OPTIONS=-Xmx2048m /opt/android-studio-stable/bin/studio-launcher.sh` * If you ever need to reset it: `rm -r ~/.AndroidStudio*/` * Import Android style settings: - * Help -> Find Action -> "Code Style" (settings) -> Java -> Manage -> Import + * Help -> Find Action -> "Code Style" (settings) -> Java -> + Manage -> Import * Select `third_party/android_platform/development/ide/intellij/codestyles/AndroidStyle.xml` * Turn on automatic import: * Help -> Find Action -> "Auto Import" @@ -97,17 +100,20 @@ ### Building from the Command Line -Gradle builds can be done from the command-line after importing the project into -Android Studio (importing into the IDE causes the Gradle wrapper to be added). -This wrapper can also be used to invoke gradle commands. +Gradle builds can be done from the command-line after importing the project +into Android Studio (importing into the IDE causes the Gradle wrapper to be +added). This wrapper can also be used to invoke gradle commands. cd $GRADLE_PROJECT_DIR && bash gradlew The resulting artifacts are not terribly useful. They are missing assets, resources, native libraries, etc. -* Use a [gradle daemon](https://docs.gradle.org/2.14.1/userguide/gradle_daemon.html) to speed up builds: - * Add the line `org.gradle.daemon=true` to `~/.gradle/gradle.properties`, creating it if necessary. +* Use a + [gradle daemon](https://docs.gradle.org/2.14.1/userguide/gradle_daemon.html) + to speed up builds using the gradlew script: + * Add the line `org.gradle.daemon=true` to `~/.gradle/gradle.properties`, + creating it if necessary. ## Status (as of Jan 19, 2017)
diff --git a/docs/qtcreator.md b/docs/qtcreator.md new file mode 100644 index 0000000..932cc09 --- /dev/null +++ b/docs/qtcreator.md
@@ -0,0 +1,120 @@ +# Use Qt Creator as IDE or GUI Debugger + +[Qt Creator](https://www.qt.io/ide/) +([Wiki](https://en.wikipedia.org/wiki/Qt_Creator)) is a cross-platform C++ IDE. + +You can use Qt Creator as a daily IDE or just as a GDB frontend and that does +not require project configuration. + +[TOC] + +## IDE + +### Workflow + +1. `ctrl+k` Activate Locator, you can open file(not support sublime-like-search) or type `. ` go to symbol. +2. `ctrl+r` Build and Run, `F5` Debug. +3. `F4` switch between header file and cpp file. +4. `ctrl+shift+r` rename symbol under cursor. +5. Code completion is built-in. And you can add your snippets. + +### Setup as IDE + +1. Install latest Qt Creator +2. under chromium/src `gn gen out/Default --ide=qtcreator` +3. qtcreator out/Default/qtcreator_project/all.creator + +It takes 3 minutes to parsing C++ files in my workstation!!! And It will not +block you while parsing. + +#### Code Style + +1. Help - About Plugins enable Beautifier. +2. Tools - Options - Beautifier - Clang Format, select use predefined style: +chromium. You can also set a keyboard shortcut for it. +3. Tools - Options - Code Style import this xml file + +``` +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE QtCreatorCodeStyle> +<!-- Written by QtCreator 4.2.1, 2017-02-08T19:07:34. --> +<qtcreator> + <data> + <variable>CodeStyleData</variable> + <valuemap type="QVariantMap"> + <value type="bool" key="AlignAssignments">true</value> + <value type="bool" key="AutoSpacesForTabs">false</value> + <value type="bool" key="BindStarToIdentifier">false</value> + <value type="bool" key="BindStarToLeftSpecifier">false</value> + <value type="bool" key="BindStarToRightSpecifier">false</value> + <value type="bool" key="BindStarToTypeName">true</value> + <value type="bool" + key="ExtraPaddingForConditionsIfConfusingAlign">true</value> + <value type="bool" key="IndentAccessSpecifiers">true</value> + <value type="bool" key="IndentBlockBody">true</value> + <value type="bool" key="IndentBlockBraces">false</value> + <value type="bool" key="IndentBlocksRelativeToSwitchLabels">false</value> + <value type="bool" key="IndentClassBraces">false</value> + <value type="bool" key="IndentControlFlowRelativeToSwitchLabels">true</value> + <value type="bool" + key="IndentDeclarationsRelativeToAccessSpecifiers">false</value> + <value type="bool" key="IndentEnumBraces">false</value> + <value type="bool" key="IndentFunctionBody">true</value> + <value type="bool" key="IndentFunctionBraces">false</value> + <value type="bool" key="IndentNamespaceBody">false</value> + <value type="bool" key="IndentNamespaceBraces">false</value> + <value type="int" key="IndentSize">2</value> + <value type="bool" key="IndentStatementsRelativeToSwitchLabels">true</value> + <value type="bool" key="IndentSwitchLabels">false</value> + <value type="int" key="PaddingMode">2</value> + <value type="bool" key="ShortGetterName">true</value> + <value type="bool" key="SpacesForTabs">true</value> + <value type="int" key="TabSize">2</value> + </valuemap> + </data> + <data> + <variable>DisplayName</variable> + <value type="QString">chrome</value> + </data> +</qtcreator> +``` + +#### Build & Run + +In left panel, projects - setup the ninja command in build and clean step and +executable chrome path in run. + +## Debugger + +**You can skip the project settings and use QtCreator as a single file +standalone GDB frontend. ** + +1. Tools - Options - Build & Run - Debuggers, make sure GDB is set. +2. Tools - Options - Kits, change the Desktop kit to GDB(LLDB doesnot work in + Linux). +3. Open file you want to debug. +4. Debug - Start Debugging - Attach to running Application, you may need to + open chrome's task manager to find the process number. + +### Tips, tricks, and troubleshooting + +#### Make GDB start fast + +Add `gdb_index = true` to `gn args`. + +#### Debugger shows start then finish + +``` +$ echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope +``` + +Ensure yama allow you to attach another process. + +#### Debugger do not stop in break point + +Ensure you are using GDB not LLDB in Linux. + +#### More + +See +https://chromium.googlesource.com/chromium/src/+/master/docs/linux_debugging.md \ No newline at end of file
diff --git a/google_apis/gaia/oauth2_token_service_delegate.cc b/google_apis/gaia/oauth2_token_service_delegate.cc index ccdba78..a59c9e8 100644 --- a/google_apis/gaia/oauth2_token_service_delegate.cc +++ b/google_apis/gaia/oauth2_token_service_delegate.cc
@@ -119,3 +119,8 @@ const net::BackoffEntry* OAuth2TokenServiceDelegate::BackoffEntry() const { return nullptr; } + +OAuth2TokenServiceDelegate::LoadCredentialsState +OAuth2TokenServiceDelegate::GetLoadCredentialsState() const { + return LOAD_CREDENTIALS_UNKNOWN; +}
diff --git a/google_apis/gaia/oauth2_token_service_delegate.h b/google_apis/gaia/oauth2_token_service_delegate.h index 1f6477b..d01b9ae 100644 --- a/google_apis/gaia/oauth2_token_service_delegate.h +++ b/google_apis/gaia/oauth2_token_service_delegate.h
@@ -20,6 +20,16 @@ // CreateAccessTokenFetcher properly. class OAuth2TokenServiceDelegate { public: + enum LoadCredentialsState { + LOAD_CREDENTIALS_UNKNOWN, + LOAD_CREDENTIALS_NOT_STARTED, + LOAD_CREDENTIALS_IN_PROGRESS, + LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS, + LOAD_CREDENTIALS_FINISHED_WITH_DB_ERRORS, + LOAD_CREDENTIALS_FINISHED_WITH_DECRYPT_ERRORS, + LOAD_CREDENTIALS_FINISHED_WITH_UNKNOWN_ERRORS, + }; + OAuth2TokenServiceDelegate(); virtual ~OAuth2TokenServiceDelegate(); @@ -58,6 +68,11 @@ // a nullptr otherwise. virtual const net::BackoffEntry* BackoffEntry() const; + // Diagnostic methods + + // Returns the state of the load credentials operation. + virtual LoadCredentialsState GetLoadCredentialsState() const; + protected: // Called by subclasses to notify observers. virtual void FireRefreshTokenAvailable(const std::string& account_id);
diff --git a/ios/chrome/browser/passwords/password_controller_unittest.mm b/ios/chrome/browser/passwords/password_controller_unittest.mm index 470b1a6..7e45473 100644 --- a/ios/chrome/browser/passwords/password_controller_unittest.mm +++ b/ios/chrome/browser/passwords/password_controller_unittest.mm
@@ -211,7 +211,7 @@ __block int failure_count = 0; void (^fail_invocation)(NSInvocation*) = ^(NSInvocation* invocation) { if (failure_count >= target_failure_count) { - [failing_manager stop]; + [failing_manager stopMocking]; [invocation invokeWithTarget:original_manager]; } else { ++failure_count;
diff --git a/ios/chrome/browser/passwords/password_generation_agent_unittest.mm b/ios/chrome/browser/passwords/password_generation_agent_unittest.mm index 1c47577..5060bc4 100644 --- a/ios/chrome/browser/passwords/password_generation_agent_unittest.mm +++ b/ios/chrome/browser/passwords/password_generation_agent_unittest.mm
@@ -435,7 +435,7 @@ @"focus"); EXPECT_OCMOCK_VERIFY(mock); - [mock stop]; + [mock stopMocking]; } // Tests that requesting password generation shows the alert UI. @@ -451,7 +451,7 @@ EXPECT_EQ(YES, mock_ui_delegate().UIShown); EXPECT_OCMOCK_VERIFY(mock); - [mock stop]; + [mock stopMocking]; } // Tests that the password generation UI is hidden when the user changes focus @@ -468,7 +468,7 @@ SimulateFormActivity(kAccountCreationFormName, kEmailFieldName, @"focus"); EXPECT_OCMOCK_VERIFY(mock); - [mock stop]; + [mock stopMocking]; } // Tests that the password field is filled when the user accepts a generated
diff --git a/ios/chrome/browser/tabs/BUILD.gn b/ios/chrome/browser/tabs/BUILD.gn index 3ac08a4..aca9ff68 100644 --- a/ios/chrome/browser/tabs/BUILD.gn +++ b/ios/chrome/browser/tabs/BUILD.gn
@@ -4,6 +4,7 @@ source_set("tabs") { sources = [ + "legacy_tab_helper.h", "tab.h", "tab_delegate.h", "tab_dialog_delegate.h", @@ -116,6 +117,7 @@ source_set("tabs_internal_arc") { sources = [ + "legacy_tab_helper.mm", "tab_model_list.mm", ] deps = [ @@ -123,6 +125,7 @@ "//base", "//ios/chrome/browser", "//ios/chrome/browser/browser_state", + "//ios/web", ] libs = [ "Foundation.framework" ] configs += [ "//build/config/compiler:enable_arc" ]
diff --git a/ios/chrome/browser/tabs/legacy_tab_helper.h b/ios/chrome/browser/tabs/legacy_tab_helper.h new file mode 100644 index 0000000..0ede4ffd --- /dev/null +++ b/ios/chrome/browser/tabs/legacy_tab_helper.h
@@ -0,0 +1,38 @@ +// Copyright 2017 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 IOS_CHROME_BROWSER_TABS_LEGACY_TAB_HELPER_H_ +#define IOS_CHROME_BROWSER_TABS_LEGACY_TAB_HELPER_H_ + +#import "base/ios/weak_nsobject.h" +#include "base/macros.h" +#import "ios/web/public/web_state/web_state_user_data.h" + +@class Tab; + +// LegacyTabHelper allows to access to the Tab owning a given WebState for +// interoperability of code using WebStates with legacy code using Tabs. +class LegacyTabHelper : public web::WebStateUserData<LegacyTabHelper> { + public: + // Creates the LegacyTabHelper to record the association of |web_state| + // with |tab|. It is an error if |web_state| is already associated with + // another Tab. + static void CreateForWebState(web::WebState* web_state, Tab* tab); + + // Returns the Tab associated with |web_state| if it exists or nil. + static Tab* GetTabForWebState(web::WebState* web_state); + + private: + LegacyTabHelper(web::WebState* web_state, Tab* tab); + ~LegacyTabHelper() override; + + // The Tab instance associated with the WebState. The Tab currently owns + // the WebState, so this should only be nil between the call to -[Tab close] + // and the object is deallocated. + base::WeakNSObject<Tab> tab_; + + DISALLOW_COPY_AND_ASSIGN(LegacyTabHelper); +}; + +#endif // IOS_CHROME_BROWSER_TABS_LEGACY_TAB_HELPER_H_
diff --git a/ios/chrome/browser/tabs/legacy_tab_helper.mm b/ios/chrome/browser/tabs/legacy_tab_helper.mm new file mode 100644 index 0000000..476e916 --- /dev/null +++ b/ios/chrome/browser/tabs/legacy_tab_helper.mm
@@ -0,0 +1,34 @@ +// Copyright 2017 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. + +#import "ios/chrome/browser/tabs/legacy_tab_helper.h" + +#import "ios/chrome/browser/tabs/tab.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +DEFINE_WEB_STATE_USER_DATA_KEY(LegacyTabHelper); + +// static +void LegacyTabHelper::CreateForWebState(web::WebState* web_state, Tab* tab) { + DCHECK(web_state); + DCHECK(!FromWebState(web_state)); + web_state->SetUserData(UserDataKey(), new LegacyTabHelper(web_state, tab)); +} + +// static +Tab* LegacyTabHelper::GetTabForWebState(web::WebState* web_state) { + DCHECK(web_state); + LegacyTabHelper* tab_helper = LegacyTabHelper::FromWebState(web_state); + return tab_helper ? tab_helper->tab_.get() : nil; +} + +LegacyTabHelper::~LegacyTabHelper() = default; + +LegacyTabHelper::LegacyTabHelper(web::WebState* web_state, Tab* tab) + : tab_(tab) { + DCHECK_EQ(web_state, tab.webState); +}
diff --git a/ios/chrome/browser/tabs/tab.mm b/ios/chrome/browser/tabs/tab.mm index 3bc61e190..18cfa16 100644 --- a/ios/chrome/browser/tabs/tab.mm +++ b/ios/chrome/browser/tabs/tab.mm
@@ -88,6 +88,7 @@ #include "ios/chrome/browser/ssl/ios_security_state_tab_helper.h" #import "ios/chrome/browser/storekit_launcher.h" #include "ios/chrome/browser/sync/ios_chrome_synced_tab_delegate.h" +#import "ios/chrome/browser/tabs/legacy_tab_helper.h" #import "ios/chrome/browser/tabs/tab_delegate.h" #import "ios/chrome/browser/tabs/tab_dialog_delegate.h" #import "ios/chrome/browser/tabs/tab_headers_delegate.h" @@ -535,6 +536,10 @@ webStateObserver_.reset( new web::WebStateObserverBridge(webStateImpl_.get(), self)); + // Do not respect |attachTabHelpers| as this tab helper is required for + // proper conversion from WebState to Tab. + LegacyTabHelper::CreateForWebState(webStateImpl_.get(), self); + [self.webController setDelegate:self]; NSString* sessionID = self.tabId; @@ -1248,6 +1253,8 @@ // to drive its own destruction. base::scoped_nsobject<Tab> kungFuDeathGrip([self retain]); [parentTabModel_ didCloseTab:self]; // Inform parent of tab closure. + + LegacyTabHelper::RemoveFromWebState(webStateImpl_.get()); webStateImpl_.reset(); }
diff --git a/ios/chrome/browser/ui/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view_controller.mm index 09f2a6ac..b201040 100644 --- a/ios/chrome/browser/ui/browser_view_controller.mm +++ b/ios/chrome/browser/ui/browser_view_controller.mm
@@ -77,6 +77,7 @@ #import "ios/chrome/browser/snapshots/snapshot_overlay.h" #import "ios/chrome/browser/snapshots/snapshot_overlay_provider.h" #import "ios/chrome/browser/storekit_launcher.h" +#import "ios/chrome/browser/tabs/legacy_tab_helper.h" #import "ios/chrome/browser/tabs/tab.h" #import "ios/chrome/browser/tabs/tab_dialog_delegate.h" #import "ios/chrome/browser/tabs/tab_headers_delegate.h" @@ -675,8 +676,6 @@ - (void)showSnackbar:(NSString*)message; // Induces an intentional crash in the browser process. - (void)induceBrowserCrash; -// Returns Tab that corresponds to the given |webState|. -- (Tab*)tabForWebState:(web::WebState*)webState; // Saves the image or display error message, based on privacy settings. - (void)managePermissionAndSaveImage:(NSData*)data; // Saves the image. In order to keep the metadata of the image, the image is @@ -2333,7 +2332,7 @@ referrer:params.referrer transition:params.transition windowName:nil - opener:[self tabForWebState:webState] + opener:LegacyTabHelper::GetTabForWebState(webState) openedByDOM:NO atIndex:TabModelConstants::kTabPositionAutomatically inBackground:(params.disposition == @@ -2508,11 +2507,11 @@ runRepostFormDialogWithCompletionHandler:(void (^)(BOOL))handler { // Display the action sheet with the arrow pointing at the top center of the // web contents. + Tab* tab = LegacyTabHelper::GetTabForWebState(webState); UIView* view = webState->GetView(); CGPoint dialogLocation = CGPointMake(CGRectGetMidX(view.frame), - CGRectGetMinY(view.frame) + - [self headerHeightForTab:[self tabForWebState:webState]]); + CGRectGetMinY(view.frame) + [self headerHeightForTab:tab]); auto helper = RepostFormTabHelper::FromWebState(webState); helper->PresentDialog(dialogLocation, base::BindBlock(^(bool shouldContinue) { handler(shouldContinue); @@ -3592,14 +3591,6 @@ return [[_model currentTab] webState]; } -- (Tab*)tabForWebState:(web::WebState*)webState { - for (Tab* tab in _model.get()) { - if (tab.webState == webState) - return tab; - } - return nil; -} - // This is called from within an animation block. - (void)toolbarHeightChanged { if ([self headerHeight] != 0) {
diff --git a/ios/chrome/browser/ui/dialogs/BUILD.gn b/ios/chrome/browser/ui/dialogs/BUILD.gn index 4c2159c..f28a15c 100644 --- a/ios/chrome/browser/ui/dialogs/BUILD.gn +++ b/ios/chrome/browser/ui/dialogs/BUILD.gn
@@ -58,6 +58,7 @@ } source_set("unit_tests_internal") { + configs += [ "//build/config/compiler:enable_arc" ] testonly = true sources = [ "dialog_presenter_unittest.mm",
diff --git a/ios/chrome/browser/ui/dialogs/dialog_presenter_unittest.mm b/ios/chrome/browser/ui/dialogs/dialog_presenter_unittest.mm index 7e498f7..2a566bf9 100644 --- a/ios/chrome/browser/ui/dialogs/dialog_presenter_unittest.mm +++ b/ios/chrome/browser/ui/dialogs/dialog_presenter_unittest.mm
@@ -4,7 +4,6 @@ #import "ios/chrome/browser/ui/dialogs/dialog_presenter.h" -#import "base/mac/scoped_nsobject.h" #include "base/time/time.h" #import "ios/chrome/browser/ui/alert_coordinator/alert_coordinator.h" #import "ios/web/public/test/fakes/test_web_state.h" @@ -14,6 +13,10 @@ #include "testing/platform_test.h" #include "url/gurl.h" +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + namespace { // TestWebState subclass that supports the WebStateDestroyed() callback for a // single observer. @@ -77,9 +80,9 @@ DialogPresenter* presenter() { return presenter_; } private: - base::scoped_nsobject<TestDialogPresenterDelegate> delegate_; - base::scoped_nsobject<UIViewController> viewController_; - base::scoped_nsobject<DialogPresenter> presenter_; + TestDialogPresenterDelegate* delegate_; + UIViewController* viewController_; + DialogPresenter* presenter_; }; // Tests that a dialog was successfully shown and that the delegate was notified
diff --git a/ios/chrome/browser/ui/dialogs/nsurl_protection_space_util_unittest.mm b/ios/chrome/browser/ui/dialogs/nsurl_protection_space_util_unittest.mm index 6a928967..f89669ab 100644 --- a/ios/chrome/browser/ui/dialogs/nsurl_protection_space_util_unittest.mm +++ b/ios/chrome/browser/ui/dialogs/nsurl_protection_space_util_unittest.mm
@@ -5,7 +5,6 @@ #import "ios/chrome/browser/ui/dialogs/nsurl_protection_space_util.h" #include "base/ios/ios_util.h" -#import "base/mac/scoped_nsobject.h" #include "base/strings/sys_string_conversions.h" #include "components/strings/grit/components_strings.h" #include "testing/gtest/include/gtest/gtest.h" @@ -14,6 +13,10 @@ #include "ui/base/l10n/l10n_util.h" #include "ui/base/l10n/l10n_util_mac.h" +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + using namespace ios_internal::nsurlprotectionspace_util; namespace { @@ -27,11 +30,11 @@ NSURLProtectionSpace* GetProtectionSpaceForHost(NSString* host, NSString* protocol, NSInteger port) { - return [[[NSURLProtectionSpace alloc] initWithHost:host - port:port - protocol:protocol - realm:nil - authenticationMethod:nil] autorelease]; + return [[NSURLProtectionSpace alloc] initWithHost:host + port:port + protocol:protocol + realm:nil + authenticationMethod:nil]; } // Returns protection space for the given |host| and |protocol| and port 80. @@ -43,11 +46,11 @@ // Returns protection space for the given proxy |host| and |protocol|. NSURLProtectionSpace* GetProtectionSpaceForProxyHost(NSString* host, NSString* type) { - return [[[NSURLProtectionSpace alloc] initWithProxyHost:host - port:80 - type:type - realm:nil - authenticationMethod:nil] autorelease]; + return [[NSURLProtectionSpace alloc] initWithProxyHost:host + port:80 + type:type + realm:nil + authenticationMethod:nil]; } } // namespace
diff --git a/ios/chrome/browser/ui/ntp/BUILD.gn b/ios/chrome/browser/ui/ntp/BUILD.gn index 8ff4d1ffb..99bb2985 100644 --- a/ios/chrome/browser/ui/ntp/BUILD.gn +++ b/ios/chrome/browser/ui/ntp/BUILD.gn
@@ -206,6 +206,7 @@ } source_set("unit_tests") { + configs += [ "//build/config/compiler:enable_arc" ] testonly = true sources = [ "centering_scrollview_unittest.mm",
diff --git a/ios/chrome/browser/ui/ntp/centering_scrollview_unittest.mm b/ios/chrome/browser/ui/ntp/centering_scrollview_unittest.mm index d421ecd..b783182 100644 --- a/ios/chrome/browser/ui/ntp/centering_scrollview_unittest.mm +++ b/ios/chrome/browser/ui/ntp/centering_scrollview_unittest.mm
@@ -2,29 +2,32 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/mac/scoped_nsobject.h" #import "ios/chrome/browser/ui/ntp/centering_scrollview.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest_mac.h" #include "testing/platform_test.h" +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + namespace { class CenteringScrollViewTest : public PlatformTest { protected: void SetUp() override { - scrollView_.reset([[CenteringScrollView alloc] initWithFrame:CGRectZero]); - contentView_.reset([[UIView alloc] initWithFrame:CGRectZero]); + scrollView_ = [[CenteringScrollView alloc] initWithFrame:CGRectZero]; + contentView_ = [[UIView alloc] initWithFrame:CGRectZero]; }; - base::scoped_nsobject<CenteringScrollView> scrollView_; - base::scoped_nsobject<UIView> contentView_; + CenteringScrollView* scrollView_; + UIView* contentView_; }; TEST_F(CenteringScrollViewTest, CenteringContent) { // Set up content view to be smaller than the scrolling view. [contentView_ setFrame:CGRectMake(0.0, 0.0, 80.0, 120.0)]; - [scrollView_ addSubview:contentView_.get()]; + [scrollView_ addSubview:contentView_]; // Do test. [scrollView_ setFrame:CGRectMake(0.0, 0.0, 320, 480.0)]; [scrollView_ layoutSubviews]; @@ -42,7 +45,7 @@ TEST_F(CenteringScrollViewTest, ScrollingContent) { // Set up content view taller than the scrolling view. [contentView_ setFrame:CGRectMake(0.0, 0.0, 240.0, 400.0)]; - [scrollView_ addSubview:contentView_.get()]; + [scrollView_ addSubview:contentView_]; // Do test [scrollView_ setFrame:CGRectMake(0.0, 0.0, 480.0, 320.0)]; [scrollView_ layoutSubviews]; @@ -60,7 +63,7 @@ TEST_F(CenteringScrollViewTest, ResetContentSize) { // Set up content view taller than the scrolling view. [contentView_ setFrame:CGRectMake(0.0, 0.0, 240.0, 400.0)]; - [scrollView_ addSubview:contentView_.get()]; + [scrollView_ addSubview:contentView_]; [scrollView_ setFrame:CGRectMake(0.0, 0.0, 280.0, 440.0)]; [scrollView_ layoutSubviews];
diff --git a/ios/chrome/browser/ui/ntp/google_landing_controller_unittest.mm b/ios/chrome/browser/ui/ntp/google_landing_controller_unittest.mm index cd02478f..99b3cf8e 100644 --- a/ios/chrome/browser/ui/ntp/google_landing_controller_unittest.mm +++ b/ios/chrome/browser/ui/ntp/google_landing_controller_unittest.mm
@@ -4,7 +4,6 @@ #include <memory> -#include "base/mac/scoped_nsobject.h" #include "base/memory/ptr_util.h" #include "base/message_loop/message_loop.h" #include "components/search_engines/template_url_service.h" @@ -19,6 +18,10 @@ #include "testing/gtest_mac.h" #import "third_party/ocmock/OCMock/OCMock.h" +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + namespace { class GoogleLandingControllerTest : public BlockCleanupTest { @@ -49,14 +52,13 @@ template_url_service->Load(); // Set up stub UrlLoader. - mockUrlLoader_.reset( - [[OCMockObject mockForProtocol:@protocol(UrlLoader)] retain]); - controller_.reset([[GoogleLandingController alloc] - initWithLoader:(id<UrlLoader>)mockUrlLoader_.get() + mockUrlLoader_ = [OCMockObject mockForProtocol:@protocol(UrlLoader)]; + controller_ = [[GoogleLandingController alloc] + initWithLoader:(id<UrlLoader>)mockUrlLoader_ browserState:chrome_browser_state_.get() focuser:nil webToolbarDelegate:nil - tabModel:nil]); + tabModel:nil]; }; base::MessageLoopForUI message_loop_; @@ -64,12 +66,12 @@ web::TestWebThread io_thread_; IOSChromeScopedTestingLocalState local_state_; std::unique_ptr<TestChromeBrowserState> chrome_browser_state_; - base::scoped_nsobject<OCMockObject> mockUrlLoader_; - base::scoped_nsobject<GoogleLandingController> controller_; + OCMockObject* mockUrlLoader_; + GoogleLandingController* controller_; }; TEST_F(GoogleLandingControllerTest, TestConstructorDestructor) { - EXPECT_TRUE(controller_.get()); + EXPECT_TRUE(controller_); } } // anonymous namespace
diff --git a/ios/chrome/browser/ui/ntp/most_visited_cell_unittest.mm b/ios/chrome/browser/ui/ntp/most_visited_cell_unittest.mm index 9e12b2b..3c494ef 100644 --- a/ios/chrome/browser/ui/ntp/most_visited_cell_unittest.mm +++ b/ios/chrome/browser/ui/ntp/most_visited_cell_unittest.mm
@@ -2,24 +2,27 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/mac/scoped_nsobject.h" #import "ios/chrome/browser/ui/ntp/most_visited_cell.h" #include "testing/platform_test.h" +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + static NSString* title = @"Most Visited Cell Title"; const GURL URL = GURL("http://example.com"); // Fixture to test MostVisitedCell. class MostVisitedCellTest : public PlatformTest { protected: - base::scoped_nsobject<MostVisitedCell> cell_; + MostVisitedCell* cell_; }; TEST_F(MostVisitedCellTest, TestConstructor) { CGRect rect = CGRectMake(0, 0, 100, 100); - cell_.reset([[MostVisitedCell alloc] initWithFrame:rect]); + cell_ = [[MostVisitedCell alloc] initWithFrame:rect]; [cell_ setURL:URL]; - EXPECT_TRUE(cell_.get()); + EXPECT_TRUE(cell_); UIGraphicsBeginImageContext([cell_ bounds].size); [cell_ drawRect:[cell_ bounds]]; UIGraphicsEndImageContext(); @@ -28,7 +31,7 @@ TEST_F(MostVisitedCellTest, ValidateTitle) { CGRect rect = CGRectMake(0, 0, 100, 100); - cell_.reset([[MostVisitedCell alloc] initWithFrame:rect]); + cell_ = [[MostVisitedCell alloc] initWithFrame:rect]; [cell_ setText:title]; EXPECT_EQ(title, [cell_ accessibilityLabel]); }
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_bar_unittest.mm b/ios/chrome/browser/ui/ntp/new_tab_page_bar_unittest.mm index 73459f5..64be9b9 100644 --- a/ios/chrome/browser/ui/ntp/new_tab_page_bar_unittest.mm +++ b/ios/chrome/browser/ui/ntp/new_tab_page_bar_unittest.mm
@@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/mac/scoped_nsobject.h" #import "ios/chrome/browser/ui/ntp/new_tab_page_bar.h" #import "ios/chrome/browser/ui/ntp/new_tab_page_bar_item.h" #include "ios/chrome/browser/ui/ui_util.h" @@ -12,6 +11,10 @@ #import "third_party/ocmock/OCMock/OCMock.h" #import "third_party/ocmock/gtest_support.h" +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + @interface NewTabPageBar (Testing) - (void)buttonDidTap:(UIButton*)button; @end @@ -22,9 +25,9 @@ protected: void SetUp() override { CGRect frame = CGRectMake(0, 0, 320, 44); - bar_.reset([[NewTabPageBar alloc] initWithFrame:frame]); + bar_ = [[NewTabPageBar alloc] initWithFrame:frame]; }; - base::scoped_nsobject<NewTabPageBar> bar_; + NewTabPageBar* bar_; }; TEST_F(NewTabPageBarTest, SetItems) { @@ -45,14 +48,14 @@ image:[UIImage imageNamed:@"ntp_bookmarks"]]; [bar_ setItems:[NSArray arrayWithObject:firstItem]]; - EXPECT_EQ(bar_.get().buttons.count, 1U); + EXPECT_EQ(bar_.buttons.count, 1U); [bar_ setItems:[NSArray arrayWithObjects:firstItem, secondItem, nil]]; - EXPECT_EQ(bar_.get().buttons.count, 2U); + EXPECT_EQ(bar_.buttons.count, 2U); [bar_ setItems:[NSArray arrayWithObjects:firstItem, secondItem, thirdItem, nil]]; - EXPECT_EQ(bar_.get().buttons.count, 3U); + EXPECT_EQ(bar_.buttons.count, 3U); [bar_ setItems:[NSArray arrayWithObject:firstItem]]; - EXPECT_EQ(bar_.get().buttons.count, 1U); + EXPECT_EQ(bar_.buttons.count, 1U); } TEST_F(NewTabPageBarTest, SetSelectedIndex_iPadOnly) {
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_controller_unittest.mm b/ios/chrome/browser/ui/ntp/new_tab_page_controller_unittest.mm index 362c473..176c18b 100644 --- a/ios/chrome/browser/ui/ntp/new_tab_page_controller_unittest.mm +++ b/ios/chrome/browser/ui/ntp/new_tab_page_controller_unittest.mm
@@ -6,8 +6,6 @@ #include <memory> -#include "base/mac/scoped_nsautorelease_pool.h" -#include "base/mac/scoped_nsobject.h" #include "base/memory/ptr_util.h" #include "base/message_loop/message_loop.h" #include "components/bookmarks/test/bookmark_test_helpers.h" @@ -30,6 +28,10 @@ #import "third_party/ocmock/OCMock/OCMock.h" #import "third_party/ocmock/gtest_support.h" +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + @interface NewTabPageController (TestSupport) - (id<NewTabPagePanelProtocol>)currentController; - (id<NewTabPagePanelProtocol>)bookmarkController; @@ -51,7 +53,7 @@ } - (id<NewTabPagePanelProtocol>)incognitoController { - return incognitoController_.get(); + return incognitoController_; } @end @@ -84,17 +86,17 @@ ios::BookmarkModelFactory::GetForBrowserState( chrome_browser_state_.get())); GURL url(kChromeUINewTabURL); - controller_.reset([[NewTabPageController alloc] - initWithUrl:url - loader:nil - focuser:nil - ntpObserver:nil - browserState:chrome_browser_state_.get() - colorCache:nil - webToolbarDelegate:nil - tabModel:nil]); + controller_ = + [[NewTabPageController alloc] initWithUrl:url + loader:nil + focuser:nil + ntpObserver:nil + browserState:chrome_browser_state_.get() + colorCache:nil + webToolbarDelegate:nil + tabModel:nil]; - incognitoController_.reset([[NewTabPageController alloc] + incognitoController_ = [[NewTabPageController alloc] initWithUrl:url loader:nil focuser:nil @@ -103,12 +105,12 @@ ->GetOffTheRecordChromeBrowserState() colorCache:nil webToolbarDelegate:nil - tabModel:nil]); + tabModel:nil]; }; void TearDown() override { - incognitoController_.reset(); - controller_.reset(); + incognitoController_ = nil; + controller_ = nil; // There may be blocks released below that have weak references to |profile| // owned by chrome_browser_state_. Ensure BlockCleanupTest::TearDown() is @@ -120,11 +122,8 @@ web::TestWebThreadBundle thread_bundle_; IOSChromeScopedTestingLocalState local_state_; std::unique_ptr<TestChromeBrowserState> chrome_browser_state_; - base::scoped_nsobject<NewTabPageController> controller_; - base::scoped_nsobject<NewTabPageController> incognitoController_; - // The pool has to be the last declared field because it must be destroyed - // first. - base::mac::ScopedNSAutoreleasePool pool_; + NewTabPageController* controller_; + NewTabPageController* incognitoController_; }; TEST_F(NewTabPageControllerTest, NewTabBarItemDidChange) {
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_egtest.mm b/ios/chrome/browser/ui/ntp/new_tab_page_egtest.mm index 15b433e..078f4805 100644 --- a/ios/chrome/browser/ui/ntp/new_tab_page_egtest.mm +++ b/ios/chrome/browser/ui/ntp/new_tab_page_egtest.mm
@@ -114,14 +114,12 @@ // Tests that all items are accessible on the most visited page. - (void)testAccessibilityOnMostVisited { SelectNewTabPagePanel(NewTabPage::kMostVisitedPanel); - // TODO(crbug.com/640179): Implement and call chrome_test_util::VerifyAccessibilityForCurrentScreen(); } // Tests that all items are accessible on the open tabs page. - (void)testAccessibilityOnOpenTabs { SelectNewTabPagePanel(NewTabPage::kOpenTabsPanel); - // TODO(crbug.com/640179): Implement and call chrome_test_util::VerifyAccessibilityForCurrentScreen(); DismissNewTabPagePanel(); } @@ -129,7 +127,6 @@ // Tests that all items are accessible on the bookmarks page. - (void)testAccessibilityOnBookmarks { SelectNewTabPagePanel(NewTabPage::kBookmarksPanel); - // TODO(crbug.com/640179): Implement and call chrome_test_util::VerifyAccessibilityForCurrentScreen(); DismissNewTabPagePanel(); } @@ -139,7 +136,6 @@ chrome_test_util::OpenNewIncognitoTab(); SelectNewTabPagePanel(NewTabPage::kIncognitoPanel); WaitForHistoryToDisappear(); - // TODO(crbug.com/640179): Implement and call chrome_test_util::VerifyAccessibilityForCurrentScreen(); chrome_test_util::CloseAllIncognitoTabs(); }
diff --git a/ios/chrome/browser/ui/ntp/notification_promo_whats_new_unittest.mm b/ios/chrome/browser/ui/ntp/notification_promo_whats_new_unittest.mm index a03f7b7..9a917a9 100644 --- a/ios/chrome/browser/ui/ntp/notification_promo_whats_new_unittest.mm +++ b/ios/chrome/browser/ui/ntp/notification_promo_whats_new_unittest.mm
@@ -22,6 +22,10 @@ #include "ui/base/l10n/l10n_util.h" #include "url/gurl.h" +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + namespace { // Test fixture for NotificationPromoWhatsNew.
diff --git a/ios/chrome/test/earl_grey/BUILD.gn b/ios/chrome/test/earl_grey/BUILD.gn index 04ea600..3f1c658 100644 --- a/ios/chrome/test/earl_grey/BUILD.gn +++ b/ios/chrome/test/earl_grey/BUILD.gn
@@ -136,6 +136,7 @@ } source_set("test_support") { + configs += [ "//build/config/compiler:enable_arc" ] testonly = true sources = [ "accessibility_util.h",
diff --git a/ios/chrome/test/earl_grey/accessibility_util.mm b/ios/chrome/test/earl_grey/accessibility_util.mm index 438a703c..11fb5385 100644 --- a/ios/chrome/test/earl_grey/accessibility_util.mm +++ b/ios/chrome/test/earl_grey/accessibility_util.mm
@@ -7,6 +7,10 @@ #include "ios/chrome/test/earl_grey/accessibility_util.h" +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + namespace { // Returns whether a UIView is hidden from the screen, based on the alpha
diff --git a/ios/chrome/test/earl_grey/chrome_actions.mm b/ios/chrome/test/earl_grey/chrome_actions.mm index d893a86..3f2690f 100644 --- a/ios/chrome/test/earl_grey/chrome_actions.mm +++ b/ios/chrome/test/earl_grey/chrome_actions.mm
@@ -9,6 +9,10 @@ #import "ios/chrome/test/app/chrome_test_util.h" #import "ios/web/public/test/earl_grey/web_view_actions.h" +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + namespace chrome_test_util { id<GREYAction> longPressElementForContextMenu(const std::string& element_id,
diff --git a/ios/chrome/test/earl_grey/chrome_assertions.mm b/ios/chrome/test/earl_grey/chrome_assertions.mm index 947d09ac..2cdbc3cb 100644 --- a/ios/chrome/test/earl_grey/chrome_assertions.mm +++ b/ios/chrome/test/earl_grey/chrome_assertions.mm
@@ -10,6 +10,10 @@ #import "ios/chrome/test/app/tab_test_util.h" #import "ios/testing/wait_util.h" +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + namespace chrome_test_util { void AssertMainTabCount(NSUInteger expected_tab_count) {
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey.h b/ios/chrome/test/earl_grey/chrome_earl_grey.h index f9ac0224..fdacda6 100644 --- a/ios/chrome/test/earl_grey/chrome_earl_grey.h +++ b/ios/chrome/test/earl_grey/chrome_earl_grey.h
@@ -17,7 +17,10 @@ // error resulting from the execution, if one occurs. The return value is the // result of the JavaScript execution. If the request is timed out, then nil is // returned. -id ExecuteJavaScript(NSString* javascript, NSError** out_error); +// TODO(crbug.com/690057): Remove __unsafe_unretained once all callers are +// converted to ARC. +id ExecuteJavaScript(NSString* javascript, + NSError* __unsafe_unretained* out_error); } // namespace chrome_test_util
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey.mm b/ios/chrome/test/earl_grey/chrome_earl_grey.mm index 16ec1cf0..ffcfa51 100644 --- a/ios/chrome/test/earl_grey/chrome_earl_grey.mm +++ b/ios/chrome/test/earl_grey/chrome_earl_grey.mm
@@ -17,9 +17,14 @@ #import "ios/web/public/web_state/js/crw_js_injection_receiver.h" #import "ios/web/public/web_state/web_state.h" +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + namespace chrome_test_util { -id ExecuteJavaScript(NSString* javascript, NSError** out_error) { +id ExecuteJavaScript(NSString* javascript, + NSError* __unsafe_unretained* out_error) { __block bool did_complete = false; __block id result = nil; __block NSError* temp_error = nil; @@ -41,10 +46,11 @@ [condition waitWithTimeout:testing::kWaitForJSCompletionTimeout]; if (!did_complete) return nil; - [temp_error autorelease]; - if (out_error) - *out_error = temp_error; - return [result autorelease]; + if (out_error) { + NSError* __autoreleasing auto_released_error = temp_error; + *out_error = auto_released_error; + } + return result; } } // namespace chrome_test_util
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey_ui.mm b/ios/chrome/test/earl_grey/chrome_earl_grey_ui.mm index 835a55bb..5aef1f3 100644 --- a/ios/chrome/test/earl_grey/chrome_earl_grey_ui.mm +++ b/ios/chrome/test/earl_grey/chrome_earl_grey_ui.mm
@@ -13,6 +13,10 @@ #import "ios/web/public/test/earl_grey/js_test_util.h" #import "ios/web/public/test/earl_grey/web_view_matchers.h" +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + using testing::WaitUntilConditionOrTimeout; using testing::kWaitForPageLoadTimeout;
diff --git a/ios/chrome/test/earl_grey/chrome_matchers.mm b/ios/chrome/test/earl_grey/chrome_matchers.mm index 6b01367..8525323 100644 --- a/ios/chrome/test/earl_grey/chrome_matchers.mm +++ b/ios/chrome/test/earl_grey/chrome_matchers.mm
@@ -23,6 +23,10 @@ #import "ios/web/public/test/earl_grey/web_view_matchers.h" #include "ui/base/l10n/l10n_util.h" +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + namespace { // Script that returns document.body as a string. @@ -50,7 +54,7 @@ }), @"JavaScript did not complete"); - return [result autorelease]; + return result; } // TODO(crbug.com/684142): This matcher uses too many implementation details, @@ -68,9 +72,8 @@ [description appendText:@"navigation delegate"]; }; - return [[[GREYElementMatcherBlock alloc] initWithMatchesBlock:matches - descriptionBlock:describe] - autorelease]; + return [[GREYElementMatcherBlock alloc] initWithMatchesBlock:matches + descriptionBlock:describe]; } id<GREYMatcher> CollectionViewSwitchIsOn(BOOL isOn) { @@ -86,9 +89,8 @@ isOn ? @"ON" : @"OFF"]; [description appendText:name]; }; - return [[[GREYElementMatcherBlock alloc] initWithMatchesBlock:matches - descriptionBlock:describe] - autorelease]; + return [[GREYElementMatcherBlock alloc] initWithMatchesBlock:matches + descriptionBlock:describe]; } } // namespace @@ -156,9 +158,8 @@ return grey_allOf( WebViewWithNavDelegateOfClass([StaticHtmlViewController class]), - [[[GREYElementMatcherBlock alloc] initWithMatchesBlock:matches - descriptionBlock:describe] - autorelease], + [[GREYElementMatcherBlock alloc] initWithMatchesBlock:matches + descriptionBlock:describe], nil); }
diff --git a/ios/chrome/test/earl_grey/chrome_test_case.mm b/ios/chrome/test/earl_grey/chrome_test_case.mm index 9cf8d36..9e5a45bf 100644 --- a/ios/chrome/test/earl_grey/chrome_test_case.mm +++ b/ios/chrome/test/earl_grey/chrome_test_case.mm
@@ -17,6 +17,10 @@ #import "ios/chrome/test/app/tab_test_util.h" #import "ios/web/public/test/http_server.h" +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + namespace { NSString* const kFlakyEarlGreyTestTargetSuffix = @"_flaky_egtests"; @@ -76,7 +80,7 @@ @interface ChromeTestCase () { // Block to be executed during object tearDown. - base::mac::ScopedBlock<ProceduralBlock> _tearDownHandler; + ProceduralBlock _tearDownHandler; BOOL _isHTTPServerStopped; BOOL _isMockAuthenticationDisabled; @@ -157,7 +161,7 @@ [super setUp]; _isHTTPServerStopped = NO; _isMockAuthenticationDisabled = NO; - _tearDownHandler.reset(); + _tearDownHandler = nil; chrome_test_util::OpenNewTab(); [[GREYUIThreadExecutor sharedInstance] drainUntilIdle]; @@ -168,7 +172,7 @@ // server are running. - (void)tearDown { if (_tearDownHandler) { - _tearDownHandler.get()(); + _tearDownHandler(); } // Clear any remaining test accounts and signed in users. @@ -197,7 +201,7 @@ - (void)setTearDownHandler:(ProceduralBlock)tearDownHandler { // Enforce that only one |_tearDownHandler| is set per test. DCHECK(!_tearDownHandler); - _tearDownHandler.reset([tearDownHandler copy]); + _tearDownHandler = [tearDownHandler copy]; } + (void)removeAnyOpenMenusAndInfoBars {
diff --git a/ios/chrome/test/earl_grey/chrome_util.mm b/ios/chrome/test/earl_grey/chrome_util.mm index 02a8d31..57c49fc7 100644 --- a/ios/chrome/test/earl_grey/chrome_util.mm +++ b/ios/chrome/test/earl_grey/chrome_util.mm
@@ -11,6 +11,10 @@ #import "ios/testing/wait_util.h" #import "ios/web/public/test/earl_grey/web_view_matchers.h" +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + namespace { const NSTimeInterval kWaitForToolbarAnimationTimeout = 1.0; } // namespace
diff --git a/pdf/pdfium/fuzzers/BUILD.gn b/pdf/pdfium/fuzzers/BUILD.gn index 4636a39b..70a043c 100644 --- a/pdf/pdfium/fuzzers/BUILD.gn +++ b/pdf/pdfium/fuzzers/BUILD.gn
@@ -133,7 +133,12 @@ "//third_party/pdfium/testing/libfuzzer:pdf_codec_png_fuzzer", ] dict = "dicts/pdf_codec_png.dict" - seed_corpus = "corpora/pdf_codec_png" + seed_corpuses = [ + "corpora/pdf_codec_png", + "//cc/test/data", + "//third_party/WebKit/LayoutTests/images/png-suite/samples", + "//third_party/WebKit/LayoutTests/images/resources/pngfuzz", + ] } fuzzer_test("pdf_codec_tiff_fuzzer") {
diff --git a/remoting/protocol/connection_unittest.cc b/remoting/protocol/connection_unittest.cc index 5fcdc50..0dbd689 100644 --- a/remoting/protocol/connection_unittest.cc +++ b/remoting/protocol/connection_unittest.cc
@@ -11,6 +11,7 @@ #include "base/memory/ptr_util.h" #include "base/message_loop/message_loop.h" #include "base/run_loop.h" +#include "base/test/scoped_task_scheduler.h" #include "base/threading/thread.h" #include "base/threading/thread_checker.h" #include "remoting/base/constants.h" @@ -258,7 +259,8 @@ public testing::WithParamInterface<bool> { public: ConnectionTest() - : video_encode_thread_("VideoEncode"), + : scoped_task_scheduler_(&message_loop_), + video_encode_thread_("VideoEncode"), audio_encode_thread_("AudioEncode"), audio_decode_thread_("AudioDecode") { video_encode_thread_.Start(); @@ -429,6 +431,7 @@ } base::MessageLoopForIO message_loop_; + base::test::ScopedTaskScheduler scoped_task_scheduler_; std::unique_ptr<base::RunLoop> run_loop_; MockConnectionToClientEventHandler host_event_handler_;
diff --git a/remoting/protocol/webrtc_video_renderer_adapter.cc b/remoting/protocol/webrtc_video_renderer_adapter.cc index d237386..cc4c3146 100644 --- a/remoting/protocol/webrtc_video_renderer_adapter.cc +++ b/remoting/protocol/webrtc_video_renderer_adapter.cc
@@ -13,9 +13,8 @@ #include "base/location.h" #include "base/memory/ptr_util.h" #include "base/single_thread_task_runner.h" -#include "base/task_runner_util.h" +#include "base/task_scheduler/post_task.h" #include "base/threading/thread_task_runner_handle.h" -#include "base/threading/worker_pool.h" #include "remoting/protocol/client_video_stats_dispatcher.h" #include "remoting/protocol/frame_consumer.h" #include "remoting/protocol/frame_stats.h" @@ -175,8 +174,9 @@ video_renderer_->GetFrameConsumer()->AllocateFrame( webrtc::DesktopSize(frame->width(), frame->height())); - base::PostTaskAndReplyWithResult( - base::WorkerPool::GetTaskRunner(false).get(), FROM_HERE, + base::PostTaskWithTraitsAndReplyWithResult( + FROM_HERE, base::TaskTraits().WithShutdownBehavior( + base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN), base::Bind(&ConvertYuvToRgb, base::Passed(&frame), base::Passed(&rgb_frame), video_renderer_->GetFrameConsumer()->GetPixelFormat()),
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json index 3aa126b..37e1810 100644 --- a/testing/buildbot/chromium.memory.json +++ b/testing/buildbot/chromium.memory.json
@@ -593,7 +593,6 @@ }, "Linux TSan Tests": { "disabled_tests": { - "app_shell_browsertests": "http://crbug.com/455633", "browser_tests": "Too many errors, clean content_browsertests first. http://crbug.com/368525", "cc_unittests": "http://crbug.com/437454", "interactive_ui_tests": "http://crbug.com/455679"
diff --git a/testing/libfuzzer/fuzzers/BUILD.gn b/testing/libfuzzer/fuzzers/BUILD.gn index 15716933..45a433b 100644 --- a/testing/libfuzzer/fuzzers/BUILD.gn +++ b/testing/libfuzzer/fuzzers/BUILD.gn
@@ -172,7 +172,11 @@ "//ui/gfx", ] dict = "dicts/png.dict" - seed_corpus = "//cc/test/data" + seed_corpuses = [ + "//cc/test/data", + "//third_party/WebKit/LayoutTests/images/png-suite/samples", + "//third_party/WebKit/LayoutTests/images/resources/pngfuzz", + ] } fuzzer_test("zlib_uncompress_fuzzer") { @@ -203,6 +207,11 @@ "//third_party/libpng", ] dict = "dicts/png.dict" + seed_corpuses = [ + "//cc/test/data", + "//third_party/WebKit/LayoutTests/images/png-suite/samples", + "//third_party/WebKit/LayoutTests/images/resources/pngfuzz", + ] } fuzzer_test("v8_script_parser_fuzzer") {
diff --git a/third_party/WebKit/LayoutTests/svg/css/display-computed.html b/third_party/WebKit/LayoutTests/svg/css/display-computed.html index 839b2de3..4e67d3c 100644 --- a/third_party/WebKit/LayoutTests/svg/css/display-computed.html +++ b/third_party/WebKit/LayoutTests/svg/css/display-computed.html
@@ -9,6 +9,7 @@ #t4, #t4 g { display: inline-table; } #t5, #t5 g { display: table; } #t6, #t6 g { display: table-cell; } +#t7, #t7 g { display: contents; } </style> <svg id="t1"><g/></svg> <svg id="t2"><g/></svg> @@ -16,6 +17,7 @@ <svg id="t4"><g/></svg> <svg id="t5"><g/></svg> <svg id="t6"><g/></svg> +<svg id="t7"><g/></svg> <script> test(function(){ assert_equals(getComputedStyle(document.querySelector("#t1")).display, "inline"); }, "svg:svg display inline"); test(function(){ assert_equals(getComputedStyle(document.querySelector("#t1 g")).display, "inline"); }, "svg:g display inline"); @@ -29,4 +31,6 @@ test(function(){ assert_equals(getComputedStyle(document.querySelector("#t5 g")).display, "table"); }, "svg:g display table"); test(function(){ assert_equals(getComputedStyle(document.querySelector("#t6")).display, "table-cell"); }, "svg:svg display table-cell"); test(function(){ assert_equals(getComputedStyle(document.querySelector("#t6 g")).display, "table-cell"); }, "svg:g display table-cell"); +test(function(){ assert_equals(getComputedStyle(document.querySelector("#t7")).display, "inline"); }, "svg:svg display contents computes to inline"); +test(function(){ assert_equals(getComputedStyle(document.querySelector("#t7 g")).display, "inline"); }, "svg:g display contents computes to inline"); </script>
diff --git a/third_party/WebKit/LayoutTests/svg/css/display-expected.html b/third_party/WebKit/LayoutTests/svg/css/display-expected.html index 3fa5645..67a8e4f 100644 --- a/third_party/WebKit/LayoutTests/svg/css/display-expected.html +++ b/third_party/WebKit/LayoutTests/svg/css/display-expected.html
@@ -14,6 +14,7 @@ #t4 { display: inline-table; } #t5 { display: table; } #t6 { display: table-cell; } +#t7, #t8 { display: inline; } </style> <img id="t1" src="../../css2.1/support/1x1-transparent.png"> <img id="t2" src="../../css2.1/support/1x1-transparent.png"> @@ -24,3 +25,5 @@ <img id="t6" src="../../css2.1/support/1x1-transparent.png"> </div> +<img id="t7" src="../../css2.1/support/1x1-transparent.png"> +Inline content <img id="t8" src="../../css2.1/support/1x1-transparent.png">
diff --git a/third_party/WebKit/LayoutTests/svg/css/display.html b/third_party/WebKit/LayoutTests/svg/css/display.html index 651f96e..985d8ec3 100644 --- a/third_party/WebKit/LayoutTests/svg/css/display.html +++ b/third_party/WebKit/LayoutTests/svg/css/display.html
@@ -14,6 +14,7 @@ #t4 { display: inline-table; } #t5 { display: table; } #t6 { display: table-cell; } +#t7, #t8 { display: contents; } </style> <svg id="t1"></svg> <svg id="t2"></svg> @@ -24,3 +25,5 @@ <svg id="t6"></svg> </div> +<svg id="t7"></svg> +Inline content <svg id="t8"></svg>
diff --git a/third_party/WebKit/Source/bindings/scripts/code_generator_web_module.py b/third_party/WebKit/Source/bindings/scripts/code_generator_web_module.py index 7953d0fc..d9c49be 100644 --- a/third_party/WebKit/Source/bindings/scripts/code_generator_web_module.py +++ b/third_party/WebKit/Source/bindings/scripts/code_generator_web_module.py
@@ -25,6 +25,7 @@ from code_generator import CodeGeneratorBase, render_template # TODO(dglazkov): Move TypedefResolver to code_generator.py from code_generator_v8 import TypedefResolver +from name_style_converter import NameStyleConverter MODULE_PYNAME = os.path.splitext(os.path.basename(__file__))[0] + '.py' @@ -81,7 +82,8 @@ self.type_resolver = type_resolver def set_class_name(self, class_name): - self.result['class_name'] = class_name + converter = NameStyleConverter(class_name) + self.result['class_name'] = converter.to_all_cases() def set_inheritance(self, base_interface): if base_interface is None: @@ -140,13 +142,14 @@ template_filename = 'web_module_interface.%s.tmpl' % file_extension return self.jinja_env.get_template(template_filename) - # TODO(dglazkov): Move to CodeGeneratorBase. - def output_paths(self, definition_name): - header_path = posixpath.join(self.output_dir, - 'Web%s.h' % definition_name) - cpp_path = posixpath.join(self.output_dir, - 'Web%s.cpp' % definition_name) - return header_path, cpp_path + def generate_file(self, template_context, file_extension): + template = self.get_template(file_extension) + path = posixpath.join( + self.output_dir, + '%s.%s' % (template_context['class_name']['snake_case'], + file_extension)) + text = render_template(template, template_context) + return (path, text) def generate_interface_code(self, interface): # TODO(dglazkov): Implement callback interfaces. @@ -156,22 +159,14 @@ template_context = interface_context(interface) - cpp_template = self.get_template('cpp') - header_template = self.get_template('h') - cpp_text = render_template(cpp_template, template_context) - header_text = render_template(header_template, template_context) - header_path, cpp_path = self.output_paths(interface.name) - return ( - (header_path, header_text), - (cpp_path, cpp_text) + self.generate_file(template_context, 'h'), + self.generate_file(template_context, 'cc') ) def generate_code(self, definitions, definition_name): self.typedef_resolver.resolve(definitions, definition_name) - header_path, cpp_path = self.output_paths(definition_name) - template_context = {} # TODO(dglazkov): Implement dictionaries if definition_name not in definitions.interfaces: return None
diff --git a/third_party/WebKit/Source/bindings/scripts/code_generator_web_module_test.py b/third_party/WebKit/Source/bindings/scripts/code_generator_web_module_test.py index b3fcc42..3c370ad 100644 --- a/third_party/WebKit/Source/bindings/scripts/code_generator_web_module_test.py +++ b/third_party/WebKit/Source/bindings/scripts/code_generator_web_module_test.py
@@ -80,7 +80,11 @@ builder.set_class_name('foo') self.assertEqual({ 'code_generator': 'test', - 'class_name': 'foo', + 'class_name': { + 'snake_case': 'foo', + 'macro_case': 'FOO', + 'upper_camel_case': 'Foo' + }, }, builder.build()) def test_set_inheritance(self):
diff --git a/third_party/WebKit/Source/bindings/scripts/name_style_converer_test.py b/third_party/WebKit/Source/bindings/scripts/name_style_converer_test.py new file mode 100644 index 0000000..71067c3 --- /dev/null +++ b/third_party/WebKit/Source/bindings/scripts/name_style_converer_test.py
@@ -0,0 +1,79 @@ +# Copyright 2017 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. + +# pylint: disable=import-error,print-statement,relative-import,protected-access + +"""Unit tests for name_style_converter.py.""" + +import unittest + +from name_style_converter import NameStyleConverter +from name_style_converter import SmartTokenizer + + +class SmartTokenizerTest(unittest.TestCase): + def test_simple_cases(self): + tokenizer = SmartTokenizer('foo') + self.assertEqual(tokenizer.tokenize(), ['foo']) + + tokenizer = SmartTokenizer('fooBar') + self.assertEqual(tokenizer.tokenize(), ['foo', 'Bar']) + + tokenizer = SmartTokenizer('fooBarBaz') + self.assertEqual(tokenizer.tokenize(), ['foo', 'Bar', 'Baz']) + + tokenizer = SmartTokenizer('Baz') + self.assertEqual(tokenizer.tokenize(), ['Baz']) + + tokenizer = SmartTokenizer('') + self.assertEqual(tokenizer.tokenize(), []) + + tokenizer = SmartTokenizer('FOO') + self.assertEqual(tokenizer.tokenize(), ['FOO']) + + tokenizer = SmartTokenizer('foo2') + self.assertEqual(tokenizer.tokenize(), ['foo', '2']) + + def test_tricky_cases(self): + tokenizer = SmartTokenizer('XMLHttpRequest') + self.assertEqual(tokenizer.tokenize(), ['XML', 'Http', 'Request']) + + tokenizer = SmartTokenizer('HTMLElement') + self.assertEqual(tokenizer.tokenize(), ['HTML', 'Element']) + + tokenizer = SmartTokenizer('WebGLRenderingContext') + self.assertEqual(tokenizer.tokenize(), + ['WebGL', 'Rendering', 'Context']) + + tokenizer = SmartTokenizer('CanvasRenderingContext2D') + self.assertEqual(tokenizer.tokenize(), + ['Canvas', 'Rendering', 'Context', '2D']) + + tokenizer = SmartTokenizer('SVGSVGElement') + self.assertEqual(tokenizer.tokenize(), ['SVG', 'SVG', 'Element']) + + +class NameStyleConverterTest(unittest.TestCase): + def test_snake_case(self): + converter = NameStyleConverter('HTMLElement') + self.assertEqual(converter.to_snake_case(), 'html_element') + + def test_upper_camel_case(self): + converter = NameStyleConverter('someSuperThing') + self.assertEqual(converter.to_upper_camel_case(), 'SomeSuperThing') + + converter = NameStyleConverter('SVGElement') + self.assertEqual(converter.to_upper_camel_case(), 'SVGElement') + + def test_macro_case(self): + converter = NameStyleConverter('WebGLBaz2D') + self.assertEqual(converter.to_macro_case(), 'WEBGL_BAZ_2D') + + def test_all_cases(self): + converter = NameStyleConverter('SVGScriptElement') + self.assertEqual(converter.to_all_cases(), { + 'snake_case': 'svg_script_element', + 'upper_camel_case': 'SVGScriptElement', + 'macro_case': 'SVG_SCRIPT_ELEMENT', + })
diff --git a/third_party/WebKit/Source/bindings/scripts/name_style_converter.py b/third_party/WebKit/Source/bindings/scripts/name_style_converter.py new file mode 100644 index 0000000..8e82ff5e --- /dev/null +++ b/third_party/WebKit/Source/bindings/scripts/name_style_converter.py
@@ -0,0 +1,81 @@ +# Copyright 2017 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. + +# pylint: disable=import-error,print-statement,relative-import + +import re + +SPECIAL_PREFIXES = [ + 'WebGL', + 'SVG', +] + +MATCHING_EXPRESSION = '((?:[A-Z][a-z]+)|[0-9]D?$)' + + +class SmartTokenizer(object): + """Detects special cases that are not easily discernible without additional + knowledge, such as recognizing that in SVGSVGElement, the first two SVGs + are separate tokens, but WebGL is one token.""" + + def __init__(self, name): + self.remaining = name + + def detect_special_prefix(self): + for prefix in SPECIAL_PREFIXES: + if self.remaining.startswith(prefix): + result = self.remaining[:len(prefix)] + self.remaining = self.remaining[len(prefix):] + return result + return None + + def tokenize(self): + prefix = self.detect_special_prefix() + return filter(None, + [prefix] + re.split(MATCHING_EXPRESSION, self.remaining)) + + +class NameStyleConverter(object): + """Converts names from camelCase and other styles to various other styles. + """ + + def __init__(self, name): + self.tokens = self.tokenize(name) + + def tokenize(self, name): + tokenizer = SmartTokenizer(name) + return tokenizer.tokenize() + + def to_snake_case(self): + """Snake case is the file and variable name style per Google C++ Style + Guide: + https://google.github.io/styleguide/cppguide.html#Variable_Names + + Also known as the hacker case. + https://en.wikipedia.org/wiki/Snake_case + """ + return '_'.join([token.lower() for token in self.tokens]) + + def to_upper_camel_case(self): + """Upper-camel case is the class and function name style per + Google C++ Style Guide: + https://google.github.io/styleguide/cppguide.html#Function_Names + + Also known as the PascalCase. + https://en.wikipedia.org/wiki/Camel_case. + """ + return ''.join([token[0].upper() + token[1:] for token in self.tokens]) + + def to_macro_case(self): + """Macro case is the macro name style per Google C++ Style Guide: + https://google.github.io/styleguide/cppguide.html#Macro_Names + """ + return '_'.join([token.upper() for token in self.tokens]) + + def to_all_cases(self): + return { + 'snake_case': self.to_snake_case(), + 'upper_camel_case': self.to_upper_camel_case(), + 'macro_case': self.to_macro_case(), + }
diff --git a/third_party/WebKit/Source/bindings/templates/templates.gni b/third_party/WebKit/Source/bindings/templates/templates.gni index bc9e938..2a52410 100644 --- a/third_party/WebKit/Source/bindings/templates/templates.gni +++ b/third_party/WebKit/Source/bindings/templates/templates.gni
@@ -24,6 +24,7 @@ "partial_interface.h.tmpl", "union_container.cpp.tmpl", "union_container.h.tmpl", - "web_module_interface.cpp.tmpl", + "web_module_interface.cc.tmpl", + "web_module_interface.h.tmpl", ], "abspath")
diff --git a/third_party/WebKit/Source/bindings/templates/web_module_interface.cpp.tmpl b/third_party/WebKit/Source/bindings/templates/web_module_interface.cc.tmpl similarity index 86% rename from third_party/WebKit/Source/bindings/templates/web_module_interface.cpp.tmpl rename to third_party/WebKit/Source/bindings/templates/web_module_interface.cc.tmpl index b918325..b86dc511 100644 --- a/third_party/WebKit/Source/bindings/templates/web_module_interface.cpp.tmpl +++ b/third_party/WebKit/Source/bindings/templates/web_module_interface.cc.tmpl
@@ -2,7 +2,7 @@ {% include 'copyright_block.txt' %} -#include "{{class_name}}.h" +#include "{{class_name.snake_case}}.h" // TODO(dglazkov): Implement generating includes. {% for filename in cpp_includes %} @@ -26,7 +26,7 @@ // TODO(dglazkov): Implement method generation {% for method in methods %} -// {{method.return_type}} Cpp{{class_name}}::{{method.name}} +// {{method.return_type}} {{class_name.upper_camel_case}}::{{method.name}} {% endfor %} } // namespace api
diff --git a/third_party/WebKit/Source/bindings/templates/web_module_interface.h.tmpl b/third_party/WebKit/Source/bindings/templates/web_module_interface.h.tmpl index 4ffb197..0680708 100644 --- a/third_party/WebKit/Source/bindings/templates/web_module_interface.h.tmpl +++ b/third_party/WebKit/Source/bindings/templates/web_module_interface.h.tmpl
@@ -3,8 +3,8 @@ {% include 'copyright_block.txt' %} // TODO(dglazkov): Use chromium-style path. -#ifndef {{class_name}}_h -#define {{class_name}}_h +#ifndef {{class_name.macro_case}}_H +#define {{class_name.macro_case}}_H {% for include_file in header_includes %} #include "{{include_file}}" @@ -13,13 +13,13 @@ namespace blink { namespace api { -class {{class_name}}{{inherits_expression}} { +class {{class_name.upper_camel_case}}{{inherits_expression}} { }; } // namespace api } // namespace blink -#endif // {{class_name}}_h +#endif // {{class_name.macro_case}}_H {% endfilter %}
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/WebTestInterface3.cpp b/third_party/WebKit/Source/bindings/tests/results/core/test_interface_3.cc similarity index 89% rename from third_party/WebKit/Source/bindings/tests/results/core/WebTestInterface3.cpp rename to third_party/WebKit/Source/bindings/tests/results/core/test_interface_3.cc index c3f1f3b3..a5517c83 100644 --- a/third_party/WebKit/Source/bindings/tests/results/core/WebTestInterface3.cpp +++ b/third_party/WebKit/Source/bindings/tests/results/core/test_interface_3.cc
@@ -6,11 +6,11 @@ // DO NOT MODIFY! // This file has been generated from the Jinja2 template in -// third_party/WebKit/Source/bindings/templates/web_module_interface.cpp.tmpl +// third_party/WebKit/Source/bindings/templates/web_module_interface.cc.tmpl // clang-format off -#include "TestInterface3.h" +#include "test_interface_3.h" // TODO(dglazkov): Implement generating includes. #include "wtf/text/WTFString.h.h" @@ -26,7 +26,7 @@ // DOMString readonlyStringifierAttribute // TODO(dglazkov): Implement method generation -// void CppTestInterface3::voidMethodDocument +// void TestInterface3::voidMethodDocument } // namespace api } // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/WebTestInterface3.h b/third_party/WebKit/Source/bindings/tests/results/core/test_interface_3.h similarity index 86% rename from third_party/WebKit/Source/bindings/tests/results/core/WebTestInterface3.h rename to third_party/WebKit/Source/bindings/tests/results/core/test_interface_3.h index 44bf3f17..8091beba 100644 --- a/third_party/WebKit/Source/bindings/tests/results/core/WebTestInterface3.h +++ b/third_party/WebKit/Source/bindings/tests/results/core/test_interface_3.h
@@ -11,8 +11,8 @@ // clang-format off // TODO(dglazkov): Use chromium-style path. -#ifndef TestInterface3_h -#define TestInterface3_h +#ifndef TEST_INTERFACE_3_H +#define TEST_INTERFACE_3_H namespace blink { namespace api { @@ -23,4 +23,4 @@ } // namespace api } // namespace blink -#endif // TestInterface3_h +#endif // TEST_INTERFACE_3_H
diff --git a/third_party/WebKit/Source/core/css/resolver/StyleAdjuster.cpp b/third_party/WebKit/Source/core/css/resolver/StyleAdjuster.cpp index 015eb7e..31d6f404 100644 --- a/third_party/WebKit/Source/core/css/resolver/StyleAdjuster.cpp +++ b/third_party/WebKit/Source/core/css/resolver/StyleAdjuster.cpp
@@ -464,6 +464,18 @@ bool isSVGElement = element && element->isSVGElement(); if (isSVGElement) { + // display: contents computes to inline for replaced elements and form + // controls, and isn't specified for other kinds of SVG content[1], so let's + // just do the same here for all other SVG elements. + // + // If we wouldn't do this, then we'd need to ensure that display: contents + // doesn't prevent SVG elements from generating a LayoutObject in + // SVGElement::layoutObjectIsNeeded. + // + // [1]: https://www.w3.org/TR/SVG/painting.html#DisplayProperty + if (style.display() == EDisplay::Contents) + style.setDisplay(EDisplay::Inline); + // Only the root <svg> element in an SVG document fragment tree honors css // position. if (!(isSVGSVGElement(*element) && element->parentNode() &&
diff --git a/third_party/WebKit/Source/core/html/parser/HTMLDocumentParser.cpp b/third_party/WebKit/Source/core/html/parser/HTMLDocumentParser.cpp index 5a886100..20fb60a 100644 --- a/third_party/WebKit/Source/core/html/parser/HTMLDocumentParser.cpp +++ b/third_party/WebKit/Source/core/html/parser/HTMLDocumentParser.cpp
@@ -257,7 +257,7 @@ } void HTMLDocumentParser::pumpTokenizerIfPossible() { - checkIfBodyStlyesheetAdded(); + checkIfBodyStylesheetAdded(); if (isStopped() || isPaused()) return; @@ -273,7 +273,7 @@ ASSERT(shouldUseThreading()); ASSERT(m_haveBackgroundParser); - checkIfBodyStlyesheetAdded(); + checkIfBodyStylesheetAdded(); if (isStopped() || isPaused()) return; @@ -289,7 +289,7 @@ // We will not have a scriptRunner when parsing a DocumentFragment. if (m_scriptRunner) m_scriptRunner->processScriptElement(scriptElement, scriptStartPosition); - checkIfBodyStlyesheetAdded(); + checkIfBodyStylesheetAdded(); } bool HTMLDocumentParser::canTakeNextToken() { @@ -632,7 +632,7 @@ // isScheduledForResume() may be set here as a result of // processTokenizedChunkFromBackgroundParser running arbitrary javascript // which invokes nested event loops. (e.g. inspector breakpoints) - checkIfBodyStlyesheetAdded(); + checkIfBodyStylesheetAdded(); if (!isParsing() || isPaused() || isScheduledForResume()) break; @@ -757,7 +757,7 @@ token().clear(); m_treeBuilder->constructTree(&atomicToken); - checkIfBodyStlyesheetAdded(); + checkIfBodyStylesheetAdded(); // FIXME: constructTree may synchronously cause Document to be detached. if (!m_token) @@ -773,7 +773,7 @@ const CompactHTMLToken& compactToken) { AtomicHTMLToken token(compactToken); m_treeBuilder->constructTree(&token); - checkIfBodyStlyesheetAdded(); + checkIfBodyStylesheetAdded(); } bool HTMLDocumentParser::hasInsertionPoint() { @@ -1074,7 +1074,7 @@ ASSERT(!isExecutingScript()); DCHECK(!isPaused()); - checkIfBodyStlyesheetAdded(); + checkIfBodyStylesheetAdded(); if (isPaused()) return; @@ -1151,7 +1151,7 @@ m_addedPendingStylesheetInBody = false; } -void HTMLDocumentParser::checkIfBodyStlyesheetAdded() { +void HTMLDocumentParser::checkIfBodyStylesheetAdded() { if (m_addedPendingStylesheetInBody) { m_addedPendingStylesheetInBody = false; m_isWaitingForStylesheets = true;
diff --git a/third_party/WebKit/Source/core/html/parser/HTMLDocumentParser.h b/third_party/WebKit/Source/core/html/parser/HTMLDocumentParser.h index 08491fd..c3bc8a9 100644 --- a/third_party/WebKit/Source/core/html/parser/HTMLDocumentParser.h +++ b/third_party/WebKit/Source/core/html/parser/HTMLDocumentParser.h
@@ -177,7 +177,7 @@ void executeScriptsWaitingForResources() final; void didAddPendingStylesheetInBody() final; void didLoadAllBodyStylesheets() final; - void checkIfBodyStlyesheetAdded(); + void checkIfBodyStylesheetAdded(); void documentElementAvailable() override; // HTMLParserScriptRunnerHost
diff --git a/third_party/WebKit/Source/core/loader/DocumentLoader.cpp b/third_party/WebKit/Source/core/loader/DocumentLoader.cpp index d4e6c19..c527aa4 100644 --- a/third_party/WebKit/Source/core/loader/DocumentLoader.cpp +++ b/third_party/WebKit/Source/core/loader/DocumentLoader.cpp
@@ -30,7 +30,6 @@ #include "core/loader/DocumentLoader.h" #include "core/dom/Document.h" -#include "core/dom/DocumentParser.h" #include "core/dom/WeakIdentifierMap.h" #include "core/events/Event.h" #include "core/frame/Deprecation.h" @@ -40,7 +39,6 @@ #include "core/frame/Settings.h" #include "core/frame/csp/ContentSecurityPolicy.h" #include "core/html/HTMLFrameOwnerElement.h" -#include "core/html/parser/HTMLDocumentParser.h" #include "core/html/parser/TextResourceDecoder.h" #include "core/inspector/ConsoleMessage.h" #include "core/inspector/InspectorInstrumentation.h"
diff --git a/third_party/WebKit/Source/platform/BUILD.gn b/third_party/WebKit/Source/platform/BUILD.gn index 57dc61b..6c36e04 100644 --- a/third_party/WebKit/Source/platform/BUILD.gn +++ b/third_party/WebKit/Source/platform/BUILD.gn
@@ -2057,7 +2057,11 @@ "//third_party/libpng", ] dict = "//testing/libfuzzer/fuzzers/dicts/png.dict" - seed_corpus = "//third_party/WebKit/LayoutTests/images/resources/pngfuzz" + seed_corpuses = [ + "//cc/test/data", + "//third_party/WebKit/LayoutTests/images/png-suite/samples", + "//third_party/WebKit/LayoutTests/images/resources/pngfuzz", + ] } # Fuzzer for blink::JSONParser.
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl.cc b/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl.cc index cb4938d..5faa5e1 100644 --- a/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl.cc +++ b/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl.cc
@@ -47,9 +47,6 @@ // We do not throttle anything while audio is played and shortly after that. constexpr base::TimeDelta kThrottlingDelayAfterAudioIsPlayed = base::TimeDelta::FromSeconds(5); -// Maximum task queueing time before the main thread is considered unresponsive. -constexpr base::TimeDelta kMainThreadResponsivenessThreshold = - base::TimeDelta::FromMilliseconds(200); void ReportForegroundRendererTaskLoad(base::TimeTicks time, double load) { int load_percentage = static_cast<int>(load * 100); @@ -114,7 +111,6 @@ helper_.scheduler_tqm_delegate().get(), helper_.scheduler_tqm_delegate()->NowTicks()), policy_may_need_update_(&any_thread_lock_), - main_thread_responsiveness_threshold_(kMainThreadResponsivenessThreshold), weak_factory_(this) { task_queue_throttler_.reset( new TaskQueueThrottler(this, "renderer.scheduler")); @@ -1623,7 +1619,8 @@ MainThreadOnly().rail_mode_observer = observer; } -bool RendererSchedulerImpl::MainThreadSeemsUnresponsive() { +bool RendererSchedulerImpl::MainThreadSeemsUnresponsive( + base::TimeDelta main_thread_responsiveness_threshold) { base::TimeTicks now = tick_clock()->NowTicks(); base::TimeDelta estimated_queueing_time; @@ -1651,7 +1648,7 @@ queueing_time_estimator.EstimateQueueingTimeIncludingCurrentTask(now); bool main_thread_seems_unresponsive = - estimated_queueing_time > main_thread_responsiveness_threshold_; + estimated_queueing_time > main_thread_responsiveness_threshold; CompositorThreadOnly().main_thread_seems_unresponsive = main_thread_seems_unresponsive;
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl.h b/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl.h index 26162de4..4b94b721 100644 --- a/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl.h +++ b/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl.h
@@ -127,7 +127,8 @@ void SetTopLevelBlameContext( base::trace_event::BlameContext* blame_context) override; void SetRAILModeObserver(RAILModeObserver* observer) override; - bool MainThreadSeemsUnresponsive() override; + bool MainThreadSeemsUnresponsive( + base::TimeDelta main_thread_responsiveness_threshold) override; // RenderWidgetSignals::Observer implementation: void SetAllRenderWidgetsHidden(bool hidden) override; @@ -523,9 +524,6 @@ } PollableThreadSafeFlag policy_may_need_update_; - // The maximum expected queueing time before the main thread is considered - // unresponsive. - base::TimeDelta main_thread_responsiveness_threshold_; base::WeakPtrFactory<RendererSchedulerImpl> weak_factory_;
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl_unittest.cc b/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl_unittest.cc index 48cc8add..e7abd615 100644 --- a/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl_unittest.cc +++ b/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl_unittest.cc
@@ -206,10 +206,7 @@ RendererSchedulerImplForTest( scoped_refptr<SchedulerTqmDelegate> main_task_runner) - : RendererSchedulerImpl(main_task_runner), update_policy_count_(0) { - main_thread_responsiveness_threshold_ = - base::TimeDelta::FromMilliseconds(200); - } + : RendererSchedulerImpl(main_task_runner), update_policy_count_(0) {} void UpdatePolicyLocked(UpdateType update_type) override { update_policy_count_++; @@ -641,6 +638,10 @@ RendererSchedulerImpl::kRailsResponseTimeMillis); } + static base::TimeDelta responsiveness_threshold() { + return base::TimeDelta::FromMilliseconds(200); + } + template <typename E> static void CallForEachEnumValue(E first, E last, @@ -3755,44 +3756,53 @@ } TEST_F(RendererSchedulerImplTest, UnresponsiveMainThread) { - EXPECT_FALSE(scheduler_->MainThreadSeemsUnresponsive()); + EXPECT_FALSE( + scheduler_->MainThreadSeemsUnresponsive(responsiveness_threshold())); // Add one second long task. AdvanceTimeWithTask(1); - EXPECT_TRUE(scheduler_->MainThreadSeemsUnresponsive()); + EXPECT_TRUE( + scheduler_->MainThreadSeemsUnresponsive(responsiveness_threshold())); // Wait a second. clock_->Advance(base::TimeDelta::FromSecondsD(2)); AdvanceTimeWithTask(0.5); - EXPECT_FALSE(scheduler_->MainThreadSeemsUnresponsive()); + EXPECT_FALSE( + scheduler_->MainThreadSeemsUnresponsive(responsiveness_threshold())); } TEST_F(RendererSchedulerImplTest, ResponsiveMainThreadDuringTask) { - EXPECT_FALSE(scheduler_->MainThreadSeemsUnresponsive()); + EXPECT_FALSE( + scheduler_->MainThreadSeemsUnresponsive(responsiveness_threshold())); clock_->Advance(base::TimeDelta::FromSecondsD(2)); scheduler_->willProcessTask( scheduler_->TimerTaskRunner().get(), (clock_->NowTicks() - base::TimeTicks()).InSecondsF()); - EXPECT_FALSE(scheduler_->MainThreadSeemsUnresponsive()); + EXPECT_FALSE( + scheduler_->MainThreadSeemsUnresponsive(responsiveness_threshold())); } TEST_F(RendererSchedulerImplTest, UnresponsiveMainThreadWithContention) { // Process a long task, lock the queueing time estimator, and check that we // still report the main thread is unresponsive. AdvanceTimeWithTask(1); - EXPECT_TRUE(scheduler_->MainThreadSeemsUnresponsive()); + EXPECT_TRUE( + scheduler_->MainThreadSeemsUnresponsive(responsiveness_threshold())); GetQueueingTimeEstimatorLock(); - EXPECT_TRUE(scheduler_->MainThreadSeemsUnresponsive()); + EXPECT_TRUE( + scheduler_->MainThreadSeemsUnresponsive(responsiveness_threshold())); // Advance the clock, so that in the last second, we were responsive. clock_->Advance(base::TimeDelta::FromSecondsD(2)); // While the queueing time estimator is locked, we believe the thread to still // be unresponsive. - EXPECT_TRUE(scheduler_->MainThreadSeemsUnresponsive()); + EXPECT_TRUE( + scheduler_->MainThreadSeemsUnresponsive(responsiveness_threshold())); // Once we've dropped the lock, we realize the main thread is responsive. DropQueueingTimeEstimatorLock(); - EXPECT_FALSE(scheduler_->MainThreadSeemsUnresponsive()); + EXPECT_FALSE( + scheduler_->MainThreadSeemsUnresponsive(responsiveness_threshold())); } } // namespace scheduler
diff --git a/third_party/WebKit/Source/platform/scheduler/test/fake_renderer_scheduler.cc b/third_party/WebKit/Source/platform/scheduler/test/fake_renderer_scheduler.cc index 93bae1e3..633fccc8 100644 --- a/third_party/WebKit/Source/platform/scheduler/test/fake_renderer_scheduler.cc +++ b/third_party/WebKit/Source/platform/scheduler/test/fake_renderer_scheduler.cc
@@ -122,7 +122,8 @@ void FakeRendererScheduler::SetRAILModeObserver(RAILModeObserver* observer) {} -bool FakeRendererScheduler::MainThreadSeemsUnresponsive() { +bool FakeRendererScheduler::MainThreadSeemsUnresponsive( + base::TimeDelta main_thread_responsiveness_threshold) { return false; }
diff --git a/third_party/WebKit/public/platform/scheduler/renderer/renderer_scheduler.h b/third_party/WebKit/public/platform/scheduler/renderer/renderer_scheduler.h index f383b687..cdcc868 100644 --- a/third_party/WebKit/public/platform/scheduler/renderer/renderer_scheduler.h +++ b/third_party/WebKit/public/platform/scheduler/renderer/renderer_scheduler.h
@@ -190,7 +190,8 @@ // Returns whether or not the main thread appears unresponsive, based on the // length and frequency of recent main thread tasks. To be called from the // compositor thread. - virtual bool MainThreadSeemsUnresponsive() = 0; + virtual bool MainThreadSeemsUnresponsive( + base::TimeDelta main_thread_responsiveness_threshold) = 0; protected: RendererScheduler();
diff --git a/third_party/WebKit/public/platform/scheduler/test/fake_renderer_scheduler.h b/third_party/WebKit/public/platform/scheduler/test/fake_renderer_scheduler.h index ddb575a..c22c7aa 100644 --- a/third_party/WebKit/public/platform/scheduler/test/fake_renderer_scheduler.h +++ b/third_party/WebKit/public/platform/scheduler/test/fake_renderer_scheduler.h
@@ -60,7 +60,8 @@ void SetTopLevelBlameContext( base::trace_event::BlameContext* blame_context) override; void SetRAILModeObserver(RAILModeObserver* observer) override; - bool MainThreadSeemsUnresponsive() override; + bool MainThreadSeemsUnresponsive( + base::TimeDelta main_thread_responsiveness_threshold) override; private: DISALLOW_COPY_AND_ASSIGN(FakeRendererScheduler);
diff --git a/third_party/WebKit/public/platform/scheduler/test/mock_renderer_scheduler.h b/third_party/WebKit/public/platform/scheduler/test/mock_renderer_scheduler.h index 2dd0daf6..1d2d25d 100644 --- a/third_party/WebKit/public/platform/scheduler/test/mock_renderer_scheduler.h +++ b/third_party/WebKit/public/platform/scheduler/test/mock_renderer_scheduler.h
@@ -60,7 +60,7 @@ MOCK_METHOD1(SetTimerQueueSuspensionWhenBackgroundedEnabled, void(bool)); MOCK_METHOD1(SetTopLevelBlameContext, void(base::trace_event::BlameContext*)); MOCK_METHOD1(SetRAILModeObserver, void(RAILModeObserver*)); - MOCK_METHOD0(MainThreadSeemsUnresponsive, bool()); + MOCK_METHOD1(MainThreadSeemsUnresponsive, bool(base::TimeDelta)); private: DISALLOW_COPY_AND_ASSIGN(MockRendererScheduler);
diff --git a/third_party/ocmock/BUILD.gn b/third_party/ocmock/BUILD.gn index 22c1a545..8589255f 100644 --- a/third_party/ocmock/BUILD.gn +++ b/third_party/ocmock/BUILD.gn
@@ -35,6 +35,10 @@ "OCMock/NSMethodSignature+OCMAdditions.m", "OCMock/NSNotificationCenter+OCMAdditions.h", "OCMock/NSNotificationCenter+OCMAdditions.m", + "OCMock/NSObject+OCMAdditions.h", + "OCMock/NSObject+OCMAdditions.m", + "OCMock/NSValue+OCMAdditions.h", + "OCMock/NSValue+OCMAdditions.m", "OCMock/OCClassMockObject.h", "OCMock/OCClassMockObject.m", "OCMock/OCMArg.h", @@ -47,8 +51,22 @@ "OCMock/OCMConstraint.m", "OCMock/OCMExceptionReturnValueProvider.h", "OCMock/OCMExceptionReturnValueProvider.m", + "OCMock/OCMExpectationRecorder.h", + "OCMock/OCMExpectationRecorder.m", + "OCMock/OCMFunctions.h", + "OCMock/OCMFunctions.m", "OCMock/OCMIndirectReturnValueProvider.h", "OCMock/OCMIndirectReturnValueProvider.m", + "OCMock/OCMInvocationExpectation.h", + "OCMock/OCMInvocationExpectation.m", + "OCMock/OCMInvocationMatcher.h", + "OCMock/OCMInvocationMatcher.m", + "OCMock/OCMInvocationStub.h", + "OCMock/OCMInvocationStub.m", + "OCMock/OCMLocation.h", + "OCMock/OCMLocation.m", + "OCMock/OCMMacroState.h", + "OCMock/OCMMacroState.m", "OCMock/OCMNotificationPoster.h", "OCMock/OCMNotificationPoster.m", "OCMock/OCMObserverRecorder.h", @@ -57,19 +75,21 @@ "OCMock/OCMPassByRefSetter.m", "OCMock/OCMRealObjectForwarder.h", "OCMock/OCMRealObjectForwarder.m", + "OCMock/OCMRecorder.h", + "OCMock/OCMRecorder.m", "OCMock/OCMReturnValueProvider.h", "OCMock/OCMReturnValueProvider.m", + "OCMock/OCMStubRecorder.h", + "OCMock/OCMStubRecorder.m", + "OCMock/OCMVerifier.h", + "OCMock/OCMVerifier.m", "OCMock/OCMock.h", "OCMock/OCMockObject.h", "OCMock/OCMockObject.m", - "OCMock/OCMockRecorder.h", - "OCMock/OCMockRecorder.m", "OCMock/OCObserverMockObject.h", "OCMock/OCObserverMockObject.m", "OCMock/OCPartialMockObject.h", "OCMock/OCPartialMockObject.m", - "OCMock/OCPartialMockRecorder.h", - "OCMock/OCPartialMockRecorder.m", "OCMock/OCProtocolMockObject.h", "OCMock/OCProtocolMockObject.m", ] @@ -78,5 +98,11 @@ "//testing/gtest", ] public_configs = [ ":ocmock_config" ] - configs += [ ":ocmock_warnings" ] + configs -= [ + "//build/config/compiler:chromium_code", + ] + configs += [ + ":ocmock_warnings", + "//build/config/compiler:no_chromium_code", + ] }
diff --git a/third_party/ocmock/Changes.txt b/third_party/ocmock/Changes.txt index 87aa9ee2..78fa49fe 100644 --- a/third_party/ocmock/Changes.txt +++ b/third_party/ocmock/Changes.txt
@@ -1,255 +1,256 @@ -Chronological listing of changes. More detail is usually found in the Git commit messages -and/or the pull requests. +Listing of notable changes by release. More detail is usually found in the Git +commit messages and/or the pull requests. + +OCMock 3.1.4 / 3.1.5 (2015-08-26) + +* Fixed deployment target in podspec -2012-01-23 +OCMock 3.1.3 (2015-08-12) -* Avoiding deprecated method to convert to a C string (thanks to Kushal Pisavadia) +* Now throwing exception when trying to create mocks for nil (Nick Gravelyn) +* Fixed ARC related bug when boxing macro args (Richard Ross) +* Added target for dynamic iOS framework, which makes OCMock compatible with + Carthage (Piet Brauer) +* Memory management and other small bug fixes -2011-11-03 +OCMock 3.1.2 (2015-01-08) -* Recreated project from scratch with new conventions in Xcode 4.2 (thanks to Matt Di Pasquale) +* Fixed bugs around reject and expectation orders (Mason Glidden, Ben Asher) +* Small adjustments to build file and dependencies -2011-09-26 +OCMock 3.1.1 (2014-08-23) +* Fixed a recently introduced bug that resulted in class arguments and return + values not to be considered objects (Patrick Hartling, Max Shcheglov) + + +OCMock 3.1 (2014-08-22) + +* Converting number types to make andReturn more intuitive (Carl Lindberg) +* Macros now silence warnings about unused return values (Gordon Fontenot) +* Added isKindOfClass constraint (Ash Furrow) +* Performance and stability improvements. As a result it is no longer possible + use verify-after-running to verify certain methods: + - All methods implemented by NSObject and categories on it + - Private methods in core Apple classes, ie. the class name has an NS or UI + prefix and the method has an underscore prefix and/or suffix. + + +OCMock 3.0.2 (2014-07-07) + +* Fixed podspec + + +OCMock 3.0.1 (2014-07-06) + +* Fixed bug that prevented stubs from returning nil +* Fixed bug related to handling of weak references +* Improved error message when trying to mock undefined method +* Added support for matching of char* arguments + + +OCMock 3.0 (2014-06-12) + +* Added macro for verify with delay +* Fixed several critical bugs +* Allowing nil as block in stub action. With partial mocks this makes it + possible to overwrite a method to do nothing (Sam Stigler) +* More descriptive messages when trying to verify unknown method + + +OCMock 3.0.M3 (2014-05-31) + +* Changed license to Apache 2 license +* Added support for verify-after-run for class methods and for methods sent + directly to the real object covered by a partial mock. +* Using a temporary meta class subclass for mocking class methods, enabling + full clean-up. As a consequence class methods mocked on a given class are no + longer mocked in all subclasses. +* Throwing descriptive exception when attempting to create partial mock on + toll-free bridged classes and tagged pointers (Mark Larsen) + + +OCMock 3.0.M2 (2014-05-07) + +* Added support from verify-after-run. Only works for methods that are sent + to a mock object. Does not work for classes and methods sent directly to + the real object covered by a partial mock. +* Failures without location are now thrown as OCMockTestFailure exception, + not as NSInternalInconsistencyException + + +OCMock 3.0.M1 (2014-04-26) + +* Added macros for modern syntax +* Automatic deregistration of observer mocks + + + + +OCMock 2.2.4 (2014-04-04) + +2014-04-05 + +* Switched unit test for OCMock itself to XCTest. +* Added andForwardToRealObject support for class methods (Carl Lindberg) +* Extended OCMockObject with verifyWithDelay (Charles Harley, Daniel + Doubrovkine) + + +OCMock 2.2.2 (2013-12-19) + +* Added implementation for Apple-interal NSIsKind informal protocol (Brian + Gerstle) +* Various fixes for method with structure returns (Carl Lindberg) +* Added a specially typed method for object references to OCMArg. +* Fixed bug that caused matching to be aborted on first ignored non-object arg. +* Fixed a bug where partial mocks wouldn't clean up mocked class methods. + (we7teck) +* Improved value macro so it can take constant arguments and expressions. (Carl + Lindberg) +* Fixed a bug that caused crashes when methods that require "special" struct + returns were mocked in partial mocks. (Carl Lindberg) + + +OCMock 2.2.1 (2013-07-24) + +* Fixed several bugs regarding class method mocking in class hierarchies. +* Fixed bug preventing the same class method to be expected more than once. + + +OCMock 2.2 (2013-07-02) + +* Can ignore non-object arguments on a per-invocation basis. +* Added constraint for any selector. + + +OCMock 2.1.2 (2013-06-19) + +* Constraints implement NSCopying for OS X 10.9 SDK compatibility. + + +OCMock 2.1 (2013-03-15) + +* Stubbing an object creation method now handles retain count correctly. +* Added support for forwardingTagetForSelector: (thanks to Jeff Watkins) +* Added class method mocking capability to class mock objects +* Added implementation of isKindOfClass: to class mock objects +* Allowing to set non-object pass-by-ref args (thanks to Glenn L. Austin) +* Calling a previously expected method on a partial mock is no longer an error. + + +OCMock 2.0 (2012-03-02) + +* Avoiding deprecated method to convert to a C string (thanks to Kushal + Pisavadia) +* Recreated project from scratch with new conventions in Xcode 4.2 (thanks to + Matt Di Pasquale) * Arguments only need to be equal, don't have to have same class -Chronological listing of changes in the original Subversion code repository. If a particular -SVN revision has no entry, that check-in did not involve any code or feature changes. -2011-02-15 (r74) +OCMock 1.77 (2011-02-15) * Added feature to explicitly disable a partial mock - - -2011-01-28 (r71) - * Updated example to work with iOS 4.2. -2010-08-21 (r69) +OCMock 1.70 (2010-08-21) -* Added feature to explicitly reject methods on nice mocks (thanks to Heath Borders) - - -2010-08-20 (r68) - -* Added feature to forward method to real object from partial mock (thanks to Marco Sandrini) - - -2010-08-02 (r67) - +* Added feature to explicitly reject methods on nice mocks (thanks to Heath + Borders) +* Added feature to forward method to real object from partial mock (thanks to + Marco Sandrini) * Fix to allow block arguments (thanks to Justin DeWind) - - -2010-07-28 (r62-r65) - * Now building OCMock library for simulator (i386) and device (armv7) * Updated example to run tests on device * Changed OCMOCK_VALUE macro to be iOS compatible (thanks to Derek Clarkson) - - -2010-07-21 (r61) - * Added a new target to build a static library for iOS use * Created an example showing how to use OCMock in an iOS project - - -2010-05-19 (r57) - * Various small clean-ups; no change in functionality (thanks to Jonah Williams) - - -2010-04-18 (r56) - * Added block constraints and invocation handler (thanks to Justin DeWind) -2009-10-16 (r55) +OCMock 1.55 (2009-10-16) * Fixed broken test for array argument descciptions (Craig Beck) * Disambiguated mock table method name to avoid compiler warning * Renamed some variables to avoid warnings when using -Wshadow * Partial mocks are now deallocated as they should * Fixed problems that occured when using mocks as arguments - - -2009-08-18 (r54) - * OnCall methods now have same signature as replaced ones. - - -2009-08-14 (r53) - * Fixed possible retain bug (Daniel Eggert) - - -2009-08-14 (r52) - * Added feature that allows to verify expectations are called in sequence. * Improved detection of unqualified method return type. - - -2009-08-13 (r51) - * Fixed bug that caused crash when using method swizzling with void return type. - - -2009-07-14 (r49) - * Added support for calling arbitrary methods when stubbed methods are invoked. - - -2009-07-14 (r48) - -* Added support for posting notifications (based on Jean-Francois Dontigny's code) - - -2009-07-14 (r46) - +* Added support for posting notifications (based on Jean-Francois Dontigny's + code) * Fixed bug around complex type encodings (Jean-Francois Dontigny) - - -2009-05-26 (r45) - * Partial mocks now work on object reference and self (thanks to Mike Mangino) - - -2009-04-24 (r43) - * Added partial mocks (calls to the original object reference cannot be mocked) -2009-04-17 (r42) +OCMock 1.42 (2009-05-19) * Mock observers now handle user infos on notifications. - - -2009-04-09 (r39) - * Added inital support for mock observers (loosely based on Dave Dribbin's idea) - - -2009-04-08 (r38) - * Moved factory methods from OCMConstraint to OCMArg - - -2009-03-13 (r37) - * Added pass by ref argument setters - - -2009-03-11 (r34) - * Linked install name now uses @rpath (Dave Dribbin) - - -2009-02-22 (r32) - * Added support for respondsToSelector (Dave Dribin) * Added constraint for any pointer * Now comparing selectors as strings (Dado Colussi) -2008-07-07 (r28) +OCMock 1.29 (2008-07-07) * Resetting invocation target in recorder to avoid retain cycles. - - -2008-06-19 (r27) - * Added optional integration with hamcrest for constraints - - -2008-05-08 (r24) - * Now building quad-fat; the 64-bit versions are somewhat experimental though - - -2008-02-28 (r22) - * Using new functions to deal with protocols (Evan Doll) - - -2007-11-22 (r20) - * Added support for void* parameters (Tuukka Norri) -* Fixed a bug that could caused crashes when non-char const pointers were described - - -2007-11-22 (r19) - +* Fixed a bug that could caused crashes when non-char const pointers were + described * Fixed bug to allow mocking of methods with type qualifieres (Nikita Zhuk) - - -2007-10-22 (r18) - * Added a simple constraint implementation. -2007-06-04 (r15) +OCMock 1.17 (2007-06-04) -* Now re-throwing fail-fast exceptions, for unexpected invocations for example, when - verify is called; in case the first throw is ignored by a framework. - - -2007-04-23 (r14) - +* Now re-throwing fail-fast exceptions, for unexpected invocations for example, + when verify is called; in case the first throw is ignored by a framework. * Added nice mocks, i.e. mocks that don't raise on unknown methods (Mark Thomas) - * Fixed bug that prevented expectations after invocations (M. Scott Ford) - - -2006-06-11 (r12) - * Added possibility to throw an exception, based on code by Justin DeWind - -* Added Evan Doll's bugfix, which forwards conformsToProtocol: methods when necessary - -* Added the ability to match struct arguments, based on code contributed by Daniel Eggert - +* Added Evan Doll's bugfix, which forwards conformsToProtocol: methods when + necessary +* Added the ability to match struct arguments, based on code contributed by + Daniel Eggert * Better description of arguments, based on code contributed by Jeremy Higgs - -* Added the ability to create multiple identical expectations on the mock object (Jeremy Higgs) - +* Added the ability to create multiple identical expectations on the mock + object (Jeremy Higgs) * Added the ability to mock out nil arguments (Jeremy Higgs) +* Added slightly modified version of Jon Reid's contribution, which adds the + possibility to stub primitive return values. +* Added Jon Reid's bugfix that prevents a crash when trying to stub an unknown + method on a protocol. -2005-12-11 (r11) - -* Added slightly modified version of Jon Reid's contribution, which adds the possibility to stub - primitive return values. - -* Added Jon Reid's bugfix that prevents a crash when trying to stub an unknown method on a - protocol. - - -2005-10-03 (r10) +OCMock 1.10 (2005-10-03) * Upgraded to build and run tests using the OCUnit that is now part of XCode. - - -2005-10-03 (r9) - -* Added XCdoe 2.1 project - - -2005-02-16 (r8) - -* Added Richard Clark's contribution, which provides support for scalar arguments. - - -2005-02-13 (r7) - +* Added XCode 2.1 project +* Added Richard Clark's contribution, which provides support for scalar + arguments. * Added support for mocking formal protocols -2004-08-26 (r6) +OCMock 1.6 (2004-08-30) * MockObject and Recorder now inherit from NSProxy.
diff --git a/third_party/ocmock/License.txt b/third_party/ocmock/License.txt index 512ad8e..f433b1a5 100644 --- a/third_party/ocmock/License.txt +++ b/third_party/ocmock/License.txt
@@ -1,15 +1,177 @@ - - Copyright (c) 2004-2012 by Mulle Kybernetik. All rights reserved. - Permission to use, copy, modify and distribute this software and its documentation - is hereby granted, provided that both the copyright notice and this permission - notice appear in all copies of the software, derivative works or modified versions, - and any portions thereof, and that both notices appear in supporting documentation, - and that credit is given to Mulle Kybernetik in all documents and publicity - pertaining to direct or indirect use of this code or its derivatives. + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ - THIS IS EXPERIMENTAL SOFTWARE AND IT IS KNOWN TO HAVE BUGS, SOME OF WHICH MAY HAVE - SERIOUS CONSEQUENCES. THE COPYRIGHT HOLDER ALLOWS FREE USE OF THIS SOFTWARE IN ITS - "AS IS" CONDITION. THE COPYRIGHT HOLDER DISCLAIMS ANY LIABILITY OF ANY KIND FOR ANY - DAMAGES WHATSOEVER RESULTING DIRECTLY OR INDIRECTLY FROM THE USE OF THIS SOFTWARE - OR OF ANY DERIVATIVE WORK. \ No newline at end of file + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS
diff --git a/third_party/ocmock/OCMock/NSInvocation+OCMAdditions.h b/third_party/ocmock/OCMock/NSInvocation+OCMAdditions.h index 04f22cd..302ac62 100644 --- a/third_party/ocmock/OCMock/NSInvocation+OCMAdditions.h +++ b/third_party/ocmock/OCMock/NSInvocation+OCMAdditions.h
@@ -1,34 +1,47 @@ -//--------------------------------------------------------------------------------------- -// $Id$ -// Copyright (c) 2006-2009 by Mulle Kybernetik. See License file for details. -//--------------------------------------------------------------------------------------- +/* + * Copyright (c) 2006-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ #import <Foundation/Foundation.h> @interface NSInvocation(OCMAdditions) -- (id)getArgumentAtIndexAsObject:(int)argIndex; +- (BOOL)hasCharPointerArgument; + +- (id)getArgumentAtIndexAsObject:(NSInteger)argIndex; - (NSString *)invocationDescription; -- (NSString *)argumentDescriptionAtIndex:(int)argIndex; +- (NSString *)argumentDescriptionAtIndex:(NSInteger)argIndex; -- (NSString *)objectDescriptionAtIndex:(int)anInt; -- (NSString *)charDescriptionAtIndex:(int)anInt; -- (NSString *)unsignedCharDescriptionAtIndex:(int)anInt; -- (NSString *)intDescriptionAtIndex:(int)anInt; -- (NSString *)unsignedIntDescriptionAtIndex:(int)anInt; -- (NSString *)shortDescriptionAtIndex:(int)anInt; -- (NSString *)unsignedShortDescriptionAtIndex:(int)anInt; -- (NSString *)longDescriptionAtIndex:(int)anInt; -- (NSString *)unsignedLongDescriptionAtIndex:(int)anInt; -- (NSString *)longLongDescriptionAtIndex:(int)anInt; -- (NSString *)unsignedLongLongDescriptionAtIndex:(int)anInt; -- (NSString *)doubleDescriptionAtIndex:(int)anInt; -- (NSString *)floatDescriptionAtIndex:(int)anInt; -- (NSString *)structDescriptionAtIndex:(int)anInt; -- (NSString *)pointerDescriptionAtIndex:(int)anInt; -- (NSString *)cStringDescriptionAtIndex:(int)anInt; -- (NSString *)selectorDescriptionAtIndex:(int)anInt; +- (NSString *)objectDescriptionAtIndex:(NSInteger)anInt; +- (NSString *)charDescriptionAtIndex:(NSInteger)anInt; +- (NSString *)unsignedCharDescriptionAtIndex:(NSInteger)anInt; +- (NSString *)intDescriptionAtIndex:(NSInteger)anInt; +- (NSString *)unsignedIntDescriptionAtIndex:(NSInteger)anInt; +- (NSString *)shortDescriptionAtIndex:(NSInteger)anInt; +- (NSString *)unsignedShortDescriptionAtIndex:(NSInteger)anInt; +- (NSString *)longDescriptionAtIndex:(NSInteger)anInt; +- (NSString *)unsignedLongDescriptionAtIndex:(NSInteger)anInt; +- (NSString *)longLongDescriptionAtIndex:(NSInteger)anInt; +- (NSString *)unsignedLongLongDescriptionAtIndex:(NSInteger)anInt; +- (NSString *)doubleDescriptionAtIndex:(NSInteger)anInt; +- (NSString *)floatDescriptionAtIndex:(NSInteger)anInt; +- (NSString *)structDescriptionAtIndex:(NSInteger)anInt; +- (NSString *)pointerDescriptionAtIndex:(NSInteger)anInt; +- (NSString *)cStringDescriptionAtIndex:(NSInteger)anInt; +- (NSString *)selectorDescriptionAtIndex:(NSInteger)anInt; @end
diff --git a/third_party/ocmock/OCMock/NSInvocation+OCMAdditions.m b/third_party/ocmock/OCMock/NSInvocation+OCMAdditions.m index 25d5188..398187a 100644 --- a/third_party/ocmock/OCMock/NSInvocation+OCMAdditions.m +++ b/third_party/ocmock/OCMock/NSInvocation+OCMAdditions.m
@@ -1,119 +1,146 @@ -//--------------------------------------------------------------------------------------- -// $Id$ -// Copyright (c) 2006-2009 by Mulle Kybernetik. See License file for details. -//--------------------------------------------------------------------------------------- +/* + * Copyright (c) 2006-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ #import "NSInvocation+OCMAdditions.h" +#import "OCMFunctions.h" @implementation NSInvocation(OCMAdditions) -- (id)getArgumentAtIndexAsObject:(int)argIndex +- (BOOL)hasCharPointerArgument { - const char* argType; - - argType = [[self methodSignature] getArgumentTypeAtIndex:argIndex]; - while(strchr("rnNoORV", argType[0]) != NULL) - argType += 1; - + NSMethodSignature *signature = [self methodSignature]; + for(NSUInteger i = 0; i < [signature numberOfArguments]; i++) + { + const char *argType = OCMTypeWithoutQualifiers([signature getArgumentTypeAtIndex:i]); + if(strcmp(argType, "*") == 0) + return YES; + } + return NO; +} + + +- (id)getArgumentAtIndexAsObject:(NSInteger)argIndex +{ + const char *argType = OCMTypeWithoutQualifiers([[self methodSignature] getArgumentTypeAtIndex:(NSUInteger)argIndex]); + if((strlen(argType) > 1) && (strchr("{^", argType[0]) == NULL) && (strcmp("@?", argType) != 0)) [NSException raise:NSInvalidArgumentException format:@"Cannot handle argument type '%s'.", argType]; - - switch (argType[0]) + + if(OCMIsObjectType(argType)) + { + id value; + [self getArgument:&value atIndex:argIndex]; + return value; + } + + switch(argType[0]) { - case '#': - case '@': - { - id value; - [self getArgument:&value atIndex:argIndex]; - return value; - } case ':': { SEL s = (SEL)0; [self getArgument:&s atIndex:argIndex]; - id value = NSStringFromSelector(s); - return value; + return [NSValue valueWithBytes:&s objCType:":"]; } case 'i': { int value; [self getArgument:&value atIndex:argIndex]; - return [NSNumber numberWithInt:value]; + return @(value); } case 's': { short value; [self getArgument:&value atIndex:argIndex]; - return [NSNumber numberWithShort:value]; + return @(value); } case 'l': { long value; [self getArgument:&value atIndex:argIndex]; - return [NSNumber numberWithLong:value]; + return @(value); } case 'q': { long long value; [self getArgument:&value atIndex:argIndex]; - return [NSNumber numberWithLongLong:value]; + return @(value); } case 'c': { char value; [self getArgument:&value atIndex:argIndex]; - return [NSNumber numberWithChar:value]; + return @(value); } case 'C': { unsigned char value; [self getArgument:&value atIndex:argIndex]; - return [NSNumber numberWithUnsignedChar:value]; + return @(value); } case 'I': { unsigned int value; [self getArgument:&value atIndex:argIndex]; - return [NSNumber numberWithUnsignedInt:value]; + return @(value); } case 'S': { unsigned short value; [self getArgument:&value atIndex:argIndex]; - return [NSNumber numberWithUnsignedShort:value]; + return @(value); } case 'L': { unsigned long value; [self getArgument:&value atIndex:argIndex]; - return [NSNumber numberWithUnsignedLong:value]; + return @(value); } case 'Q': { unsigned long long value; [self getArgument:&value atIndex:argIndex]; - return [NSNumber numberWithUnsignedLongLong:value]; + return @(value); } case 'f': { float value; [self getArgument:&value atIndex:argIndex]; - return [NSNumber numberWithFloat:value]; + return @(value); } case 'd': { double value; [self getArgument:&value atIndex:argIndex]; - return [NSNumber numberWithDouble:value]; + return @(value); } + case 'D': + { + long double value; + [self getArgument:&value atIndex:argIndex]; + return [NSValue valueWithBytes:&value objCType:@encode(__typeof__(value))]; + } case 'B': { bool value; [self getArgument:&value atIndex:argIndex]; - return [NSNumber numberWithBool:value]; + return @(value); } case '^': + case '*': { void *value = NULL; [self getArgument:&value atIndex:argIndex]; @@ -121,8 +148,11 @@ } case '{': // structure { - NSUInteger maxArgSize = [[self methodSignature] frameLength]; - NSMutableData *argumentData = [[[NSMutableData alloc] initWithLength:maxArgSize] autorelease]; + NSUInteger argSize; + NSGetSizeAndAlignment([[self methodSignature] getArgumentTypeAtIndex:(NSUInteger)argIndex], &argSize, NULL); + if(argSize == 0) // TODO: Can this happen? Is frameLength a good choice in that case? + argSize = [[self methodSignature] frameLength]; + NSMutableData *argumentData = [[[NSMutableData alloc] initWithLength:argSize] autorelease]; [self getArgument:[argumentData mutableBytes] atIndex:argIndex]; return [NSValue valueWithBytes:[argumentData bytes] objCType:argType]; } @@ -142,21 +172,19 @@ NSArray *selectorParts = [NSStringFromSelector([self selector]) componentsSeparatedByString:@":"]; NSMutableString *description = [[NSMutableString alloc] init]; - unsigned int i; + NSUInteger i; for(i = 2; i < numberOfArgs; i++) { [description appendFormat:@"%@%@:", (i > 2 ? @" " : @""), [selectorParts objectAtIndex:(i - 2)]]; - [description appendString:[self argumentDescriptionAtIndex:i]]; + [description appendString:[self argumentDescriptionAtIndex:(NSInteger)i]]; } return [description autorelease]; } -- (NSString *)argumentDescriptionAtIndex:(int)argIndex +- (NSString *)argumentDescriptionAtIndex:(NSInteger)argIndex { - const char *argType = [[self methodSignature] getArgumentTypeAtIndex:argIndex]; - if(strchr("rnNoORV", argType[0]) != NULL) - argType += 1; + const char *argType = OCMTypeWithoutQualifiers([[self methodSignature] getArgumentTypeAtIndex:(NSUInteger)argIndex]); switch(*argType) { @@ -174,8 +202,8 @@ case 'Q': return [self unsignedLongLongDescriptionAtIndex:argIndex]; case 'd': return [self doubleDescriptionAtIndex:argIndex]; case 'f': return [self floatDescriptionAtIndex:argIndex]; - // Why does this throw EXC_BAD_ACCESS when appending the string? - // case NSObjCStructType: return [self structDescriptionAtIndex:index]; + case 'D': return [self longDoubleDescriptionAtIndex:argIndex]; + case '{': return [self structDescriptionAtIndex:argIndex]; case '^': return [self pointerDescriptionAtIndex:argIndex]; case '*': return [self cStringDescriptionAtIndex:argIndex]; case ':': return [self selectorDescriptionAtIndex:argIndex]; @@ -185,7 +213,7 @@ } -- (NSString *)objectDescriptionAtIndex:(int)anInt +- (NSString *)objectDescriptionAtIndex:(NSInteger)anInt { id object; @@ -195,18 +223,18 @@ else if(![object isProxy] && [object isKindOfClass:[NSString class]]) return [NSString stringWithFormat:@"@\"%@\"", [object description]]; else - return [object description]; + // The description cannot be nil, if it is then replace it + return [object description] ?: @"<nil description>"; } -- (NSString *)boolDescriptionAtIndex:(int)anInt +- (NSString *)boolDescriptionAtIndex:(NSInteger)anInt { bool value; - [self getArgument:&value atIndex:anInt]; - return value ? @"YES" : @"NO"; + return value? @"YES" : @"NO"; } -- (NSString *)charDescriptionAtIndex:(int)anInt +- (NSString *)charDescriptionAtIndex:(NSInteger)anInt { unsigned char buffer[128]; memset(buffer, 0x0, 128); @@ -215,12 +243,12 @@ // If there's only one character in the buffer, and it's 0 or 1, then we have a BOOL if (buffer[1] == '\0' && (buffer[0] == 0 || buffer[0] == 1)) - return [NSString stringWithFormat:@"%@", (buffer[0] == 1 ? @"YES" : @"NO")]; + return (buffer[0] == 1 ? @"YES" : @"NO"); else return [NSString stringWithFormat:@"'%c'", *buffer]; } -- (NSString *)unsignedCharDescriptionAtIndex:(int)anInt +- (NSString *)unsignedCharDescriptionAtIndex:(NSInteger)anInt { unsigned char buffer[128]; memset(buffer, 0x0, 128); @@ -229,7 +257,7 @@ return [NSString stringWithFormat:@"'%c'", *buffer]; } -- (NSString *)intDescriptionAtIndex:(int)anInt +- (NSString *)intDescriptionAtIndex:(NSInteger)anInt { int intValue; @@ -237,7 +265,7 @@ return [NSString stringWithFormat:@"%d", intValue]; } -- (NSString *)unsignedIntDescriptionAtIndex:(int)anInt +- (NSString *)unsignedIntDescriptionAtIndex:(NSInteger)anInt { unsigned int intValue; @@ -245,7 +273,7 @@ return [NSString stringWithFormat:@"%d", intValue]; } -- (NSString *)shortDescriptionAtIndex:(int)anInt +- (NSString *)shortDescriptionAtIndex:(NSInteger)anInt { short shortValue; @@ -253,7 +281,7 @@ return [NSString stringWithFormat:@"%hi", shortValue]; } -- (NSString *)unsignedShortDescriptionAtIndex:(int)anInt +- (NSString *)unsignedShortDescriptionAtIndex:(NSInteger)anInt { unsigned short shortValue; @@ -261,7 +289,7 @@ return [NSString stringWithFormat:@"%hu", shortValue]; } -- (NSString *)longDescriptionAtIndex:(int)anInt +- (NSString *)longDescriptionAtIndex:(NSInteger)anInt { long longValue; @@ -269,7 +297,7 @@ return [NSString stringWithFormat:@"%ld", longValue]; } -- (NSString *)unsignedLongDescriptionAtIndex:(int)anInt +- (NSString *)unsignedLongDescriptionAtIndex:(NSInteger)anInt { unsigned long longValue; @@ -277,7 +305,7 @@ return [NSString stringWithFormat:@"%lu", longValue]; } -- (NSString *)longLongDescriptionAtIndex:(int)anInt +- (NSString *)longLongDescriptionAtIndex:(NSInteger)anInt { long long longLongValue; @@ -285,7 +313,7 @@ return [NSString stringWithFormat:@"%qi", longLongValue]; } -- (NSString *)unsignedLongLongDescriptionAtIndex:(int)anInt +- (NSString *)unsignedLongLongDescriptionAtIndex:(NSInteger)anInt { unsigned long long longLongValue; @@ -293,7 +321,7 @@ return [NSString stringWithFormat:@"%qu", longLongValue]; } -- (NSString *)doubleDescriptionAtIndex:(int)anInt; +- (NSString *)doubleDescriptionAtIndex:(NSInteger)anInt { double doubleValue; @@ -301,7 +329,7 @@ return [NSString stringWithFormat:@"%f", doubleValue]; } -- (NSString *)floatDescriptionAtIndex:(int)anInt +- (NSString *)floatDescriptionAtIndex:(NSInteger)anInt { float floatValue; @@ -309,15 +337,20 @@ return [NSString stringWithFormat:@"%f", floatValue]; } -- (NSString *)structDescriptionAtIndex:(int)anInt; +- (NSString *)longDoubleDescriptionAtIndex:(NSInteger)anInt { - void *buffer; + long double longDoubleValue; - [self getArgument:&buffer atIndex:anInt]; - return [NSString stringWithFormat:@":(struct)%p", buffer]; + [self getArgument:&longDoubleValue atIndex:anInt]; + return [NSString stringWithFormat:@"%Lf", longDoubleValue]; } -- (NSString *)pointerDescriptionAtIndex:(int)anInt +- (NSString *)structDescriptionAtIndex:(NSInteger)anInt +{ + return [NSString stringWithFormat:@"(%@)", [[self getArgumentAtIndexAsObject:anInt] description]]; +} + +- (NSString *)pointerDescriptionAtIndex:(NSInteger)anInt { void *buffer; @@ -325,17 +358,18 @@ return [NSString stringWithFormat:@"%p", buffer]; } -- (NSString *)cStringDescriptionAtIndex:(int)anInt +- (NSString *)cStringDescriptionAtIndex:(NSInteger)anInt { - char buffer[128]; + char buffer[104]; + char *cStringPtr; - memset(buffer, 0x0, 128); - - [self getArgument:&buffer atIndex:anInt]; + [self getArgument:&cStringPtr atIndex:anInt]; + strlcpy(buffer, cStringPtr, sizeof(buffer)); + strlcpy(buffer + 100, "...", (sizeof(buffer) - 100)); return [NSString stringWithFormat:@"\"%s\"", buffer]; } -- (NSString *)selectorDescriptionAtIndex:(int)anInt +- (NSString *)selectorDescriptionAtIndex:(NSInteger)anInt { SEL selectorValue;
diff --git a/third_party/ocmock/OCMock/NSMethodSignature+OCMAdditions.h b/third_party/ocmock/OCMock/NSMethodSignature+OCMAdditions.h index 23741e3..c180206 100644 --- a/third_party/ocmock/OCMock/NSMethodSignature+OCMAdditions.h +++ b/third_party/ocmock/OCMock/NSMethodSignature+OCMAdditions.h
@@ -1,7 +1,18 @@ -//--------------------------------------------------------------------------------------- -// $Id$ -// Copyright (c) 2009 by Mulle Kybernetik. See License file for details. -//--------------------------------------------------------------------------------------- +/* + * Copyright (c) 2009-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ #import <Foundation/Foundation.h> @@ -13,6 +24,8 @@ @interface NSMethodSignature(OCMAdditions) -- (const char *)methodReturnTypeWithoutQualifiers; +- (BOOL)usesSpecialStructureReturn; +- (NSString *)fullTypeString; +- (const char *)fullObjCTypes; @end
diff --git a/third_party/ocmock/OCMock/NSMethodSignature+OCMAdditions.m b/third_party/ocmock/OCMock/NSMethodSignature+OCMAdditions.m index a69bb14..1012635c 100644 --- a/third_party/ocmock/OCMock/NSMethodSignature+OCMAdditions.m +++ b/third_party/ocmock/OCMock/NSMethodSignature+OCMAdditions.m
@@ -1,19 +1,61 @@ -//--------------------------------------------------------------------------------------- -// $Id$ -// Copyright (c) 2009 by Mulle Kybernetik. See License file for details. -//--------------------------------------------------------------------------------------- +/* + * Copyright (c) 2009-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ #import "NSMethodSignature+OCMAdditions.h" +#import "OCMFunctions.h" +#import <objc/runtime.h> @implementation NSMethodSignature(OCMAdditions) -- (const char *)methodReturnTypeWithoutQualifiers +- (BOOL)usesSpecialStructureReturn { - const char *returnType = [self methodReturnType]; - while(strchr("rnNoORV", returnType[0]) != NULL) - returnType += 1; - return returnType; + const char *types = OCMTypeWithoutQualifiers([self methodReturnType]); + + if((types == NULL) || (types[0] != '{')) + return NO; + + /* In some cases structures are returned by ref. The rules are complex and depend on the + architecture, see: + + http://sealiesoftware.com/blog/archive/2008/10/30/objc_explain_objc_msgSend_stret.html + http://developer.apple.com/library/mac/#documentation/DeveloperTools/Conceptual/LowLevelABI/000-Introduction/introduction.html + https://github.com/atgreen/libffi/blob/master/src/x86/ffi64.c + http://www.uclibc.org/docs/psABI-x86_64.pdf + http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042e/IHI0042E_aapcs.pdf + + NSMethodSignature knows the details but has no API to return it, though it is in + the debugDescription. Horribly kludgy. + */ + NSRange range = [[self debugDescription] rangeOfString:@"is special struct return? YES"]; + return range.length > 0; +} + +- (NSString *)fullTypeString +{ + NSMutableString *typeString = [NSMutableString string]; + [typeString appendFormat:@"%s", [self methodReturnType]]; + for (NSUInteger i=0; i<[self numberOfArguments]; i++) + [typeString appendFormat:@"%s", [self getArgumentTypeAtIndex:i]]; + return typeString; +} + +- (const char *)fullObjCTypes +{ + return [[self fullTypeString] UTF8String]; } @end
diff --git a/third_party/ocmock/OCMock/NSNotificationCenter+OCMAdditions.h b/third_party/ocmock/OCMock/NSNotificationCenter+OCMAdditions.h index ab4832b..b54e2b7 100644 --- a/third_party/ocmock/OCMock/NSNotificationCenter+OCMAdditions.h +++ b/third_party/ocmock/OCMock/NSNotificationCenter+OCMAdditions.h
@@ -1,15 +1,26 @@ -//--------------------------------------------------------------------------------------- -// $Id$ -// Copyright (c) 2009 by Mulle Kybernetik. See License file for details. -//--------------------------------------------------------------------------------------- +/* + * Copyright (c) 2009-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ #import <Foundation/Foundation.h> -@class OCMockObserver; +@class OCObserverMockObject; @interface NSNotificationCenter(OCMAdditions) -- (void)addMockObserver:(OCMockObserver *)notificationObserver name:(NSString *)notificationName object:(id)notificationSender; +- (void)addMockObserver:(OCObserverMockObject *)notificationObserver name:(NSString *)notificationName object:(id)notificationSender; @end
diff --git a/third_party/ocmock/OCMock/NSNotificationCenter+OCMAdditions.m b/third_party/ocmock/OCMock/NSNotificationCenter+OCMAdditions.m index 286cb68..b83da82 100644 --- a/third_party/ocmock/OCMock/NSNotificationCenter+OCMAdditions.m +++ b/third_party/ocmock/OCMock/NSNotificationCenter+OCMAdditions.m
@@ -1,7 +1,18 @@ -//--------------------------------------------------------------------------------------- -// $Id$ -// Copyright (c) 2009 by Mulle Kybernetik. See License file for details. -//--------------------------------------------------------------------------------------- +/* + * Copyright (c) 2009-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ #import "NSNotificationCenter+OCMAdditions.h" #import "OCObserverMockObject.h" @@ -9,8 +20,9 @@ @implementation NSNotificationCenter(OCMAdditions) -- (void)addMockObserver:(OCMockObserver *)notificationObserver name:(NSString *)notificationName object:(id)notificationSender +- (void)addMockObserver:(OCObserverMockObject *)notificationObserver name:(NSString *)notificationName object:(id)notificationSender { + [notificationObserver autoRemoveFromCenter:self]; [self addObserver:notificationObserver selector:@selector(handleNotification:) name:notificationName object:notificationSender]; }
diff --git a/third_party/ocmock/OCMock/NSObject+OCMAdditions.h b/third_party/ocmock/OCMock/NSObject+OCMAdditions.h new file mode 100644 index 0000000..805a9589 --- /dev/null +++ b/third_party/ocmock/OCMock/NSObject+OCMAdditions.h
@@ -0,0 +1,24 @@ +/* + * Copyright (c) 2013-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#import <Foundation/Foundation.h> + +@interface NSObject(OCMAdditions) + ++ (IMP)instanceMethodForwarderForSelector:(SEL)aSelector; ++ (void)enumerateMethodsInClass:(Class)aClass usingBlock:(void (^)(Class cls, SEL sel))aBlock; + +@end
diff --git a/third_party/ocmock/OCMock/NSObject+OCMAdditions.m b/third_party/ocmock/OCMock/NSObject+OCMAdditions.m new file mode 100644 index 0000000..87f1934 --- /dev/null +++ b/third_party/ocmock/OCMock/NSObject+OCMAdditions.m
@@ -0,0 +1,75 @@ +/* + * Copyright (c) 2009-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#import "NSObject+OCMAdditions.h" +#import "NSMethodSignature+OCMAdditions.h" +#import <objc/runtime.h> + +@implementation NSObject(OCMAdditions) + ++ (IMP)instanceMethodForwarderForSelector:(SEL)aSelector +{ + // use sel_registerName() and not @selector to avoid warning + SEL selectorWithNoImplementation = sel_registerName("methodWhichMustNotExist::::"); + +#ifndef __arm64__ + static NSMutableDictionary *_OCMReturnTypeCache; + + if(_OCMReturnTypeCache == nil) + _OCMReturnTypeCache = [[NSMutableDictionary alloc] init]; + + BOOL needsStructureReturn; + void *rawCacheKey[2] = { (void *)self, aSelector }; + NSData *cacheKey = [NSData dataWithBytes:rawCacheKey length:sizeof(rawCacheKey)]; + NSNumber *cachedValue = [_OCMReturnTypeCache objectForKey:cacheKey]; + + if(cachedValue == nil) + { + NSMethodSignature *sig = [self instanceMethodSignatureForSelector:aSelector]; + needsStructureReturn = [sig usesSpecialStructureReturn]; + [_OCMReturnTypeCache setObject:@(needsStructureReturn) forKey:cacheKey]; + } + else + { + needsStructureReturn = [cachedValue boolValue]; + } + + if(needsStructureReturn) + return class_getMethodImplementation_stret([NSObject class], selectorWithNoImplementation); +#endif + + return class_getMethodImplementation([NSObject class], selectorWithNoImplementation); +} + + ++ (void)enumerateMethodsInClass:(Class)aClass usingBlock:(void (^)(Class cls, SEL sel))aBlock +{ + for(Class cls = aClass; cls != nil; cls = class_getSuperclass(cls)) + { + Method *methodList = class_copyMethodList(cls, NULL); + if(methodList == NULL) + continue; + + for(Method *mPtr = methodList; *mPtr != NULL; mPtr++) + { + SEL sel = method_getName(*mPtr); + aBlock(cls, sel); + } + free(methodList); + } +} + +@end
diff --git a/third_party/ocmock/OCMock/NSValue+OCMAdditions.h b/third_party/ocmock/OCMock/NSValue+OCMAdditions.h new file mode 100644 index 0000000..81aa60a --- /dev/null +++ b/third_party/ocmock/OCMock/NSValue+OCMAdditions.h
@@ -0,0 +1,23 @@ +/* + * Copyright (c) 2014-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#import <Foundation/Foundation.h> + +@interface NSValue(OCMAdditions) + +- (BOOL)getBytes:(void *)outputBuf objCType:(const char *)targetType; + +@end
diff --git a/third_party/ocmock/OCMock/NSValue+OCMAdditions.m b/third_party/ocmock/OCMock/NSValue+OCMAdditions.m new file mode 100644 index 0000000..076a25c3 --- /dev/null +++ b/third_party/ocmock/OCMock/NSValue+OCMAdditions.m
@@ -0,0 +1,107 @@ +/* + * Copyright (c) 2014-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#import "NSValue+OCMAdditions.h" + + +@implementation NSValue(OCMAdditions) + +static CFNumberType OCMNumberTypeForObjCType(const char *objcType) +{ + switch (objcType[0]) + { + case 'c': return kCFNumberCharType; + case 'C': return kCFNumberCharType; + case 'B': return kCFNumberCharType; + case 's': return kCFNumberShortType; + case 'S': return kCFNumberShortType; + case 'i': return kCFNumberIntType; + case 'I': return kCFNumberIntType; + case 'l': return kCFNumberLongType; + case 'L': return kCFNumberLongType; + case 'q': return kCFNumberLongLongType; + case 'Q': return kCFNumberLongLongType; + case 'f': return kCFNumberFloatType; + case 'd': return kCFNumberDoubleType; + default: return 0; + } +} + + +static NSNumber *OCMNumberForValue(NSValue *value) +{ +#define CREATE_NUM(_type) ({ _type _v; [value getValue:&_v]; @(_v); }) + switch([value objCType][0]) + { + case 'c': return CREATE_NUM(char); + case 'C': return CREATE_NUM(unsigned char); + case 'B': return CREATE_NUM(bool); + case 's': return CREATE_NUM(short); + case 'S': return CREATE_NUM(unsigned short); + case 'i': return CREATE_NUM(int); + case 'I': return CREATE_NUM(unsigned int); + case 'l': return CREATE_NUM(long); + case 'L': return CREATE_NUM(unsigned long); + case 'q': return CREATE_NUM(long long); + case 'Q': return CREATE_NUM(unsigned long long); + case 'f': return CREATE_NUM(float); + case 'd': return CREATE_NUM(double); + default: return nil; + } +} + + +- (BOOL)getBytes:(void *)outputBuf objCType:(const char *)targetType +{ + /* + * See if they are similar number types, and if we can convert losslessly between them. + * For the most part, we set things up to use CFNumberGetValue, which returns false if + * conversion will be lossy. + */ + CFNumberType inputType = OCMNumberTypeForObjCType([self objCType]); + CFNumberType outputType = OCMNumberTypeForObjCType(targetType); + + if(inputType == 0 || outputType == 0) // one or both are non-number types + return NO; + + NSNumber *inputNumber = [self isKindOfClass:[NSNumber class]] ? (NSNumber *)self : OCMNumberForValue(self); + + /* + * Due to some legacy, back-compatible requirements in CFNumber.c, CFNumberGetValue can return true for + * some conversions which should not be allowed (by reading source, conversions from integer types to + * 8-bit or 16-bit integer types). So, check ourselves. + */ + long long min; + long long max; + long long val = [inputNumber longLongValue]; + switch(targetType[0]) + { + case 'B': + case 'c': min = CHAR_MIN; max = CHAR_MAX; break; + case 'C': min = 0; max = UCHAR_MAX; break; + case 's': min = SHRT_MIN; max = SHRT_MAX; break; + case 'S': min = 0; max = USHRT_MAX; break; + default: min = LLONG_MIN; max = LLONG_MAX; break; + } + if(val < min || val > max) + return NO; + + /* Get the number, and return NO if the value was out of range or conversion was lossy */ + return CFNumberGetValue((CFNumberRef)inputNumber, outputType, outputBuf); +} + + +@end
diff --git a/third_party/ocmock/OCMock/OCClassMockObject.h b/third_party/ocmock/OCMock/OCClassMockObject.h index 69945b2..e5f9cd86 100644 --- a/third_party/ocmock/OCMock/OCClassMockObject.h +++ b/third_party/ocmock/OCMock/OCClassMockObject.h
@@ -1,17 +1,30 @@ -//--------------------------------------------------------------------------------------- -// $Id$ -// Copyright (c) 2005-2008 by Mulle Kybernetik. See License file for details. -//--------------------------------------------------------------------------------------- +/* + * Copyright (c) 2005-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ #import <OCMock/OCMockObject.h> @interface OCClassMockObject : OCMockObject { - Class mockedClass; + Class mockedClass; + Class originalMetaClass; } - (id)initWithClass:(Class)aClass; - (Class)mockedClass; +- (Class)mockObjectClass; // since -class returns the mockedClass @end
diff --git a/third_party/ocmock/OCMock/OCClassMockObject.m b/third_party/ocmock/OCMock/OCClassMockObject.m index 65b8fdf..2e2bc1ec2 100644 --- a/third_party/ocmock/OCMock/OCClassMockObject.m +++ b/third_party/ocmock/OCMock/OCClassMockObject.m
@@ -1,10 +1,24 @@ -//--------------------------------------------------------------------------------------- -// $Id$ -// Copyright (c) 2005-2008 by Mulle Kybernetik. See License file for details. -//--------------------------------------------------------------------------------------- +/* + * Copyright (c) 2005-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +#import <objc/runtime.h> #import "OCClassMockObject.h" - +#import "NSObject+OCMAdditions.h" +#import "OCMFunctions.h" +#import "OCMInvocationStub.h" @implementation OCClassMockObject @@ -12,14 +26,22 @@ - (id)initWithClass:(Class)aClass { + NSParameterAssert(aClass != nil); [super init]; mockedClass = aClass; + [self prepareClassForClassMethodMocking]; return self; } +- (void)dealloc +{ + [self stopMocking]; + [super dealloc]; +} + - (NSString *)description { - return [NSString stringWithFormat:@"OCMockObject[%@]", NSStringFromClass(mockedClass)]; + return [NSString stringWithFormat:@"OCMockObject(%@)", NSStringFromClass(mockedClass)]; } - (Class)mockedClass @@ -27,12 +49,140 @@ return mockedClass; } +#pragma mark Extending/overriding superclass behaviour + +- (void)stopMocking +{ + if(originalMetaClass != nil) + [self restoreMetaClass]; + [super stopMocking]; +} + +- (void)restoreMetaClass +{ + OCMSetAssociatedMockForClass(nil, mockedClass); + OCMSetIsa(mockedClass, originalMetaClass); + originalMetaClass = nil; +} + +- (void)addStub:(OCMInvocationStub *)aStub +{ + [super addStub:aStub]; + if([aStub recordedAsClassMethod]) + [self setupForwarderForClassMethodSelector:[[aStub recordedInvocation] selector]]; +} + + +#pragma mark Class method mocking + +- (void)prepareClassForClassMethodMocking +{ + /* haven't figured out how to work around runtime dependencies on NSString, so exclude it for now */ + /* also weird: [[NSString class] isKindOfClass:[NSString class]] is false, hence the additional clause */ + if([[mockedClass class] isKindOfClass:[NSString class]] || (mockedClass == [NSString class])) + return; + + /* if there is another mock for this exact class, stop it */ + id otherMock = OCMGetAssociatedMockForClass(mockedClass, NO); + if(otherMock != nil) + [otherMock restoreMetaClass]; + + OCMSetAssociatedMockForClass(self, mockedClass); + + /* dynamically create a subclass and use its meta class as the meta class for the mocked class */ + Class subclass = OCMCreateSubclass(mockedClass, mockedClass); + originalMetaClass = object_getClass(mockedClass); + id newMetaClass = object_getClass(subclass); + OCMSetIsa(mockedClass, OCMGetIsa(subclass)); + + /* point forwardInvocation: of the object to the implementation in the mock */ + Method myForwardMethod = class_getInstanceMethod([self mockObjectClass], @selector(forwardInvocationForClassObject:)); + IMP myForwardIMP = method_getImplementation(myForwardMethod); + class_addMethod(newMetaClass, @selector(forwardInvocation:), myForwardIMP, method_getTypeEncoding(myForwardMethod)); + + /* create a dummy initialize method */ + Method myDummyInitializeMethod = class_getInstanceMethod([self mockObjectClass], @selector(initializeForClassObject)); + const char *initializeTypes = method_getTypeEncoding(myDummyInitializeMethod); + IMP myDummyInitializeIMP = method_getImplementation(myDummyInitializeMethod); + class_addMethod(newMetaClass, @selector(initialize), myDummyInitializeIMP, initializeTypes); + + /* adding forwarder for most class methods (instance methods on meta class) to allow for verify after run */ + NSArray *methodBlackList = @[@"class", @"forwardingTargetForSelector:", @"methodSignatureForSelector:", @"forwardInvocation:", @"isBlock", + @"instanceMethodForwarderForSelector:", @"instanceMethodSignatureForSelector:"]; + [NSObject enumerateMethodsInClass:originalMetaClass usingBlock:^(Class cls, SEL sel) { + if((cls == object_getClass([NSObject class])) || (cls == [NSObject class]) || (cls == object_getClass(cls))) + return; + NSString *className = NSStringFromClass(cls); + NSString *selName = NSStringFromSelector(sel); + if(([className hasPrefix:@"NS"] || [className hasPrefix:@"UI"]) && + ([selName hasPrefix:@"_"] || [selName hasSuffix:@"_"])) + return; + if([methodBlackList containsObject:selName]) + return; + @try + { + [self setupForwarderForClassMethodSelector:sel]; + } + @catch(NSException *e) + { + // ignore for now + } + }]; +} + +- (void)setupForwarderForClassMethodSelector:(SEL)selector +{ + SEL aliasSelector = OCMAliasForOriginalSelector(selector); + if(class_getClassMethod(mockedClass, aliasSelector) != NULL) + return; + + Method originalMethod = class_getClassMethod(mockedClass, selector); + IMP originalIMP = method_getImplementation(originalMethod); + const char *types = method_getTypeEncoding(originalMethod); + + Class metaClass = object_getClass(mockedClass); + IMP forwarderIMP = [originalMetaClass instanceMethodForwarderForSelector:selector]; + class_replaceMethod(metaClass, selector, forwarderIMP, types); + class_addMethod(metaClass, aliasSelector, originalIMP, types); +} + + +- (void)forwardInvocationForClassObject:(NSInvocation *)anInvocation +{ + // in here "self" is a reference to the real class, not the mock + OCClassMockObject *mock = OCMGetAssociatedMockForClass((Class) self, YES); + if(mock == nil) + { + [NSException raise:NSInternalInconsistencyException format:@"No mock for class %@", NSStringFromClass((Class)self)]; + } + if([mock handleInvocation:anInvocation] == NO) + { + [anInvocation setSelector:OCMAliasForOriginalSelector([anInvocation selector])]; + [anInvocation invoke]; + } +} + +- (void)initializeForClassObject +{ + // we really just want to have an implementation so that the superclass's is not called +} + #pragma mark Proxy API - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { - return [mockedClass instanceMethodSignatureForSelector:aSelector]; + return [mockedClass instanceMethodSignatureForSelector:aSelector]; +} + +- (Class)mockObjectClass +{ + return [super class]; +} + +- (Class)class +{ + return mockedClass; } - (BOOL)respondsToSelector:(SEL)selector @@ -40,4 +190,88 @@ return [mockedClass instancesRespondToSelector:selector]; } +- (BOOL)isKindOfClass:(Class)aClass +{ + return [mockedClass isSubclassOfClass:aClass]; +} + +- (BOOL)conformsToProtocol:(Protocol *)aProtocol +{ + return class_conformsToProtocol(mockedClass, aProtocol); +} + +@end + + +#pragma mark - + +/** + taken from: + `class-dump -f isNS /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator7.0.sdk/System/Library/Frameworks/CoreFoundation.framework` + + @interface NSObject (__NSIsKinds) + - (_Bool)isNSValue__; + - (_Bool)isNSTimeZone__; + - (_Bool)isNSString__; + - (_Bool)isNSSet__; + - (_Bool)isNSOrderedSet__; + - (_Bool)isNSNumber__; + - (_Bool)isNSDictionary__; + - (_Bool)isNSDate__; + - (_Bool)isNSData__; + - (_Bool)isNSArray__; + */ + +@implementation OCClassMockObject(NSIsKindsImplementation) + +- (BOOL)isNSValue__ +{ + return [mockedClass isKindOfClass:[NSValue class]]; +} + +- (BOOL)isNSTimeZone__ +{ + return [mockedClass isKindOfClass:[NSTimeZone class]]; +} + +- (BOOL)isNSSet__ +{ + return [mockedClass isKindOfClass:[NSSet class]]; +} + +- (BOOL)isNSOrderedSet__ +{ + return [mockedClass isKindOfClass:[NSOrderedSet class]]; +} + +- (BOOL)isNSNumber__ +{ + return [mockedClass isKindOfClass:[NSNumber class]]; +} + +- (BOOL)isNSDate__ +{ + return [mockedClass isKindOfClass:[NSDate class]]; +} + +- (BOOL)isNSString__ +{ + return [mockedClass isKindOfClass:[NSString class]]; +} + +- (BOOL)isNSDictionary__ +{ + return [mockedClass isKindOfClass:[NSDictionary class]]; +} + +- (BOOL)isNSData__ +{ + return [mockedClass isKindOfClass:[NSData class]]; +} + +- (BOOL)isNSArray__ +{ + return [mockedClass isKindOfClass:[NSArray class]]; +} + @end
diff --git a/third_party/ocmock/OCMock/OCMArg.h b/third_party/ocmock/OCMock/OCMArg.h index 669c0948..a8bdf55 100644 --- a/third_party/ocmock/OCMock/OCMArg.h +++ b/third_party/ocmock/OCMock/OCMArg.h
@@ -1,7 +1,18 @@ -//--------------------------------------------------------------------------------------- -// $Id$ -// Copyright (c) 2009-2010 by Mulle Kybernetik. See License file for details. -//--------------------------------------------------------------------------------------- +/* + * Copyright (c) 2009-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ #import <Foundation/Foundation.h> @@ -10,18 +21,21 @@ // constraining arguments + (id)any; ++ (SEL)anySelector; + (void *)anyPointer; ++ (id __autoreleasing *)anyObjectRef; + (id)isNil; + (id)isNotNil; ++ (id)isEqual:(id)value; + (id)isNotEqual:(id)value; ++ (id)isKindOfClass:(Class)cls; + (id)checkWithSelector:(SEL)selector onObject:(id)anObject; -#if NS_BLOCKS_AVAILABLE -+ (id)checkWithBlock:(BOOL (^)(id))block; -#endif ++ (id)checkWithBlock:(BOOL (^)(id obj))block; // manipulating arguments + (id *)setTo:(id)value; ++ (void *)setToValue:(NSValue *)value; // internal use only @@ -30,4 +44,10 @@ @end #define OCMOCK_ANY [OCMArg any] -#define OCMOCK_VALUE(variable) [NSValue value:&variable withObjCType:@encode(__typeof__(variable))] + +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) + #define OCMOCK_VALUE(variable) \ + ({ __typeof__(variable) __v = (variable); [NSValue value:&__v withObjCType:@encode(__typeof__(__v))]; }) +#else + #define OCMOCK_VALUE(variable) [NSValue value:&variable withObjCType:@encode(__typeof__(variable))] +#endif
diff --git a/third_party/ocmock/OCMock/OCMArg.m b/third_party/ocmock/OCMock/OCMArg.m index a73e3df..3a9aec33 100644 --- a/third_party/ocmock/OCMock/OCMArg.m +++ b/third_party/ocmock/OCMock/OCMArg.m
@@ -1,13 +1,23 @@ -//--------------------------------------------------------------------------------------- -// $Id$ -// Copyright (c) 2009-2010 by Mulle Kybernetik. See License file for details. -//--------------------------------------------------------------------------------------- +/* + * Copyright (c) 2009-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ #import <objc/runtime.h> #import <OCMock/OCMArg.h> #import <OCMock/OCMConstraint.h> #import "OCMPassByRefSetter.h" -#import "OCMConstraint.h" @implementation OCMArg @@ -21,6 +31,16 @@ return (void *)0x01234567; } ++ (id __autoreleasing *)anyObjectRef +{ + return (id *)0x01234567; +} + ++ (SEL)anySelector +{ + return NSSelectorFromString(@"aSelectorThatMatchesAnySelector"); +} + + (id)isNil { return [OCMIsNilConstraint constraint]; @@ -31,6 +51,11 @@ return [OCMIsNotNilConstraint constraint]; } ++ (id)isEqual:(id)value +{ + return value; +} + + (id)isNotEqual:(id)value { OCMIsNotEqualConstraint *constraint = [OCMIsNotEqualConstraint constraint]; @@ -38,25 +63,33 @@ return constraint; } ++ (id)isKindOfClass:(Class)cls +{ + return [[[OCMBlockConstraint alloc] initWithConstraintBlock:^BOOL(id obj) { + return [obj isKindOfClass:cls]; + }] autorelease]; +} + + (id)checkWithSelector:(SEL)selector onObject:(id)anObject { return [OCMConstraint constraintWithSelector:selector onObject:anObject]; } -#if NS_BLOCKS_AVAILABLE - -+ (id)checkWithBlock:(BOOL (^)(id))block ++ (id)checkWithBlock:(BOOL (^)(id))block { return [[[OCMBlockConstraint alloc] initWithConstraintBlock:block] autorelease]; } -#endif - + (id *)setTo:(id)value { return (id *)[[[OCMPassByRefSetter alloc] initWithValue:value] autorelease]; } ++ (void *)setToValue:(NSValue *)value +{ + return (id *)[[[OCMPassByRefSetter alloc] initWithValue:value] autorelease]; +} + + (id)resolveSpecialValues:(NSValue *)value { const char *type = [value objCType]; @@ -68,7 +101,15 @@ if((pointer != NULL) && (object_getClass((id)pointer) == [OCMPassByRefSetter class])) return (id)pointer; } + else if(type[0] == ':') + { + SEL selector; + [value getValue:&selector]; + if(selector == NSSelectorFromString(@"aSelectorThatMatchesAnySelector")) + return [OCMArg any]; + } return value; } + @end
diff --git a/third_party/ocmock/OCMock/OCMBlockCaller.h b/third_party/ocmock/OCMock/OCMBlockCaller.h index 652acc34..5c0aecc 100644 --- a/third_party/ocmock/OCMock/OCMBlockCaller.h +++ b/third_party/ocmock/OCMock/OCMBlockCaller.h
@@ -1,11 +1,21 @@ -//--------------------------------------------------------------------------------------- -// $Id$ -// Copyright (c) 2010 by Mulle Kybernetik. See License file for details. -//--------------------------------------------------------------------------------------- +/* + * Copyright (c) 2010-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ #import <Foundation/Foundation.h> -#if NS_BLOCKS_AVAILABLE @interface OCMBlockCaller : NSObject { @@ -18,4 +28,3 @@ @end -#endif
diff --git a/third_party/ocmock/OCMock/OCMBlockCaller.m b/third_party/ocmock/OCMock/OCMBlockCaller.m index 439d885..b804edd 100644 --- a/third_party/ocmock/OCMock/OCMBlockCaller.m +++ b/third_party/ocmock/OCMock/OCMBlockCaller.m
@@ -1,18 +1,31 @@ -//--------------------------------------------------------------------------------------- -// $Id$ -// Copyright (c) 2010 by Mulle Kybernetik. See License file for details. -//--------------------------------------------------------------------------------------- +/* + * Copyright (c) 2010-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ #import "OCMBlockCaller.h" -#if NS_BLOCKS_AVAILABLE @implementation OCMBlockCaller -(id)initWithCallBlock:(void (^)(NSInvocation *))theBlock { - self = [super init]; - block = [theBlock copy]; + if ((self = [super init])) + { + block = [theBlock copy]; + } + return self; } @@ -24,9 +37,10 @@ - (void)handleInvocation:(NSInvocation *)anInvocation { - block(anInvocation); + if (block != nil) + { + block(anInvocation); + } } @end - -#endif
diff --git a/third_party/ocmock/OCMock/OCMBoxedReturnValueProvider.h b/third_party/ocmock/OCMock/OCMBoxedReturnValueProvider.h index f2d9c919..bc6f119 100644 --- a/third_party/ocmock/OCMock/OCMBoxedReturnValueProvider.h +++ b/third_party/ocmock/OCMock/OCMBoxedReturnValueProvider.h
@@ -1,7 +1,18 @@ -//--------------------------------------------------------------------------------------- -// $Id$ -// Copyright (c) 2009 by Mulle Kybernetik. See License file for details. -//--------------------------------------------------------------------------------------- +/* + * Copyright (c) 2009-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ #import "OCMReturnValueProvider.h"
diff --git a/third_party/ocmock/OCMock/OCMBoxedReturnValueProvider.m b/third_party/ocmock/OCMock/OCMBoxedReturnValueProvider.m index 1ce9a18..6219fe5 100644 --- a/third_party/ocmock/OCMock/OCMBoxedReturnValueProvider.m +++ b/third_party/ocmock/OCMock/OCMBoxedReturnValueProvider.m
@@ -1,231 +1,61 @@ -//--------------------------------------------------------------------------------------- -// $Id$ -// Copyright (c) 2009 by Mulle Kybernetik. See License file for details. -//--------------------------------------------------------------------------------------- +/* + * Copyright (c) 2009-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ #import "OCMBoxedReturnValueProvider.h" - -static const char* OCMTypeWithoutQualifiers(const char* objCType) -{ - while(strchr("rnNoORV", objCType[0]) != NULL) - objCType += 1; - return objCType; -} - -/* - * Sometimes an external type is an opaque struct (which will have an @encode of "{structName}" - * or "{structName=}") but the actual method return type, or property type, will know the contents - * of the struct (so will have an objcType of say "{structName=iiSS}". This function will determine - * those are equal provided they have the same structure name, otherwise everything else will be - * compared textually. This can happen particularly for pointers to such structures, which still - * encode what is being pointed to. - */ -static BOOL OCMTypesEqualAllowOpaqueStructs(const char *type1, const char *type2) -{ - type1 = OCMTypeWithoutQualifiers(type1); - type2 = OCMTypeWithoutQualifiers(type2); - - switch (type1[0]) - { - case '{': - case '(': - { - if (type2[0] != type1[0]) - return NO; - char endChar = type1[0] == '{'? '}' : ')'; - - const char *type1End = strchr(type1, endChar); - const char *type2End = strchr(type2, endChar); - const char *type1Equals = strchr(type1, '='); - const char *type2Equals = strchr(type2, '='); - - /* Opaque types either don't have an equals sign (just the name and the end brace), or - * empty content after the equals sign. - * We want that to compare the same as a type of the same name but with the content. - */ - BOOL type1Opaque = (type1Equals == NULL || (type1End < type1Equals) || type1Equals[1] == endChar); - BOOL type2Opaque = (type2Equals == NULL || (type2End < type2Equals) || type2Equals[1] == endChar); - const char *type1NameEnd = (type1Equals == NULL || (type1End < type1Equals)) ? type1End : type1Equals; - const char *type2NameEnd = (type2Equals == NULL || (type2End < type2Equals)) ? type2End : type2Equals; - intptr_t type1NameLen = type1NameEnd - type1; - intptr_t type2NameLen = type2NameEnd - type2; - - /* If the names are not equal, return NO */ - if (type1NameLen != type2NameLen || strncmp(type1, type2, type1NameLen)) - return NO; - - /* If the same name, and at least one is opaque, that is close enough. */ - if (type1Opaque || type2Opaque) - return YES; - - /* Otherwise, compare all the elements. Use NSGetSizeAndAlignment to walk through the struct elements. */ - type1 = type1Equals + 1; - type2 = type2Equals + 1; - while (type1[0] != endChar && type1[0] != '\0') - { - if (!OCMTypesEqualAllowOpaqueStructs(type1, type2)) - return NO; - type1 = NSGetSizeAndAlignment(type1, NULL, NULL); - type2 = NSGetSizeAndAlignment(type2, NULL, NULL); - } - return YES; - } - case '^': - /* for a pointer, make sure the other is a pointer, then recursively compare the rest */ - if (type2[0] != type1[0]) - return NO; - return OCMTypesEqualAllowOpaqueStructs(type1+1, type2+1); - - case '?': - return type2[0] == '?'; - - case '\0': - return type2[0] == '\0'; - - default: - { - // Move the type pointers past the current types, then compare that region - const char *afterType1 = NSGetSizeAndAlignment(type1, NULL, NULL); - const char *afterType2 = NSGetSizeAndAlignment(type2, NULL, NULL); - intptr_t type1Len = afterType1 - type1; - intptr_t type2Len = afterType2 - type2; - - return (type1Len == type2Len && (strncmp(type1, type2, type1Len) == 0)); - } - } -} - -static CFNumberType OCMNumberTypeForObjCType(const char *objcType) -{ - switch (objcType[0]) - { - case 'c': return kCFNumberCharType; - case 'C': return kCFNumberCharType; - case 'B': return kCFNumberCharType; - case 's': return kCFNumberShortType; - case 'S': return kCFNumberShortType; - case 'i': return kCFNumberIntType; - case 'I': return kCFNumberIntType; - case 'l': return kCFNumberLongType; - case 'L': return kCFNumberLongType; - case 'q': return kCFNumberLongLongType; - case 'Q': return kCFNumberLongLongType; - case 'f': return kCFNumberFloatType; - case 'd': return kCFNumberDoubleType; - } - - return 0; -} - -static NSNumber *OCMNumberForValue(NSValue *value) -{ -#define CREATE_NUM(_type, _meth) ({ _type _v; [value getValue:&_v]; [NSNumber _meth _v]; }) - switch ([value objCType][0]) - { - case 'c': return CREATE_NUM(char, numberWithChar:); - case 'C': return CREATE_NUM(unsigned char, numberWithUnsignedChar:); - case 'B': return CREATE_NUM(bool, numberWithBool:); - case 's': return CREATE_NUM(short, numberWithShort:); - case 'S': return CREATE_NUM(unsigned short, numberWithUnsignedShort:); - case 'i': return CREATE_NUM(int, numberWithInt:); - case 'I': return CREATE_NUM(unsigned int, numberWithUnsignedInt:); - case 'l': return CREATE_NUM(long, numberWithLong:); - case 'L': return CREATE_NUM(unsigned long, numberWithUnsignedLong:); - case 'q': return CREATE_NUM(long long, numberWithLongLong:); - case 'Q': return CREATE_NUM(unsigned long long, numberWithUnsignedLongLong:); - case 'f': return CREATE_NUM(float, numberWithFloat:); - case 'd': return CREATE_NUM(double, numberWithDouble:); - } - - return nil; -} +#import "OCMFunctions.h" +#import "NSValue+OCMAdditions.h" @implementation OCMBoxedReturnValueProvider - (void)handleInvocation:(NSInvocation *)anInvocation { - const char *returnType = [[anInvocation methodSignature] methodReturnType]; + const char *returnType = [[anInvocation methodSignature] methodReturnType]; NSUInteger returnTypeSize = [[anInvocation methodSignature] methodReturnLength]; char valueBuffer[returnTypeSize]; - NSValue *value = (NSValue *)returnValue; - - if ([self getBytes:valueBuffer forValue:value compatibleWithType:returnType]) + NSValue *returnValueAsNSValue = (NSValue *)returnValue; + + if([self isMethodReturnType:returnType compatibleWithValueType:[returnValueAsNSValue objCType]]) + { + [returnValueAsNSValue getValue:valueBuffer]; + [anInvocation setReturnValue:valueBuffer]; + } + else if([returnValueAsNSValue getBytes:valueBuffer objCType:returnType]) { [anInvocation setReturnValue:valueBuffer]; } else { [NSException raise:NSInvalidArgumentException - format:@"Return value does not match method signature; signature declares '%s' but value is '%s'.", returnType, [value objCType]]; + format:@"Return value cannot be used for method; method signature declares '%s' but value is '%s'.", returnType, [returnValueAsNSValue objCType]]; } } + - (BOOL)isMethodReturnType:(const char *)returnType compatibleWithValueType:(const char *)valueType { - /* Allow void* for methods that return id, mainly to be able to handle nil */ - if(strcmp(returnType, @encode(id)) == 0 && strcmp(valueType, @encode(void *)) == 0) - return YES; - /* Same types are obviously compatible */ if(strcmp(returnType, valueType) == 0) return YES; - - @try { - if(OCMTypesEqualAllowOpaqueStructs(returnType, valueType)) - return YES; - } - @catch (NSException *e) { - /* Probably a bitfield or something that NSGetSizeAndAlignment chokes on, oh well */ - return NO; - } - return NO; -} - -- (BOOL)getBytes:(void *)outputBuf forValue:(NSValue *)inputValue compatibleWithType:(const char *)targetType -{ - /* If the types are directly compatible, use it */ - if ([self isMethodReturnType:targetType compatibleWithValueType:[inputValue objCType]]) - { - [inputValue getValue:outputBuf]; + /* Allow void* for methods that return id, mainly to be able to handle nil */ + if(strcmp(returnType, @encode(id)) == 0 && strcmp(valueType, @encode(void *)) == 0) return YES; - } - /* - * See if they are similar number types, and if we can convert losslessly between them. - * For the most part, we set things up to use CFNumberGetValue, which returns false if - * conversion will be lossy. - */ - CFNumberType inputType = OCMNumberTypeForObjCType([inputValue objCType]); - CFNumberType outputType = OCMNumberTypeForObjCType(targetType); - - if (inputType == 0 || outputType == 0) // one or both are non-number types - return NO; - - - NSNumber *inputNumber = [inputValue isKindOfClass:[NSNumber class]]? (id)inputValue : OCMNumberForValue(inputValue); - - /* - * Due to some legacy, back-compatible requirements in CFNumber.c, CFNumberGetValue can return true for - * some conversions which should not be allowed (by reading source, conversions from integer types to - * 8-bit or 16-bit integer types). So, check ourselves. - */ - long long min = LLONG_MIN; - long long max = LLONG_MAX; - long long val = [inputNumber longLongValue]; - switch (targetType[0]) - { - case 'B': - case 'c': min = CHAR_MIN; max = CHAR_MAX; break; - case 'C': min = 0; max = UCHAR_MAX; break; - case 's': min = SHRT_MIN; max = SHRT_MAX; break; - case 'S': min = 0; max = USHRT_MAX; break; - } - if (val < min || val > max) - return NO; - - /* Get the number, and return NO if the value was out of range or conversion was lossy */ - return CFNumberGetValue((CFNumberRef)inputNumber, outputType, outputBuf); + return OCMEqualTypesAllowingOpaqueStructs(returnType, valueType); } + @end
diff --git a/third_party/ocmock/OCMock/OCMConstraint.h b/third_party/ocmock/OCMock/OCMConstraint.h index 3ae1264..2164f32 100644 --- a/third_party/ocmock/OCMock/OCMConstraint.h +++ b/third_party/ocmock/OCMock/OCMConstraint.h
@@ -1,22 +1,33 @@ -//--------------------------------------------------------------------------------------- -// $Id$ -// Copyright (c) 2007-2010 by Mulle Kybernetik. See License file for details. -//--------------------------------------------------------------------------------------- +/* + * Copyright (c) 2007-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ #import <Foundation/Foundation.h> @interface OCMConstraint : NSObject -+ (id)constraint; ++ (instancetype)constraint; - (BOOL)evaluate:(id)value; // if you are looking for any, isNil, etc, they have moved to OCMArg // try to use [OCMArg checkWith...] instead of the constraintWith... methods below -+ (id)constraintWithSelector:(SEL)aSelector onObject:(id)anObject; -+ (id)constraintWithSelector:(SEL)aSelector onObject:(id)anObject withValue:(id)aValue; ++ (instancetype)constraintWithSelector:(SEL)aSelector onObject:(id)anObject; ++ (instancetype)constraintWithSelector:(SEL)aSelector onObject:(id)anObject withValue:(id)aValue; @end @@ -46,19 +57,15 @@ @end -#if NS_BLOCKS_AVAILABLE - @interface OCMBlockConstraint : OCMConstraint { BOOL (^block)(id); } -- (id)initWithConstraintBlock:(BOOL (^)(id))block; +- (instancetype)initWithConstraintBlock:(BOOL (^)(id))block; @end -#endif - #define CONSTRAINT(aSelector) [OCMConstraint constraintWithSelector:aSelector onObject:self] #define CONSTRAINTV(aSelector, aValue) [OCMConstraint constraintWithSelector:aSelector onObject:self withValue:(aValue)]
diff --git a/third_party/ocmock/OCMock/OCMConstraint.m b/third_party/ocmock/OCMock/OCMConstraint.m index b4535a4..d92be74 100644 --- a/third_party/ocmock/OCMock/OCMConstraint.m +++ b/third_party/ocmock/OCMock/OCMConstraint.m
@@ -1,14 +1,25 @@ -//--------------------------------------------------------------------------------------- -// $Id$ -// Copyright (c) 2007-2010 by Mulle Kybernetik. See License file for details. -//--------------------------------------------------------------------------------------- +/* + * Copyright (c) 2007-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ #import <OCMock/OCMConstraint.h> @implementation OCMConstraint -+ (id)constraint ++ (instancetype)constraint { return [[[self alloc] init] autorelease]; } @@ -18,12 +29,12 @@ return NO; } -- (id)copyWithZone:(NSZone *)zone +- (id)copyWithZone:(struct _NSZone *)zone { - return [self retain]; + return [self retain]; } -+ (id)constraintWithSelector:(SEL)aSelector onObject:(id)anObject ++ (instancetype)constraintWithSelector:(SEL)aSelector onObject:(id)anObject { OCMInvocationConstraint *constraint = [OCMInvocationConstraint constraint]; NSMethodSignature *signature = [anObject methodSignatureForSelector:aSelector]; @@ -36,7 +47,7 @@ return constraint; } -+ (id)constraintWithSelector:(SEL)aSelector onObject:(id)anObject withValue:(id)aValue ++ (instancetype)constraintWithSelector:(SEL)aSelector onObject:(id)anObject withValue:(id)aValue { OCMInvocationConstraint *constraint = [self constraintWithSelector:aSelector onObject:anObject]; if([[constraint->invocation methodSignature] numberOfArguments] < 4) @@ -119,22 +130,27 @@ #pragma mark - -#if NS_BLOCKS_AVAILABLE - @implementation OCMBlockConstraint -- (id)initWithConstraintBlock:(BOOL (^)(id))aBlock; +- (instancetype)initWithConstraintBlock:(BOOL (^)(id))aBlock { - self = [super init]; - block = aBlock; + if ((self = [super init])) + { + block = [aBlock copy]; + } + return self; } +- (void)dealloc { + [block release]; + [super dealloc]; +} + - (BOOL)evaluate:(id)value { - return block(value); + return block ? block(value) : NO; } -@end -#endif +@end
diff --git a/third_party/ocmock/OCMock/OCMExceptionReturnValueProvider.h b/third_party/ocmock/OCMock/OCMExceptionReturnValueProvider.h index 8e97469..1b9f3ad5 100644 --- a/third_party/ocmock/OCMock/OCMExceptionReturnValueProvider.h +++ b/third_party/ocmock/OCMock/OCMExceptionReturnValueProvider.h
@@ -1,7 +1,18 @@ -//--------------------------------------------------------------------------------------- -// $Id$ -// Copyright (c) 2009 by Mulle Kybernetik. See License file for details. -//--------------------------------------------------------------------------------------- +/* + * Copyright (c) 2009-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ #import "OCMReturnValueProvider.h"
diff --git a/third_party/ocmock/OCMock/OCMExceptionReturnValueProvider.m b/third_party/ocmock/OCMock/OCMExceptionReturnValueProvider.m index 784d1a79..ac65e22d 100644 --- a/third_party/ocmock/OCMock/OCMExceptionReturnValueProvider.m +++ b/third_party/ocmock/OCMock/OCMExceptionReturnValueProvider.m
@@ -1,7 +1,18 @@ -//--------------------------------------------------------------------------------------- -// $Id$ -// Copyright (c) 2009 by Mulle Kybernetik. See License file for details. -//--------------------------------------------------------------------------------------- +/* + * Copyright (c) 2009-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ #import "OCMExceptionReturnValueProvider.h"
diff --git a/third_party/ocmock/OCMock/OCMExpectationRecorder.h b/third_party/ocmock/OCMock/OCMExpectationRecorder.h new file mode 100644 index 0000000..592c179 --- /dev/null +++ b/third_party/ocmock/OCMock/OCMExpectationRecorder.h
@@ -0,0 +1,23 @@ +/* + * Copyright (c) 2004-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#import <OCMock/OCMock.h> + +@interface OCMExpectationRecorder : OCMStubRecorder + +- (id)never; + +@end
diff --git a/third_party/ocmock/OCMock/OCMExpectationRecorder.m b/third_party/ocmock/OCMock/OCMExpectationRecorder.m new file mode 100644 index 0000000..14a742b --- /dev/null +++ b/third_party/ocmock/OCMock/OCMExpectationRecorder.m
@@ -0,0 +1,56 @@ +/* + * Copyright (c) 2004-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#import "OCMExpectationRecorder.h" +#import "OCMInvocationExpectation.h" + +@implementation OCMExpectationRecorder + +#pragma mark Initialisers, description, accessors, etc. + +- (id)init +{ + self = [super init]; + [invocationMatcher release]; + invocationMatcher = [[OCMInvocationExpectation alloc] init]; + return self; +} + +- (OCMInvocationExpectation *)expectation +{ + return (OCMInvocationExpectation *)invocationMatcher; +} + + +#pragma mark Modifying the expectation + +- (id)never +{ + [[self expectation] setMatchAndReject:YES]; + return self; +} + + +#pragma mark Finishing recording + +- (void)forwardInvocation:(NSInvocation *)anInvocation +{ + [super forwardInvocation:anInvocation]; + [mockObject addExpectation:[self expectation]]; +} + + +@end
diff --git a/third_party/ocmock/OCMock/OCMFunctions.h b/third_party/ocmock/OCMock/OCMFunctions.h new file mode 100644 index 0000000..ea4eb452 --- /dev/null +++ b/third_party/ocmock/OCMock/OCMFunctions.h
@@ -0,0 +1,51 @@ +/* + * Copyright (c) 2014-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#import <Foundation/Foundation.h> + +@class OCMLocation; +@class OCClassMockObject; +@class OCPartialMockObject; + + +#if defined(__cplusplus) +extern "C" { +#endif + +BOOL OCMIsObjectType(const char *objCType); +const char *OCMTypeWithoutQualifiers(const char *objCType); +BOOL OCMEqualTypesAllowingOpaqueStructs(const char *type1, const char *type2); + +Class OCMCreateSubclass(Class kt, void *ref); + +void OCMSetIsa(id object, Class kt); +Class OCMGetIsa(id object); + +BOOL OCMIsAliasSelector(SEL selector); +SEL OCMAliasForOriginalSelector(SEL selector); +SEL OCMOriginalSelectorForAlias(SEL selector); + +void OCMSetAssociatedMockForClass(OCClassMockObject *mock, Class aClass); +OCClassMockObject *OCMGetAssociatedMockForClass(Class aClass, BOOL includeSuperclasses); + +void OCMSetAssociatedMockForObject(OCClassMockObject *mock, id anObject); +OCPartialMockObject *OCMGetAssociatedMockForObject(id anObject); + +void OCMReportFailure(OCMLocation *loc, NSString *description); + +#if defined(__cplusplus) +} +#endif
diff --git a/third_party/ocmock/OCMock/OCMFunctions.m b/third_party/ocmock/OCMock/OCMFunctions.m new file mode 100644 index 0000000..e1ec9fa --- /dev/null +++ b/third_party/ocmock/OCMock/OCMFunctions.m
@@ -0,0 +1,295 @@ +/* + * Copyright (c) 2014-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#import <objc/runtime.h> +#import "OCMFunctions.h" +#import "OCMLocation.h" +#import "OCClassMockObject.h" +#import "OCPartialMockObject.h" + + +#pragma mark Known private API + +@interface NSException(OCMKnownExceptionMethods) ++ (NSException *)failureInFile:(NSString *)file atLine:(int)line withDescription:(NSString *)formatString, ...; +@end + +@interface NSObject(OCMKnownTestCaseMethods) +- (void)recordFailureWithDescription:(NSString *)description inFile:(NSString *)file atLine:(NSUInteger)line expected:(BOOL)expected; +- (void)failWithException:(NSException *)exception; +@end + + +#pragma mark Functions related to ObjC type system + +BOOL OCMIsObjectType(const char *objCType) +{ + objCType = OCMTypeWithoutQualifiers(objCType); + + if(strcmp(objCType, @encode(id)) == 0 || strcmp(objCType, @encode(Class)) == 0) + return YES; + + // if the returnType is a typedef to an object, it has the form ^{OriginClass=#} + NSString *regexString = @"^\\^\\{(.*)=#.*\\}"; + NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:regexString options:0 error:NULL]; + NSString *type = [NSString stringWithCString:objCType encoding:NSASCIIStringEncoding]; + if([regex numberOfMatchesInString:type options:0 range:NSMakeRange(0, type.length)] > 0) + return YES; + + // if the return type is a block we treat it like an object + // TODO: if the runtime were to encode the block's argument and/or return types, this test would not be sufficient + if(strcmp(objCType, @encode(void(^)())) == 0) + return YES; + + return NO; +} + + +const char *OCMTypeWithoutQualifiers(const char *objCType) +{ + while(strchr("rnNoORV", objCType[0]) != NULL) + objCType += 1; + return objCType; +} + + +/* + * Sometimes an external type is an opaque struct (which will have an @encode of "{structName}" + * or "{structName=}") but the actual method return type, or property type, will know the contents + * of the struct (so will have an objcType of say "{structName=iiSS}". This function will determine + * those are equal provided they have the same structure name, otherwise everything else will be + * compared textually. This can happen particularly for pointers to such structures, which still + * encode what is being pointed to. + * + * For some types some runtime functions throw exceptions, which is why we wrap this in an + * exception handler just below. + */ +static BOOL OCMEqualTypesAllowingOpaqueStructsInternal(const char *type1, const char *type2) +{ + type1 = OCMTypeWithoutQualifiers(type1); + type2 = OCMTypeWithoutQualifiers(type2); + + switch (type1[0]) + { + case '{': + case '(': + { + if (type2[0] != type1[0]) + return NO; + char endChar = type1[0] == '{'? '}' : ')'; + + const char *type1End = strchr(type1, endChar); + const char *type2End = strchr(type2, endChar); + const char *type1Equals = strchr(type1, '='); + const char *type2Equals = strchr(type2, '='); + + /* Opaque types either don't have an equals sign (just the name and the end brace), or + * empty content after the equals sign. + * We want that to compare the same as a type of the same name but with the content. + */ + BOOL type1Opaque = (type1Equals == NULL || (type1End < type1Equals) || type1Equals[1] == endChar); + BOOL type2Opaque = (type2Equals == NULL || (type2End < type2Equals) || type2Equals[1] == endChar); + const char *type1NameEnd = (type1Equals == NULL || (type1End < type1Equals)) ? type1End : type1Equals; + const char *type2NameEnd = (type2Equals == NULL || (type2End < type2Equals)) ? type2End : type2Equals; + intptr_t type1NameLen = type1NameEnd - type1; + intptr_t type2NameLen = type2NameEnd - type2; + + /* If the names are not equal, return NO */ + if (type1NameLen != type2NameLen || strncmp(type1, type2, type1NameLen)) + return NO; + + /* If the same name, and at least one is opaque, that is close enough. */ + if (type1Opaque || type2Opaque) + return YES; + + /* Otherwise, compare all the elements. Use NSGetSizeAndAlignment to walk through the struct elements. */ + type1 = type1Equals + 1; + type2 = type2Equals + 1; + while (type1[0] != endChar && type1[0] != '\0') + { + if (!OCMEqualTypesAllowingOpaqueStructs(type1, type2)) + return NO; + type1 = NSGetSizeAndAlignment(type1, NULL, NULL); + type2 = NSGetSizeAndAlignment(type2, NULL, NULL); + } + return YES; + } + case '^': + /* for a pointer, make sure the other is a pointer, then recursively compare the rest */ + if (type2[0] != type1[0]) + return NO; + return OCMEqualTypesAllowingOpaqueStructs(type1 + 1, type2 + 1); + + case '?': + return type2[0] == '?'; + + case '\0': + return type2[0] == '\0'; + + default: + { + // Move the type pointers past the current types, then compare that region + const char *afterType1 = NSGetSizeAndAlignment(type1, NULL, NULL); + const char *afterType2 = NSGetSizeAndAlignment(type2, NULL, NULL); + intptr_t type1Len = afterType1 - type1; + intptr_t type2Len = afterType2 - type2; + + return (type1Len == type2Len && (strncmp(type1, type2, type1Len) == 0)); + } + } +} + +BOOL OCMEqualTypesAllowingOpaqueStructs(const char *type1, const char *type2) +{ + @try + { + return OCMEqualTypesAllowingOpaqueStructsInternal(type1, type2); + } + @catch (NSException *e) + { + /* Probably a bitfield or something that NSGetSizeAndAlignment chokes on, oh well */ + return NO; + } +} + + +#pragma mark Creating classes + +Class OCMCreateSubclass(Class class, void *ref) +{ + const char *className = [[NSString stringWithFormat:@"%@-%p-%u", NSStringFromClass(class), ref, arc4random()] UTF8String]; + Class subclass = objc_allocateClassPair(class, className, 0); + objc_registerClassPair(subclass); + return subclass; +} + + +#pragma mark Directly manipulating the isa pointer (look away) + +void OCMSetIsa(id object, Class class) +{ + *((Class *)object) = class; +} + +Class OCMGetIsa(id object) +{ + return *((Class *)object); +} + + +#pragma mark Alias for renaming real methods + +static NSString *const OCMRealMethodAliasPrefix = @"ocmock_replaced_"; +static const char *const OCMRealMethodAliasPrefixCString = "ocmock_replaced_"; + +BOOL OCMIsAliasSelector(SEL selector) +{ + return [NSStringFromSelector(selector) hasPrefix:OCMRealMethodAliasPrefix]; +} + +SEL OCMAliasForOriginalSelector(SEL selector) +{ + char aliasName[2048]; + const char *originalName = sel_getName(selector); + strlcpy(aliasName, OCMRealMethodAliasPrefixCString, sizeof(aliasName)); + strlcat(aliasName, originalName, sizeof(aliasName)); + return sel_registerName(aliasName); +} + +SEL OCMOriginalSelectorForAlias(SEL selector) +{ + if(!OCMIsAliasSelector(selector)) + [NSException raise:NSInvalidArgumentException format:@"Not an alias selector; found %@", NSStringFromSelector(selector)]; + NSString *string = NSStringFromSelector(selector); + return NSSelectorFromString([string substringFromIndex:[OCMRealMethodAliasPrefix length]]); +} + + +#pragma mark Wrappers around associative references + +static NSString *const OCMClassMethodMockObjectKey = @"OCMClassMethodMockObjectKey"; + +void OCMSetAssociatedMockForClass(OCClassMockObject *mock, Class aClass) +{ + if((mock != nil) && (objc_getAssociatedObject(aClass, OCMClassMethodMockObjectKey) != nil)) + [NSException raise:NSInternalInconsistencyException format:@"Another mock is already associated with class %@", NSStringFromClass(aClass)]; + objc_setAssociatedObject(aClass, OCMClassMethodMockObjectKey, mock, OBJC_ASSOCIATION_ASSIGN); +} + +OCClassMockObject *OCMGetAssociatedMockForClass(Class aClass, BOOL includeSuperclasses) +{ + OCClassMockObject *mock = nil; + do + { + mock = objc_getAssociatedObject(aClass, OCMClassMethodMockObjectKey); + aClass = class_getSuperclass(aClass); + } + while((mock == nil) && (aClass != nil) && includeSuperclasses); + return mock; +} + +static NSString *const OCMPartialMockObjectKey = @"OCMPartialMockObjectKey"; + +void OCMSetAssociatedMockForObject(OCClassMockObject *mock, id anObject) +{ + if((mock != nil) && (objc_getAssociatedObject(anObject, OCMPartialMockObjectKey) != nil)) + [NSException raise:NSInternalInconsistencyException format:@"Another mock is already associated with object %@", anObject]; + objc_setAssociatedObject(anObject, OCMPartialMockObjectKey, mock, OBJC_ASSOCIATION_ASSIGN); +} + +OCPartialMockObject *OCMGetAssociatedMockForObject(id anObject) +{ + return objc_getAssociatedObject(anObject, OCMPartialMockObjectKey); +} + + +#pragma mark Functions related to IDE error reporting + +void OCMReportFailure(OCMLocation *loc, NSString *description) +{ + id testCase = [loc testCase]; + if((testCase != nil) && [testCase respondsToSelector:@selector(recordFailureWithDescription:inFile:atLine:expected:)]) + { + [testCase recordFailureWithDescription:description inFile:[loc file] atLine:[loc line] expected:NO]; + } + else if((testCase != nil) && [testCase respondsToSelector:@selector(failWithException:)]) + { + NSException *exception = nil; + if([NSException instancesRespondToSelector:@selector(failureInFile:atLine:withDescription:)]) + { + exception = [NSException failureInFile:[loc file] atLine:(int)[loc line] withDescription:description]; + } + else + { + NSString *reason = [NSString stringWithFormat:@"%@:%lu %@", [loc file], (unsigned long)[loc line], description]; + exception = [NSException exceptionWithName:@"OCMockTestFailure" reason:reason userInfo:nil]; + } + [testCase failWithException:exception]; + } + else if(loc != nil) + { + NSLog(@"%@:%lu %@", [loc file], (unsigned long)[loc line], description); + NSString *reason = [NSString stringWithFormat:@"%@:%lu %@", [loc file], (unsigned long)[loc line], description]; + [[NSException exceptionWithName:@"OCMockTestFailure" reason:reason userInfo:nil] raise]; + + } + else + { + NSLog(@"%@", description); + [[NSException exceptionWithName:@"OCMockTestFailure" reason:description userInfo:nil] raise]; + } + +}
diff --git a/third_party/ocmock/OCMock/OCMIndirectReturnValueProvider.h b/third_party/ocmock/OCMock/OCMIndirectReturnValueProvider.h index 4efeacb..a7b1542 100644 --- a/third_party/ocmock/OCMock/OCMIndirectReturnValueProvider.h +++ b/third_party/ocmock/OCMock/OCMIndirectReturnValueProvider.h
@@ -1,7 +1,18 @@ -//--------------------------------------------------------------------------------------- -// $Id$ -// Copyright (c) 2009 by Mulle Kybernetik. See License file for details. -//--------------------------------------------------------------------------------------- +/* + * Copyright (c) 2009-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ #import <Foundation/Foundation.h>
diff --git a/third_party/ocmock/OCMock/OCMIndirectReturnValueProvider.m b/third_party/ocmock/OCMock/OCMIndirectReturnValueProvider.m index c8b734d7..1f22d99 100644 --- a/third_party/ocmock/OCMock/OCMIndirectReturnValueProvider.m +++ b/third_party/ocmock/OCMock/OCMIndirectReturnValueProvider.m
@@ -1,19 +1,34 @@ -//--------------------------------------------------------------------------------------- -// $Id$ -// Copyright (c) 2009 by Mulle Kybernetik. See License file for details. -//--------------------------------------------------------------------------------------- +/* + * Copyright (c) 2009-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ #import "NSMethodSignature+OCMAdditions.h" #import "OCMIndirectReturnValueProvider.h" +#import "NSInvocation+OCMAdditions.h" @implementation OCMIndirectReturnValueProvider - (id)initWithProvider:(id)aProvider andSelector:(SEL)aSelector { - self = [super init]; - provider = [aProvider retain]; - selector = aSelector; + if ((self = [super init])) + { + provider = [aProvider retain]; + selector = aSelector; + } + return self; } @@ -25,9 +40,15 @@ - (void)handleInvocation:(NSInvocation *)anInvocation { - [anInvocation setTarget:provider]; + id originalTarget = [anInvocation target]; + SEL originalSelector = [anInvocation selector]; + + [anInvocation setTarget:provider]; [anInvocation setSelector:selector]; [anInvocation invoke]; + + [anInvocation setTarget:originalTarget]; + [anInvocation setSelector:originalSelector]; } @end
diff --git a/third_party/ocmock/OCMock/OCMInvocationExpectation.h b/third_party/ocmock/OCMock/OCMInvocationExpectation.h new file mode 100644 index 0000000..a0a0909 --- /dev/null +++ b/third_party/ocmock/OCMock/OCMInvocationExpectation.h
@@ -0,0 +1,30 @@ +/* + * Copyright (c) 2014-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#import "OCMInvocationStub.h" + +@interface OCMInvocationExpectation : OCMInvocationStub +{ + BOOL matchAndReject; + BOOL isSatisfied; +} + +- (void)setMatchAndReject:(BOOL)flag; +- (BOOL)isMatchAndReject; + +- (BOOL)isSatisfied; + +@end
diff --git a/third_party/ocmock/OCMock/OCMInvocationExpectation.m b/third_party/ocmock/OCMock/OCMInvocationExpectation.m new file mode 100644 index 0000000..db572d0 --- /dev/null +++ b/third_party/ocmock/OCMock/OCMInvocationExpectation.m
@@ -0,0 +1,56 @@ +/* + * Copyright (c) 2014-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#import "OCMInvocationExpectation.h" +#import "NSInvocation+OCMAdditions.h" + + +@implementation OCMInvocationExpectation + +- (void)setMatchAndReject:(BOOL)flag +{ + matchAndReject = flag; + if(matchAndReject) + isSatisfied = YES; +} + +- (BOOL)isMatchAndReject +{ + return matchAndReject; +} + +- (BOOL)isSatisfied +{ + return isSatisfied; +} + +- (void)handleInvocation:(NSInvocation *)anInvocation +{ + [super handleInvocation:anInvocation]; + + if(matchAndReject) + { + isSatisfied = NO; + [NSException raise:NSInternalInconsistencyException format:@"%@: explicitly disallowed method invoked: %@", + [self description], [anInvocation invocationDescription]]; + } + else + { + isSatisfied = YES; + } +} + +@end
diff --git a/third_party/ocmock/OCMock/OCMInvocationMatcher.h b/third_party/ocmock/OCMock/OCMInvocationMatcher.h new file mode 100644 index 0000000..bd2758b1 --- /dev/null +++ b/third_party/ocmock/OCMock/OCMInvocationMatcher.h
@@ -0,0 +1,37 @@ +/* + * Copyright (c) 2014-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#import <Foundation/Foundation.h> + +@interface OCMInvocationMatcher : NSObject +{ + NSInvocation *recordedInvocation; + BOOL recordedAsClassMethod; + BOOL ignoreNonObjectArgs; +} + +- (void)setInvocation:(NSInvocation *)anInvocation; +- (NSInvocation *)recordedInvocation; + +- (void)setRecordedAsClassMethod:(BOOL)flag; +- (BOOL)recordedAsClassMethod; + +- (void)setIgnoreNonObjectArgs:(BOOL)flag; + +- (BOOL)matchesSelector:(SEL)aSelector; +- (BOOL)matchesInvocation:(NSInvocation *)anInvocation; + +@end
diff --git a/third_party/ocmock/OCMock/OCMInvocationMatcher.m b/third_party/ocmock/OCMock/OCMInvocationMatcher.m new file mode 100644 index 0000000..55f7374 --- /dev/null +++ b/third_party/ocmock/OCMock/OCMInvocationMatcher.m
@@ -0,0 +1,151 @@ +/* + * Copyright (c) 2014-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#import <objc/runtime.h> +#import <OCMock/OCMArg.h> +#import <OCMock/OCMConstraint.h> +#import "OCMPassByRefSetter.h" +#import "NSInvocation+OCMAdditions.h" +#import "OCMInvocationMatcher.h" +#import "OCClassMockObject.h" +#import "OCMFunctions.h" + + +@interface NSObject(HCMatcherDummy) +- (BOOL)matches:(id)item; +@end + + +@implementation OCMInvocationMatcher + +- (void)dealloc +{ + [recordedInvocation release]; + [super dealloc]; +} + +- (void)setInvocation:(NSInvocation *)anInvocation +{ + [recordedInvocation release]; + // When the method has a char* argument we do not retain the arguments. This makes it possible + // to match char* args literally and with anyPointer. Not retaining the argument means that + // in these cases tests that use their own autorelease pools may fail unexpectedly. + if(![anInvocation hasCharPointerArgument]) + [anInvocation retainArguments]; + recordedInvocation = [anInvocation retain]; +} + +- (void)setRecordedAsClassMethod:(BOOL)flag +{ + recordedAsClassMethod = flag; +} + +- (BOOL)recordedAsClassMethod +{ + return recordedAsClassMethod; +} + +- (void)setIgnoreNonObjectArgs:(BOOL)flag +{ + ignoreNonObjectArgs = flag; +} + +- (NSString *)description +{ + return [recordedInvocation invocationDescription]; +} + +- (NSInvocation *)recordedInvocation +{ + return recordedInvocation; +} + +- (BOOL)matchesSelector:(SEL)sel +{ + if(sel == [recordedInvocation selector]) + return YES; + if(OCMIsAliasSelector(sel) && + OCMOriginalSelectorForAlias(sel) == [recordedInvocation selector]) + return YES; + + return NO; +} + +- (BOOL)matchesInvocation:(NSInvocation *)anInvocation +{ + id target = [anInvocation target]; + BOOL isClassMethodInvocation = (target != nil) && (target == [target class]); + if(isClassMethodInvocation != recordedAsClassMethod) + return NO; + + if(![self matchesSelector:[anInvocation selector]]) + return NO; + + NSMethodSignature *signature = [recordedInvocation methodSignature]; + NSUInteger n = [signature numberOfArguments]; + for(NSUInteger i = 2; i < n; i++) + { + if(ignoreNonObjectArgs && strcmp([signature getArgumentTypeAtIndex:i], @encode(id))) + { + continue; + } + + id recordedArg = [recordedInvocation getArgumentAtIndexAsObject:i]; + id passedArg = [anInvocation getArgumentAtIndexAsObject:i]; + + if([recordedArg isProxy]) + { + if(![recordedArg isEqual:passedArg]) + return NO; + continue; + } + + if([recordedArg isKindOfClass:[NSValue class]]) + recordedArg = [OCMArg resolveSpecialValues:recordedArg]; + + if([recordedArg isKindOfClass:[OCMConstraint class]]) + { + if([recordedArg evaluate:passedArg] == NO) + return NO; + } + else if([recordedArg isKindOfClass:[OCMPassByRefSetter class]]) + { + id valueToSet = [(OCMPassByRefSetter *)recordedArg value]; + // side effect but easier to do here than in handleInvocation + if(![valueToSet isKindOfClass:[NSValue class]]) + *(id *)[passedArg pointerValue] = valueToSet; + else + [(NSValue *)valueToSet getValue:[passedArg pointerValue]]; + } + else if([recordedArg conformsToProtocol:objc_getProtocol("HCMatcher")]) + { + if([recordedArg matches:passedArg] == NO) + return NO; + } + else + { + if(([recordedArg class] == [NSNumber class]) && + ([(NSNumber*)recordedArg compare:(NSNumber*)passedArg] != NSOrderedSame)) + return NO; + if(([recordedArg isEqual:passedArg] == NO) && + !((recordedArg == nil) && (passedArg == nil))) + return NO; + } + } + return YES; +} + +@end
diff --git a/third_party/ocmock/OCMock/OCMInvocationStub.h b/third_party/ocmock/OCMock/OCMInvocationStub.h new file mode 100644 index 0000000..d52467b --- /dev/null +++ b/third_party/ocmock/OCMock/OCMInvocationStub.h
@@ -0,0 +1,29 @@ +/* + * Copyright (c) 2014-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#import "OCMInvocationMatcher.h" + +@interface OCMInvocationStub : OCMInvocationMatcher +{ + NSMutableArray *invocationActions; +} + +- (void)addInvocationAction:(id)anAction; +- (NSArray *)invocationActions; + +- (void)handleInvocation:(NSInvocation *)anInvocation; + +@end
diff --git a/third_party/ocmock/OCMock/OCMInvocationStub.m b/third_party/ocmock/OCMock/OCMInvocationStub.m new file mode 100644 index 0000000..b16e7b9 --- /dev/null +++ b/third_party/ocmock/OCMock/OCMInvocationStub.m
@@ -0,0 +1,53 @@ +/* + * Copyright (c) 2014-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#import "OCMInvocationStub.h" + +@implementation OCMInvocationStub + +- (id)init +{ + self = [super init]; + invocationActions = [[NSMutableArray alloc] init]; + return self; +} + +- (void)dealloc +{ + [invocationActions release]; + [super dealloc]; +} + + +- (void)addInvocationAction:(id)anAction +{ + [invocationActions addObject:anAction]; +} + +- (NSArray *)invocationActions +{ + return invocationActions; +} + + +- (void)handleInvocation:(NSInvocation *)anInvocation +{ +// if(![self matchesInvocation:anInvocation]) +// return NO; + [invocationActions makeObjectsPerformSelector:@selector(handleInvocation:) withObject:anInvocation]; +} + +@end
diff --git a/third_party/ocmock/OCMock/OCMLocation.h b/third_party/ocmock/OCMock/OCMLocation.h new file mode 100644 index 0000000..7a8231e --- /dev/null +++ b/third_party/ocmock/OCMock/OCMLocation.h
@@ -0,0 +1,36 @@ +/* + * Copyright (c) 2014-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#import <Foundation/Foundation.h> + +@interface OCMLocation : NSObject +{ + id testCase; + NSString *file; + NSUInteger line; +} + ++ (instancetype)locationWithTestCase:(id)aTestCase file:(NSString *)aFile line:(NSUInteger)aLine; + +- (instancetype)initWithTestCase:(id)aTestCase file:(NSString *)aFile line:(NSUInteger)aLine; + +- (id)testCase; +- (NSString *)file; +- (NSUInteger)line; + +@end + +extern OCMLocation *OCMMakeLocation(id testCase, const char *file, int line);
diff --git a/third_party/ocmock/OCMock/OCMLocation.m b/third_party/ocmock/OCMock/OCMLocation.m new file mode 100644 index 0000000..dbe59bc2 --- /dev/null +++ b/third_party/ocmock/OCMock/OCMLocation.m
@@ -0,0 +1,66 @@ +/* + * Copyright (c) 2014-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#import "OCMLocation.h" + +@implementation OCMLocation + ++ (instancetype)locationWithTestCase:(id)aTestCase file:(NSString *)aFile line:(NSUInteger)aLine +{ + return [[[OCMLocation alloc] initWithTestCase:aTestCase file:aFile line:aLine] autorelease]; +} + +- (instancetype)initWithTestCase:(id)aTestCase file:(NSString *)aFile line:(NSUInteger)aLine +{ + if ((self = [super init])) + { + testCase = aTestCase; + file = [aFile retain]; + line = aLine; + } + + return self; +} + +- (void)dealloc +{ + [file release]; + [super dealloc]; +} + +- (id)testCase +{ + return testCase; +} + +- (NSString *)file +{ + return file; +} + +- (NSUInteger)line +{ + return line; +} + +@end + + +OCMLocation *OCMMakeLocation(id testCase, const char *fileCString, int line) +{ + return [OCMLocation locationWithTestCase:testCase file:[NSString stringWithUTF8String:fileCString] line:line]; +} +
diff --git a/third_party/ocmock/OCMock/OCMMacroState.h b/third_party/ocmock/OCMock/OCMMacroState.h new file mode 100644 index 0000000..96dc5e2 --- /dev/null +++ b/third_party/ocmock/OCMock/OCMMacroState.h
@@ -0,0 +1,45 @@ +/* + * Copyright (c) 2014-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#import <Foundation/Foundation.h> + +@class OCMLocation; +@class OCMRecorder; +@class OCMStubRecorder; +@class OCMockObject; + + +@interface OCMMacroState : NSObject +{ + OCMRecorder *recorder; +} + ++ (void)beginStubMacro; ++ (OCMStubRecorder *)endStubMacro; + ++ (void)beginExpectMacro; ++ (OCMStubRecorder *)endExpectMacro; + ++ (void)beginVerifyMacroAtLocation:(OCMLocation *)aLocation; ++ (void)endVerifyMacro; + ++ (OCMMacroState *)globalState; + +- (OCMRecorder *)recorder; + +- (void)switchToClassMethod; + +@end
diff --git a/third_party/ocmock/OCMock/OCMMacroState.m b/third_party/ocmock/OCMock/OCMMacroState.m new file mode 100644 index 0000000..ace6755 --- /dev/null +++ b/third_party/ocmock/OCMock/OCMMacroState.m
@@ -0,0 +1,112 @@ +/* + * Copyright (c) 2014-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#import "OCMMacroState.h" +#import "OCMStubRecorder.h" +#import "OCMockObject.h" +#import "OCMExpectationRecorder.h" +#import "OCMVerifier.h" +#import "OCMInvocationMatcher.h" + + +@implementation OCMMacroState + +static OCMMacroState *globalState; + +#pragma mark Methods to begin/end macros + ++ (void)beginStubMacro +{ + OCMStubRecorder *recorder = [[[OCMStubRecorder alloc] init] autorelease]; + globalState = [[[OCMMacroState alloc] initWithRecorder:recorder] autorelease]; +} + ++ (OCMStubRecorder *)endStubMacro +{ + OCMStubRecorder *recorder = (OCMStubRecorder *)[globalState recorder]; + globalState = nil; + return recorder; +} + + ++ (void)beginExpectMacro +{ + OCMExpectationRecorder *recorder = [[[OCMExpectationRecorder alloc] init] autorelease]; + globalState = [[[OCMMacroState alloc] initWithRecorder:recorder] autorelease]; +} + ++ (OCMStubRecorder *)endExpectMacro +{ + return [self endStubMacro]; +} + + ++ (void)beginVerifyMacroAtLocation:(OCMLocation *)aLocation +{ + OCMVerifier *recorder = [[[OCMVerifier alloc] init] autorelease]; + [recorder setLocation:aLocation]; + globalState = [[[OCMMacroState alloc] initWithRecorder:recorder] autorelease]; +} + ++ (void)endVerifyMacro +{ + globalState = nil; +} + + +#pragma mark Accessing global state + ++ (OCMMacroState *)globalState +{ + return globalState; +} + + +#pragma mark Init, dealloc, accessors + +- (id)initWithRecorder:(OCMRecorder *)aRecorder +{ + if ((self = [super init])) + { + recorder = [aRecorder retain]; + } + + return self; +} + +- (void)dealloc +{ + [recorder release]; + if(globalState == self) + globalState = nil; + [super dealloc]; +} + +- (OCMRecorder *)recorder +{ + return recorder; +} + + +#pragma mark Changing the recorder + +- (void)switchToClassMethod +{ + [recorder classMethod]; +} + + +@end
diff --git a/third_party/ocmock/OCMock/OCMNotificationPoster.h b/third_party/ocmock/OCMock/OCMNotificationPoster.h index 817da78..d80d2a3 100644 --- a/third_party/ocmock/OCMock/OCMNotificationPoster.h +++ b/third_party/ocmock/OCMock/OCMNotificationPoster.h
@@ -1,7 +1,18 @@ -//--------------------------------------------------------------------------------------- -// $Id$ -// Copyright (c) 2009 by Mulle Kybernetik. See License file for details. -//--------------------------------------------------------------------------------------- +/* + * Copyright (c) 2009-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ #import <Foundation/Foundation.h>
diff --git a/third_party/ocmock/OCMock/OCMNotificationPoster.m b/third_party/ocmock/OCMock/OCMNotificationPoster.m index 64973a2..764b23b 100644 --- a/third_party/ocmock/OCMock/OCMNotificationPoster.m +++ b/third_party/ocmock/OCMock/OCMNotificationPoster.m
@@ -1,7 +1,18 @@ -//--------------------------------------------------------------------------------------- -// $Id$ -// Copyright (c) 2009 by Mulle Kybernetik. See License file for details. -//--------------------------------------------------------------------------------------- +/* + * Copyright (c) 2009-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ #import "OCMNotificationPoster.h" @@ -10,8 +21,11 @@ - (id)initWithNotification:(id)aNotification { - self = [super init]; - notification = [aNotification retain]; + if ((self = [super init])) + { + notification = [aNotification retain]; + } + return self; }
diff --git a/third_party/ocmock/OCMock/OCMObserverRecorder.h b/third_party/ocmock/OCMock/OCMObserverRecorder.h index 556da3c..04f657b 100644 --- a/third_party/ocmock/OCMock/OCMObserverRecorder.h +++ b/third_party/ocmock/OCMock/OCMObserverRecorder.h
@@ -1,7 +1,18 @@ -//--------------------------------------------------------------------------------------- -// $Id$ -// Copyright (c) 2009 by Mulle Kybernetik. See License file for details. -//--------------------------------------------------------------------------------------- +/* + * Copyright (c) 2009-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ #import <Foundation/Foundation.h>
diff --git a/third_party/ocmock/OCMock/OCMObserverRecorder.m b/third_party/ocmock/OCMock/OCMObserverRecorder.m index e50be50..ce28c0a6 100644 --- a/third_party/ocmock/OCMock/OCMObserverRecorder.m +++ b/third_party/ocmock/OCMock/OCMObserverRecorder.m
@@ -1,7 +1,18 @@ -//--------------------------------------------------------------------------------------- -// $Id$ -// Copyright (c) 2009 by Mulle Kybernetik. See License file for details. -//--------------------------------------------------------------------------------------- +/* + * Copyright (c) 2009-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ #import <objc/runtime.h> #import <OCMock/OCMConstraint.h> @@ -52,23 +63,24 @@ { if([expectedArg isKindOfClass:[OCMConstraint class]]) { - if([expectedArg evaluate:observedArg] == NO) - return NO; + return [expectedArg evaluate:observedArg]; } else if([expectedArg conformsToProtocol:objc_getProtocol("HCMatcher")]) { - if([expectedArg matches:observedArg] == NO) - return NO; + return [expectedArg matches:observedArg]; + } + else if (expectedArg == observedArg) + { + return YES; + } + else if (expectedArg == nil || observedArg == nil) + { + return NO; } else { - if([expectedArg class] != [observedArg class]) - return NO; - if(([expectedArg isEqual:observedArg] == NO) && - !((expectedArg == nil) && (observedArg == nil))) - return NO; + return [expectedArg isEqual:observedArg]; } - return YES; }
diff --git a/third_party/ocmock/OCMock/OCMPassByRefSetter.h b/third_party/ocmock/OCMock/OCMPassByRefSetter.h index 12b99e35..51d3566 100644 --- a/third_party/ocmock/OCMock/OCMPassByRefSetter.h +++ b/third_party/ocmock/OCMock/OCMPassByRefSetter.h
@@ -1,7 +1,18 @@ -//--------------------------------------------------------------------------------------- -// $Id$ -// Copyright (c) 2009 by Mulle Kybernetik. See License file for details. -//--------------------------------------------------------------------------------------- +/* + * Copyright (c) 2009-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ #import <Foundation/Foundation.h>
diff --git a/third_party/ocmock/OCMock/OCMPassByRefSetter.m b/third_party/ocmock/OCMock/OCMPassByRefSetter.m index 919b81a..7d9f7557 100644 --- a/third_party/ocmock/OCMock/OCMPassByRefSetter.m +++ b/third_party/ocmock/OCMock/OCMPassByRefSetter.m
@@ -1,7 +1,18 @@ -//--------------------------------------------------------------------------------------- -// $Id$ -// Copyright (c) 2009 by Mulle Kybernetik. See License file for details. -//--------------------------------------------------------------------------------------- +/* + * Copyright (c) 2009-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ #import "OCMPassByRefSetter.h" @@ -10,8 +21,11 @@ - (id)initWithValue:(id)aValue { - self = [super init]; - value = [aValue retain]; + if ((self = [super init])) + { + value = [aValue retain]; + } + return self; }
diff --git a/third_party/ocmock/OCMock/OCMRealObjectForwarder.h b/third_party/ocmock/OCMock/OCMRealObjectForwarder.h index 9357a422..d09bebb6 100644 --- a/third_party/ocmock/OCMock/OCMRealObjectForwarder.h +++ b/third_party/ocmock/OCMock/OCMRealObjectForwarder.h
@@ -1,7 +1,18 @@ -//--------------------------------------------------------------------------------------- -// $Id$ -// Copyright (c) 2010 by Mulle Kybernetik. See License file for details. -//--------------------------------------------------------------------------------------- +/* + * Copyright (c) 2010-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ #import <Foundation/Foundation.h>
diff --git a/third_party/ocmock/OCMock/OCMRealObjectForwarder.m b/third_party/ocmock/OCMock/OCMRealObjectForwarder.m index 7c0a7db1..7042c7f 100644 --- a/third_party/ocmock/OCMock/OCMRealObjectForwarder.m +++ b/third_party/ocmock/OCMock/OCMRealObjectForwarder.m
@@ -1,11 +1,23 @@ -//--------------------------------------------------------------------------------------- -// $Id$ -// Copyright (c) 2010 by Mulle Kybernetik. See License file for details. -//--------------------------------------------------------------------------------------- +/* + * Copyright (c) 2010-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ #import <objc/runtime.h> #import "OCPartialMockObject.h" #import "OCMRealObjectForwarder.h" +#import "OCMFunctions.h" @implementation OCMRealObjectForwarder @@ -13,15 +25,22 @@ - (void)handleInvocation:(NSInvocation *)anInvocation { id invocationTarget = [anInvocation target]; - SEL invocationSelector = [anInvocation selector]; - SEL aliasedSelector = NSSelectorFromString([OCMRealMethodAliasPrefix stringByAppendingString:NSStringFromSelector(invocationSelector)]); - - [anInvocation setSelector:aliasedSelector]; - if([invocationTarget isProxy] && (class_getInstanceMethod([invocationTarget class], @selector(realObject)))) + + [anInvocation setSelector:OCMAliasForOriginalSelector([anInvocation selector])]; + if ([invocationTarget isProxy]) { - // the method has been invoked on the mock, we need to change the target to the real object - [anInvocation setTarget:[(OCPartialMockObject *)invocationTarget realObject]]; - } + if (class_getInstanceMethod([invocationTarget mockObjectClass], @selector(realObject))) + { + // the method has been invoked on the mock, we need to change the target to the real object + [anInvocation setTarget:[(OCPartialMockObject *)invocationTarget realObject]]; + } + else + { + [NSException raise:NSInternalInconsistencyException + format:@"Method andForwardToRealObject can only be used with partial mocks and class methods."]; + } + } + [anInvocation invoke]; }
diff --git a/third_party/ocmock/OCMock/OCMRecorder.h b/third_party/ocmock/OCMock/OCMRecorder.h new file mode 100644 index 0000000..3496c77c --- /dev/null +++ b/third_party/ocmock/OCMock/OCMRecorder.h
@@ -0,0 +1,39 @@ +/* + * Copyright (c) 2014-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#import <Foundation/Foundation.h> + +@class OCMockObject; +@class OCMInvocationMatcher; + + +@interface OCMRecorder : NSProxy +{ + OCMockObject *mockObject; + OCMInvocationMatcher *invocationMatcher; +} + +- (instancetype)init; +- (instancetype)initWithMockObject:(OCMockObject *)aMockObject; + +- (void)setMockObject:(OCMockObject *)aMockObject; + +- (OCMInvocationMatcher *)invocationMatcher; + +- (id)classMethod; +- (id)ignoringNonObjectArgs; + +@end
diff --git a/third_party/ocmock/OCMock/OCMRecorder.m b/third_party/ocmock/OCMock/OCMRecorder.m new file mode 100644 index 0000000..4f630b68 --- /dev/null +++ b/third_party/ocmock/OCMock/OCMRecorder.m
@@ -0,0 +1,109 @@ +/* + * Copyright (c) 2014-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#import <objc/runtime.h> +#import "OCMRecorder.h" +#import "OCMockObject.h" +#import "OCMInvocationMatcher.h" +#import "OCClassMockObject.h" + +@implementation OCMRecorder + +- (instancetype)init +{ + // no super, we're inheriting from NSProxy + return self; +} + +- (instancetype)initWithMockObject:(OCMockObject *)aMockObject +{ + [self init]; + [self setMockObject:aMockObject]; + return self; +} + +- (void)setMockObject:(OCMockObject *)aMockObject +{ + mockObject = aMockObject; +} + +- (void)dealloc +{ + [invocationMatcher release]; + [super dealloc]; +} + +- (NSString *)description +{ + return [invocationMatcher description]; +} + +- (OCMInvocationMatcher *)invocationMatcher +{ + return invocationMatcher; +} + + +#pragma mark Modifying the matcher + +- (id)classMethod +{ + // should we handle the case where this is called with a mock that isn't a class mock? + [invocationMatcher setRecordedAsClassMethod:YES]; + return self; +} + +- (id)ignoringNonObjectArgs +{ + [invocationMatcher setIgnoreNonObjectArgs:YES]; + return self; +} + + +#pragma mark Recording the actual invocation + +- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector +{ + if([invocationMatcher recordedAsClassMethod]) + return [[(OCClassMockObject *)mockObject mockedClass] methodSignatureForSelector:aSelector]; + + NSMethodSignature *signature = [mockObject methodSignatureForSelector:aSelector]; + if(signature == nil) + { + // if we're a working with a class mock and there is a class method, auto-switch + if(([object_getClass(mockObject) isSubclassOfClass:[OCClassMockObject class]]) && + ([[(OCClassMockObject *)mockObject mockedClass] respondsToSelector:aSelector])) + { + [self classMethod]; + signature = [self methodSignatureForSelector:aSelector]; + } + } + return signature; +} + +- (void)forwardInvocation:(NSInvocation *)anInvocation +{ + [anInvocation setTarget:nil]; + [invocationMatcher setInvocation:anInvocation]; +} + +- (void)doesNotRecognizeSelector:(SEL)aSelector +{ + [NSException raise:NSInvalidArgumentException format:@"%@: cannot stub/expect/verify method '%@' because no such method exists in the mocked class.", mockObject, NSStringFromSelector(aSelector)]; +} + + +@end
diff --git a/third_party/ocmock/OCMock/OCMReturnValueProvider.h b/third_party/ocmock/OCMock/OCMReturnValueProvider.h index 3566c6d..f6690c3a 100644 --- a/third_party/ocmock/OCMock/OCMReturnValueProvider.h +++ b/third_party/ocmock/OCMock/OCMReturnValueProvider.h
@@ -1,7 +1,18 @@ -//--------------------------------------------------------------------------------------- -// $Id$ -// Copyright (c) 2009 by Mulle Kybernetik. See License file for details. -//--------------------------------------------------------------------------------------- +/* + * Copyright (c) 2009-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ #import <Foundation/Foundation.h> @@ -10,7 +21,7 @@ id returnValue; } -- (id)initWithValue:(id)aValue; +- (instancetype)initWithValue:(id)aValue; - (void)handleInvocation:(NSInvocation *)anInvocation;
diff --git a/third_party/ocmock/OCMock/OCMReturnValueProvider.m b/third_party/ocmock/OCMock/OCMReturnValueProvider.m index d98b59c..99e285a 100644 --- a/third_party/ocmock/OCMock/OCMReturnValueProvider.m +++ b/third_party/ocmock/OCMock/OCMReturnValueProvider.m
@@ -1,18 +1,33 @@ -//--------------------------------------------------------------------------------------- -// $Id$ -// Copyright (c) 2009 by Mulle Kybernetik. See License file for details. -//--------------------------------------------------------------------------------------- +/* + * Copyright (c) 2009-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ #import "NSMethodSignature+OCMAdditions.h" #import "OCMReturnValueProvider.h" +#import "OCMFunctions.h" @implementation OCMReturnValueProvider -- (id)initWithValue:(id)aValue +- (instancetype)initWithValue:(id)aValue { - self = [super init]; - returnValue = [aValue retain]; + if ((self = [super init])) + { + returnValue = [aValue retain]; + } + return self; } @@ -24,10 +39,17 @@ - (void)handleInvocation:(NSInvocation *)anInvocation { - const char *returnType = [[anInvocation methodSignature] methodReturnTypeWithoutQualifiers]; - if(strcmp(returnType, @encode(id)) != 0) - @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Expected invocation with object return type. Did you mean to use andReturnValue: instead?" userInfo:nil]; - [anInvocation setReturnValue:&returnValue]; + if(!OCMIsObjectType([[anInvocation methodSignature] methodReturnType])) + { + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Expected invocation with object return type. Did you mean to use andReturnValue: instead?" userInfo:nil]; + } + NSString *sel = NSStringFromSelector([anInvocation selector]); + if([sel hasPrefix:@"alloc"] || [sel hasPrefix:@"new"] || [sel hasPrefix:@"copy"] || [sel hasPrefix:@"mutableCopy"]) + { + // methods that "create" an object return it with an extra retain count + [returnValue retain]; + } + [anInvocation setReturnValue:&returnValue]; } @end
diff --git a/third_party/ocmock/OCMock/OCMStubRecorder.h b/third_party/ocmock/OCMock/OCMStubRecorder.h new file mode 100644 index 0000000..a89a38f --- /dev/null +++ b/third_party/ocmock/OCMock/OCMStubRecorder.h
@@ -0,0 +1,63 @@ +/* + * Copyright (c) 2004-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#import "OCMRecorder.h" +#import "OCMFunctions.h" +#import <objc/runtime.h> + +@interface OCMStubRecorder : OCMRecorder + +- (id)andReturn:(id)anObject; +- (id)andReturnValue:(NSValue *)aValue; +- (id)andThrow:(NSException *)anException; +- (id)andPost:(NSNotification *)aNotification; +- (id)andCall:(SEL)selector onObject:(id)anObject; +- (id)andDo:(void (^)(NSInvocation *invocation))block; +- (id)andForwardToRealObject; + +@end + +@interface OCMStubRecorder (Properties) + +#define andReturn(aValue) _andReturn(({ \ + __typeof__(aValue) _val = (aValue); \ + NSValue *_nsval = [NSValue value:&_val withObjCType:@encode(__typeof__(_val))]; \ + if (OCMIsObjectType(@encode(__typeof(_val)))) { \ + objc_setAssociatedObject(_nsval, "OCMAssociatedBoxedValue", *(__unsafe_unretained id *) (void *) &_val, OBJC_ASSOCIATION_RETAIN); \ + } \ + _nsval; \ +})) +@property (nonatomic, readonly) OCMStubRecorder *(^ _andReturn)(NSValue *); + +#define andThrow(anException) _andThrow(anException) +@property (nonatomic, readonly) OCMStubRecorder *(^ _andThrow)(NSException *); + +#define andPost(aNotification) _andPost(aNotification) +@property (nonatomic, readonly) OCMStubRecorder *(^ _andPost)(NSNotification *); + +#define andCall(anObject, aSelector) _andCall(anObject, aSelector) +@property (nonatomic, readonly) OCMStubRecorder *(^ _andCall)(id, SEL); + +#define andDo(aBlock) _andDo(aBlock) +@property (nonatomic, readonly) OCMStubRecorder *(^ _andDo)(void (^)(NSInvocation *)); + +#define andForwardToRealObject() _andForwardToRealObject() +@property (nonatomic, readonly) OCMStubRecorder *(^ _andForwardToRealObject)(void); + +@end + + +
diff --git a/third_party/ocmock/OCMock/OCMStubRecorder.m b/third_party/ocmock/OCMock/OCMStubRecorder.m new file mode 100644 index 0000000..665fa8f3 --- /dev/null +++ b/third_party/ocmock/OCMock/OCMStubRecorder.m
@@ -0,0 +1,187 @@ +/* + * Copyright (c) 2004-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#import "OCMStubRecorder.h" +#import "OCClassMockObject.h" +#import "OCMReturnValueProvider.h" +#import "OCMBoxedReturnValueProvider.h" +#import "OCMExceptionReturnValueProvider.h" +#import "OCMIndirectReturnValueProvider.h" +#import "OCMNotificationPoster.h" +#import "OCMBlockCaller.h" +#import "OCMRealObjectForwarder.h" +#import "OCMFunctions.h" +#import "OCMInvocationStub.h" + + +@implementation OCMStubRecorder + +#pragma mark Initialisers, description, accessors, etc. + +- (id)init +{ + self = [super init]; + invocationMatcher = [[OCMInvocationStub alloc] init]; + return self; +} + +- (OCMInvocationStub *)stub +{ + return (OCMInvocationStub *)invocationMatcher; +} + + +#pragma mark Recording invocation actions + +- (id)andReturn:(id)anObject +{ + [[self stub] addInvocationAction:[[[OCMReturnValueProvider alloc] initWithValue:anObject] autorelease]]; + return self; +} + +- (id)andReturnValue:(NSValue *)aValue +{ + [[self stub] addInvocationAction:[[[OCMBoxedReturnValueProvider alloc] initWithValue:aValue] autorelease]]; + return self; +} + +- (id)andThrow:(NSException *)anException +{ + [[self stub] addInvocationAction:[[[OCMExceptionReturnValueProvider alloc] initWithValue:anException] autorelease]]; + return self; +} + +- (id)andPost:(NSNotification *)aNotification +{ + [[self stub] addInvocationAction:[[[OCMNotificationPoster alloc] initWithNotification:aNotification] autorelease]]; + return self; +} + +- (id)andCall:(SEL)selector onObject:(id)anObject +{ + [[self stub] addInvocationAction:[[[OCMIndirectReturnValueProvider alloc] initWithProvider:anObject andSelector:selector] autorelease]]; + return self; +} + +- (id)andDo:(void (^)(NSInvocation *))aBlock +{ + [[self stub] addInvocationAction:[[[OCMBlockCaller alloc] initWithCallBlock:aBlock] autorelease]]; + return self; +} + +- (id)andForwardToRealObject +{ + [[self stub] addInvocationAction:[[[OCMRealObjectForwarder alloc] init] autorelease]]; + return self; +} + + +#pragma mark Finishing recording + +- (void)forwardInvocation:(NSInvocation *)anInvocation +{ + [super forwardInvocation:anInvocation]; + [mockObject addStub:[self stub]]; +} + + +@end + + +@implementation OCMStubRecorder (Properties) + +@dynamic _andReturn; + +- (OCMStubRecorder *(^)(NSValue *))_andReturn +{ + id (^theBlock)(id) = ^ (NSValue *aValue) + { + if(OCMIsObjectType([aValue objCType])) + { + NSValue *objValue = nil; + [aValue getValue:&objValue]; + return [self andReturn:objValue]; + } + else + { + return [self andReturnValue:aValue]; + } + }; + return [[theBlock copy] autorelease]; +} + + +@dynamic _andThrow; + +- (OCMStubRecorder *(^)(NSException *))_andThrow +{ + id (^theBlock)(id) = ^ (NSException * anException) + { + return [self andThrow:anException]; + }; + return [[theBlock copy] autorelease]; +} + + +@dynamic _andPost; + +- (OCMStubRecorder *(^)(NSNotification *))_andPost +{ + id (^theBlock)(id) = ^ (NSNotification * aNotification) + { + return [self andPost:aNotification]; + }; + return [[theBlock copy] autorelease]; +} + + +@dynamic _andCall; + +- (OCMStubRecorder *(^)(id, SEL))_andCall +{ + id (^theBlock)(id, SEL) = ^ (id anObject, SEL aSelector) + { + return [self andCall:aSelector onObject:anObject]; + }; + return [[theBlock copy] autorelease]; +} + + +@dynamic _andDo; + +- (OCMStubRecorder *(^)(void (^)(NSInvocation *)))_andDo +{ + id (^theBlock)(void (^)(NSInvocation *)) = ^ (void (^ blockToCall)(NSInvocation *)) + { + return [self andDo:blockToCall]; + }; + return [[theBlock copy] autorelease]; +} + + +@dynamic _andForwardToRealObject; + +- (OCMStubRecorder *(^)(void))_andForwardToRealObject +{ + id (^theBlock)(void) = ^ (void) + { + return [self andForwardToRealObject]; + }; + return [[theBlock copy] autorelease]; +} + + +@end
diff --git a/third_party/ocmock/OCMock/OCMVerifier.h b/third_party/ocmock/OCMock/OCMVerifier.h new file mode 100644 index 0000000..c41a6bd --- /dev/null +++ b/third_party/ocmock/OCMock/OCMVerifier.h
@@ -0,0 +1,25 @@ +/* + * Copyright (c) 2014-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#import "OCMRecorder.h" +#import "OCMLocation.h" + + +@interface OCMVerifier : OCMRecorder + +@property(retain) OCMLocation *location; + +@end
diff --git a/third_party/ocmock/OCMock/OCMVerifier.m b/third_party/ocmock/OCMock/OCMVerifier.m new file mode 100644 index 0000000..011935d --- /dev/null +++ b/third_party/ocmock/OCMock/OCMVerifier.m
@@ -0,0 +1,48 @@ +/* + * Copyright (c) 2014-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#import <objc/runtime.h> +#import "OCMVerifier.h" +#import "OCMockObject.h" +#import "OCMLocation.h" +#import "OCMInvocationMatcher.h" + + +@implementation OCMVerifier + +- (id)init +{ + if ((self = [super init])) + { + invocationMatcher = [[OCMInvocationMatcher alloc] init]; + } + + return self; +} + +- (void)forwardInvocation:(NSInvocation *)anInvocation +{ + [super forwardInvocation:anInvocation]; + [mockObject verifyInvocation:invocationMatcher atLocation:self.location]; +} + +- (void)dealloc +{ + [_location release]; + [super dealloc]; +} + +@end
diff --git a/third_party/ocmock/OCMock/OCMock-Info.plist b/third_party/ocmock/OCMock/OCMock-Info.plist index 4bd402c3..1b55c2e 100644 --- a/third_party/ocmock/OCMock/OCMock-Info.plist +++ b/third_party/ocmock/OCMock/OCMock-Info.plist
@@ -9,7 +9,7 @@ <key>CFBundleIconFile</key> <string></string> <key>CFBundleIdentifier</key> - <string>com.mulle-kybernetik.${PRODUCT_NAME:rfc1034identifier}</string> + <string>com.mulle-kybernetik.$(PRODUCT_NAME:rfc1034identifier)</string> <key>CFBundleInfoDictionaryVersion</key> <string>6.0</string> <key>CFBundleName</key> @@ -23,7 +23,7 @@ <key>CFBundleVersion</key> <string>1</string> <key>NSHumanReadableCopyright</key> - <string>Copyright © 2004-2011 Mulle Kybernetik.</string> + <string>Copyright © 2004-2013 Mulle Kybernetik.</string> <key>NSPrincipalClass</key> <string></string> </dict>
diff --git a/third_party/ocmock/OCMock/OCMock.h b/third_party/ocmock/OCMock/OCMock.h index e18de58..0790594b 100644 --- a/third_party/ocmock/OCMock/OCMock.h +++ b/third_party/ocmock/OCMock/OCMock.h
@@ -1,10 +1,85 @@ -//--------------------------------------------------------------------------------------- -// $Id$ -// Copyright (c) 2004-2008 by Mulle Kybernetik. See License file for details. -//--------------------------------------------------------------------------------------- +/* + * Copyright (c) 2004-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ #import <OCMock/OCMockObject.h> -#import <OCMock/OCMockRecorder.h> +#import <OCMock/OCMRecorder.h> +#import <OCMock/OCMStubRecorder.h> #import <OCMock/OCMConstraint.h> #import <OCMock/OCMArg.h> +#import <OCMock/OCMLocation.h> +#import <OCMock/OCMMacroState.h> #import <OCMock/NSNotificationCenter+OCMAdditions.h> + + +#define OCMClassMock(cls) [OCMockObject niceMockForClass:cls] + +#define OCMStrictClassMock(cls) [OCMockObject mockForClass:cls] + +#define OCMProtocolMock(protocol) [OCMockObject niceMockForProtocol:protocol] + +#define OCMStrictProtocolMock(protocol) [OCMockObject mockForProtocol:protocol] + +#define OCMPartialMock(obj) [OCMockObject partialMockForObject:obj] + +#define OCMObserverMock() [OCMockObject observerMock] + + +#define OCMStub(invocation) \ +({ \ + _OCMSilenceWarnings( \ + [OCMMacroState beginStubMacro]; \ + invocation; \ + [OCMMacroState endStubMacro]; \ + ); \ +}) + +#define OCMExpect(invocation) \ +({ \ + _OCMSilenceWarnings( \ + [OCMMacroState beginExpectMacro]; \ + invocation; \ + [OCMMacroState endExpectMacro]; \ + ); \ +}) + +#define ClassMethod(invocation) \ + _OCMSilenceWarnings( \ + [[OCMMacroState globalState] switchToClassMethod]; \ + invocation; \ + ); + + +#define OCMVerifyAll(mock) [mock verifyAtLocation:OCMMakeLocation(self, __FILE__, __LINE__)] + +#define OCMVerifyAllWithDelay(mock, delay) [mock verifyWithDelay:delay atLocation:OCMMakeLocation(self, __FILE__, __LINE__)] + +#define OCMVerify(invocation) \ +({ \ + _OCMSilenceWarnings( \ + [OCMMacroState beginVerifyMacroAtLocation:OCMMakeLocation(self, __FILE__, __LINE__)]; \ + invocation; \ + [OCMMacroState endVerifyMacro]; \ + ); \ +}) + +#define _OCMSilenceWarnings(macro) \ +({ \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wunused-value\"") \ + _Pragma("clang diagnostic ignored \"-Wunused-getter-return-value\"") \ + macro \ + _Pragma("clang diagnostic pop") \ +})
diff --git a/third_party/ocmock/OCMock/OCMockObject.h b/third_party/ocmock/OCMock/OCMockObject.h index ebd2ba3..58df67d7 100644 --- a/third_party/ocmock/OCMock/OCMockObject.h +++ b/third_party/ocmock/OCMock/OCMockObject.h
@@ -1,18 +1,36 @@ -//--------------------------------------------------------------------------------------- -// $Id$ -// Copyright (c) 2004-2008 by Mulle Kybernetik. See License file for details. -//--------------------------------------------------------------------------------------- +/* + * Copyright (c) 2004-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ #import <Foundation/Foundation.h> +@class OCMLocation; +@class OCMInvocationStub; +@class OCMStubRecorder; +@class OCMInvocationMatcher; +@class OCMInvocationExpectation; + + @interface OCMockObject : NSProxy { BOOL isNice; BOOL expectationOrderMatters; - NSMutableArray *recorders; + NSMutableArray *stubs; NSMutableArray *expectations; - NSMutableArray *rejections; NSMutableArray *exceptions; + NSMutableArray *invocations; } + (id)mockForClass:(Class)aClass; @@ -24,7 +42,7 @@ + (id)observerMock; -- (id)init; +- (instancetype)init; - (void)setExpectationOrderMatters:(BOOL)flag; @@ -32,12 +50,25 @@ - (id)expect; - (id)reject; -- (void)verify; +- (id)verify; +- (id)verifyAtLocation:(OCMLocation *)location; + +- (void)verifyWithDelay:(NSTimeInterval)delay; +- (void)verifyWithDelay:(NSTimeInterval)delay atLocation:(OCMLocation *)location; + +- (void)stopMocking; // internal use only -- (id)getNewRecorder; +- (void)addStub:(OCMInvocationStub *)aStub; +- (void)addExpectation:(OCMInvocationExpectation *)anExpectation; + - (BOOL)handleInvocation:(NSInvocation *)anInvocation; - (void)handleUnRecordedInvocation:(NSInvocation *)anInvocation; +- (BOOL)handleSelector:(SEL)sel; + +- (void)verifyInvocation:(OCMInvocationMatcher *)matcher; +- (void)verifyInvocation:(OCMInvocationMatcher *)matcher atLocation:(OCMLocation *)location; @end +
diff --git a/third_party/ocmock/OCMock/OCMockObject.m b/third_party/ocmock/OCMock/OCMockObject.m index 6f2daf0..9534b3d 100644 --- a/third_party/ocmock/OCMock/OCMockObject.m +++ b/third_party/ocmock/OCMock/OCMockObject.m
@@ -1,22 +1,33 @@ -//--------------------------------------------------------------------------------------- -// $Id$ -// Copyright (c) 2004-2009 by Mulle Kybernetik. See License file for details. -//--------------------------------------------------------------------------------------- +/* + * Copyright (c) 2004-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ #import <OCMock/OCMockObject.h> #import "OCClassMockObject.h" #import "OCProtocolMockObject.h" #import "OCPartialMockObject.h" #import "OCObserverMockObject.h" -#import <OCMock/OCMockRecorder.h> +#import "OCMStubRecorder.h" +#import <OCMock/OCMLocation.h> #import "NSInvocation+OCMAdditions.h" - -@interface OCMockObject(Private) -+ (id)_makeNice:(OCMockObject *)mock; -- (NSString *)_recorderDescriptions:(BOOL)onlyExpectations; -@end - -#pragma mark - +#import "OCMInvocationMatcher.h" +#import "OCMMacroState.h" +#import "OCMFunctions.h" +#import "OCMVerifier.h" +#import "OCMInvocationExpectation.h" +#import "OCMExpectationRecorder.h" @implementation OCMockObject @@ -26,7 +37,7 @@ + (void)initialize { if([[NSInvocation class] instanceMethodSignatureForSelector:@selector(getArgumentAtIndexAsObject:)] == NULL) - [NSException raise:NSInternalInconsistencyException format:@"** Expected method not present; the method getArgumentAtIndexAsObject: is not implemented by NSInvocation. If you see this exception it is likely that you are using the static library version of OCMock and your project is not configured correctly to load categories from static libraries. Did you forget to add the -force_load linker flag?"]; + [NSException raise:NSInternalInconsistencyException format:@"** Expected method not present; the method getArgumentAtIndexAsObject: is not implemented by NSInvocation. If you see this exception it is likely that you are using the static library version of OCMock and your project is not configured correctly to load categories from static libraries. Did you forget to add the -ObjC linker flag?"]; } @@ -72,26 +83,25 @@ } - #pragma mark Initialisers, description, accessors, etc. -- (id)init +- (instancetype)init { // no [super init], we're inheriting from NSProxy expectationOrderMatters = NO; - recorders = [[NSMutableArray alloc] init]; + stubs = [[NSMutableArray alloc] init]; expectations = [[NSMutableArray alloc] init]; - rejections = [[NSMutableArray alloc] init]; exceptions = [[NSMutableArray alloc] init]; - return self; + invocations = [[NSMutableArray alloc] init]; + return self; } - (void)dealloc { - [recorders release]; + [stubs release]; [expectations release]; - [rejections release]; [exceptions release]; + [invocations release]; [super dealloc]; } @@ -100,155 +110,257 @@ return @"OCMockObject"; } +- (void)addStub:(OCMInvocationStub *)aStub +{ + [stubs addObject:aStub]; +} + +- (void)addExpectation:(OCMInvocationExpectation *)anExpectation +{ + [expectations addObject:anExpectation]; +} + + +#pragma mark Public API - (void)setExpectationOrderMatters:(BOOL)flag { expectationOrderMatters = flag; } +- (void)stopMocking +{ + // no-op for mock objects that are not class object or partial mocks +} -#pragma mark Public API - (id)stub { - OCMockRecorder *recorder = [self getNewRecorder]; - [recorders addObject:recorder]; - return recorder; + return [[[OCMStubRecorder alloc] initWithMockObject:self] autorelease]; } - - (id)expect { - OCMockRecorder *recorder = [self stub]; - [expectations addObject:recorder]; - return recorder; + return [[[OCMExpectationRecorder alloc] initWithMockObject:self] autorelease]; } - - (id)reject { - OCMockRecorder *recorder = [self stub]; - [rejections addObject:recorder]; - return recorder; + return [[self expect] never]; } -- (void)verify +- (id)verify { - if([expectations count] == 1) + return [self verifyAtLocation:nil]; +} + +- (id)verifyAtLocation:(OCMLocation *)location +{ + NSMutableArray *unsatisfiedExpectations = [NSMutableArray array]; + for(OCMInvocationExpectation *e in expectations) + { + if(![e isSatisfied]) + [unsatisfiedExpectations addObject:e]; + } + + if([unsatisfiedExpectations count] == 1) { - [NSException raise:NSInternalInconsistencyException format:@"%@: expected method was not invoked: %@", - [self description], [[expectations objectAtIndex:0] description]]; + NSString *description = [NSString stringWithFormat:@"%@: expected method was not invoked: %@", + [self description], [[unsatisfiedExpectations objectAtIndex:0] description]]; + OCMReportFailure(location, description); } - if([expectations count] > 0) + else if([unsatisfiedExpectations count] > 0) { - [NSException raise:NSInternalInconsistencyException format:@"%@ : %ld expected methods were not invoked: %@", - [self description], (unsigned long)[expectations count], [self _recorderDescriptions:YES]]; + NSString *description = [NSString stringWithFormat:@"%@: %@ expected methods were not invoked: %@", + [self description], @([unsatisfiedExpectations count]), [self _stubDescriptions:YES]]; + OCMReportFailure(location, description); } + if([exceptions count] > 0) { - [[exceptions objectAtIndex:0] raise]; + NSString *description = [NSString stringWithFormat:@"%@: %@ (This is a strict mock failure that was ignored when it actually occured.)", + [self description], [[exceptions objectAtIndex:0] description]]; + OCMReportFailure(location, description); } + + return [[[OCMVerifier alloc] initWithMockObject:self] autorelease]; } +- (void)verifyWithDelay:(NSTimeInterval)delay +{ + [self verifyWithDelay:delay atLocation:nil]; +} + +- (void)verifyWithDelay:(NSTimeInterval)delay atLocation:(OCMLocation *)location +{ + NSTimeInterval step = 0.01; + while(delay > 0) + { + if([expectations count] == 0) + break; + [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:step]]; + delay -= step; + step *= 2; + } + [self verifyAtLocation:location]; +} + + +#pragma mark Verify after running + +- (void)verifyInvocation:(OCMInvocationMatcher *)matcher +{ + [self verifyInvocation:matcher atLocation:nil]; +} + +- (void)verifyInvocation:(OCMInvocationMatcher *)matcher atLocation:(OCMLocation *)location +{ + for(NSInvocation *invocation in invocations) + { + if([matcher matchesInvocation:invocation]) + return; + } + NSString *description = [NSString stringWithFormat:@"%@: Method %@ was not invoked.", + [self description], [matcher description]]; + + OCMReportFailure(location, description); +} + #pragma mark Handling invocations +- (id)forwardingTargetForSelector:(SEL)aSelector +{ + if([OCMMacroState globalState] != nil) + { + OCMRecorder *recorder = [[OCMMacroState globalState] recorder]; + [recorder setMockObject:self]; + return recorder; + } + return nil; +} + + +- (BOOL)handleSelector:(SEL)sel +{ + for(OCMInvocationStub *recorder in stubs) + if([recorder matchesSelector:sel]) + return YES; + + return NO; +} + - (void)forwardInvocation:(NSInvocation *)anInvocation { - if([self handleInvocation:anInvocation] == NO) - [self handleUnRecordedInvocation:anInvocation]; + @try + { + if([self handleInvocation:anInvocation] == NO) + [self handleUnRecordedInvocation:anInvocation]; + } + @catch(NSException *e) + { + [exceptions addObject:e]; + [e raise]; + } } - (BOOL)handleInvocation:(NSInvocation *)anInvocation { - OCMockRecorder *recorder = nil; - unsigned int i; - - for(i = 0; i < [recorders count]; i++) - { - recorder = [recorders objectAtIndex:i]; - if([recorder matchesInvocation:anInvocation]) - break; - } - - if(i == [recorders count]) - return NO; - - if([rejections containsObject:recorder]) - { - NSException *exception = [NSException exceptionWithName:NSInternalInconsistencyException reason: - [NSString stringWithFormat:@"%@: explicitly disallowed method invoked: %@", [self description], - [anInvocation invocationDescription]] userInfo:nil]; - [exceptions addObject:exception]; - [exception raise]; - } + [invocations addObject:anInvocation]; - if([expectations containsObject:recorder]) - { - if(expectationOrderMatters && ([expectations objectAtIndex:0] != recorder)) - { - [NSException raise:NSInternalInconsistencyException format:@"%@: unexpected method invoked: %@\n\texpected:\t%@", - [self description], [recorder description], [[expectations objectAtIndex:0] description]]; - - } - [[recorder retain] autorelease]; - [expectations removeObject:recorder]; - [recorders removeObjectAtIndex:i]; - } - [[recorder invocationHandlers] makeObjectsPerformSelector:@selector(handleInvocation:) withObject:anInvocation]; - - return YES; + OCMInvocationStub *stub = nil; + for(stub in stubs) + { + // If the stub forwards its invocation to the real object, then we don't want to do handleInvocation: yet, since forwarding the invocation to the real object could call a method that is expected to happen after this one, which is bad if expectationOrderMatters is YES + if([stub matchesInvocation:anInvocation]) + break; + } + // Retain the stub in case it ends up being removed from stubs and expectations, since we still have to call handleInvocation on the stub at the end + [stub retain]; + if(stub == nil) + return NO; + + if([expectations containsObject:stub]) + { + OCMInvocationExpectation *expectation = [self _nextExptectedInvocation]; + if(expectationOrderMatters && (expectation != stub)) + { + [NSException raise:NSInternalInconsistencyException format:@"%@: unexpected method invoked: %@\n\texpected:\t%@", + [self description], [stub description], [[expectations objectAtIndex:0] description]]; + } + + // We can't check isSatisfied yet, since the stub won't be satisfied until we call handleInvocation:, and we don't want to call handleInvocation: yes for the reason in the comment above, since we'll still have the current expectation in the expectations array, which will cause an exception if expectationOrderMatters is YES and we're not ready for any future expected methods to be called yet + if(![(OCMInvocationExpectation *)stub isMatchAndReject]) + { + [expectations removeObject:stub]; + [stubs removeObject:stub]; + } + } + [stub handleInvocation:anInvocation]; + [stub release]; + + return YES; +} + + +- (OCMInvocationExpectation *)_nextExptectedInvocation +{ + for(OCMInvocationExpectation *expectation in expectations) + if(![expectation isMatchAndReject]) + return expectation; + return nil; } - (void)handleUnRecordedInvocation:(NSInvocation *)anInvocation { if(isNice == NO) { - NSException *exception = [NSException exceptionWithName:NSInternalInconsistencyException reason: - [NSString stringWithFormat:@"%@: unexpected method invoked: %@ %@", [self description], - [anInvocation invocationDescription], [self _recorderDescriptions:NO]] userInfo:nil]; - [exceptions addObject:exception]; - [exception raise]; + [NSException raise:NSInternalInconsistencyException format:@"%@: unexpected method invoked: %@ %@", + [self description], [anInvocation invocationDescription], [self _stubDescriptions:NO]]; } } +- (void)doesNotRecognizeSelector:(SEL)aSelector __unused +{ + if([OCMMacroState globalState] != nil) + { + // we can't do anything clever with the macro state because we must raise an exception here + [NSException raise:NSInvalidArgumentException format:@"%@: Cannot stub/expect/verify method '%@' because no such method exists in the mocked class.", + [self description], NSStringFromSelector(aSelector)]; + } + else + { + [NSException raise:NSInvalidArgumentException format:@"-[%@ %@]: unrecognized selector sent to instance %p", + [self description], NSStringFromSelector(aSelector), (void *)self]; + } +} + #pragma mark Helper methods -- (id)getNewRecorder -{ - return [[[OCMockRecorder alloc] initWithSignatureResolver:self] autorelease]; -} - - -- (NSString *)_recorderDescriptions:(BOOL)onlyExpectations +- (NSString *)_stubDescriptions:(BOOL)onlyExpectations { NSMutableString *outputString = [NSMutableString string]; - - OCMockRecorder *currentObject; - NSEnumerator *recorderEnumerator = [recorders objectEnumerator]; - while((currentObject = [recorderEnumerator nextObject]) != nil) - { - NSString *prefix; + for(OCMStubRecorder *stub in stubs) + { + NSString *prefix = @""; if(onlyExpectations) { - if(![expectations containsObject:currentObject]) + if([expectations containsObject:stub] == NO) continue; - prefix = @" "; } else { - if ([expectations containsObject:currentObject]) - prefix = @"expected: "; + if([expectations containsObject:stub]) + prefix = @"expected:\t"; else - prefix = @"stubbed: "; + prefix = @"stubbed:\t"; } - [outputString appendFormat:@"\n\t%@\t%@", prefix, [currentObject description]]; + [outputString appendFormat:@"\n\t%@%@", prefix, [stub description]]; } - return outputString; }
diff --git a/third_party/ocmock/OCMock/OCMockRecorder.h b/third_party/ocmock/OCMock/OCMockRecorder.h deleted file mode 100644 index b11a253..0000000 --- a/third_party/ocmock/OCMock/OCMockRecorder.h +++ /dev/null
@@ -1,32 +0,0 @@ -//--------------------------------------------------------------------------------------- -// $Id$ -// Copyright (c) 2004-2010 by Mulle Kybernetik. See License file for details. -//--------------------------------------------------------------------------------------- - -#import <Foundation/Foundation.h> - -@interface OCMockRecorder : NSProxy -{ - id signatureResolver; - NSInvocation *recordedInvocation; - NSMutableArray *invocationHandlers; -} - -- (id)initWithSignatureResolver:(id)anObject; - -- (BOOL)matchesInvocation:(NSInvocation *)anInvocation; -- (void)releaseInvocation; - -- (id)andReturn:(id)anObject; -- (id)andReturnValue:(NSValue *)aValue; -- (id)andThrow:(NSException *)anException; -- (id)andPost:(NSNotification *)aNotification; -- (id)andCall:(SEL)selector onObject:(id)anObject; -#if NS_BLOCKS_AVAILABLE -- (id)andDo:(void (^)(NSInvocation *))block; -#endif -- (id)andForwardToRealObject; - -- (NSArray *)invocationHandlers; - -@end
diff --git a/third_party/ocmock/OCMock/OCMockRecorder.m b/third_party/ocmock/OCMock/OCMockRecorder.m deleted file mode 100644 index 5cd63d2..0000000 --- a/third_party/ocmock/OCMock/OCMockRecorder.m +++ /dev/null
@@ -1,187 +0,0 @@ -//--------------------------------------------------------------------------------------- -// $Id$ -// Copyright (c) 2004-2011 by Mulle Kybernetik. See License file for details. -//--------------------------------------------------------------------------------------- - -#import <objc/runtime.h> -#import <OCMock/OCMockRecorder.h> -#import <OCMock/OCMArg.h> -#import <OCMock/OCMConstraint.h> -#import "OCMPassByRefSetter.h" -#import "OCMReturnValueProvider.h" -#import "OCMBoxedReturnValueProvider.h" -#import "OCMExceptionReturnValueProvider.h" -#import "OCMIndirectReturnValueProvider.h" -#import "OCMNotificationPoster.h" -#import "OCMBlockCaller.h" -#import "NSInvocation+OCMAdditions.h" - -@interface NSObject(HCMatcherDummy) -- (BOOL)matches:(id)item; -@end - -#pragma mark - - - -@implementation OCMockRecorder - -#pragma mark Initialisers, description, accessors, etc. - -- (id)initWithSignatureResolver:(id)anObject -{ - signatureResolver = anObject; - invocationHandlers = [[NSMutableArray alloc] init]; - return self; -} - -- (void)dealloc -{ - [recordedInvocation release]; - [invocationHandlers release]; - [super dealloc]; -} - -- (NSString *)description -{ - return [recordedInvocation invocationDescription]; -} - -- (void)releaseInvocation -{ - [recordedInvocation release]; - recordedInvocation = nil; -} - - -#pragma mark Recording invocation handlers - -- (id)andReturn:(id)anObject -{ - [invocationHandlers addObject:[[[OCMReturnValueProvider alloc] initWithValue:anObject] autorelease]]; - return self; -} - -- (id)andReturnValue:(NSValue *)aValue -{ - [invocationHandlers addObject:[[[OCMBoxedReturnValueProvider alloc] initWithValue:aValue] autorelease]]; - return self; -} - -- (id)andThrow:(NSException *)anException -{ - [invocationHandlers addObject:[[[OCMExceptionReturnValueProvider alloc] initWithValue:anException] autorelease]]; - return self; -} - -- (id)andPost:(NSNotification *)aNotification -{ - [invocationHandlers addObject:[[[OCMNotificationPoster alloc] initWithNotification:aNotification] autorelease]]; - return self; -} - -- (id)andCall:(SEL)selector onObject:(id)anObject -{ - [invocationHandlers addObject:[[[OCMIndirectReturnValueProvider alloc] initWithProvider:anObject andSelector:selector] autorelease]]; - return self; -} - -#if NS_BLOCKS_AVAILABLE - -- (id)andDo:(void (^)(NSInvocation *))aBlock -{ - [invocationHandlers addObject:[[[OCMBlockCaller alloc] initWithCallBlock:aBlock] autorelease]]; - return self; -} - -#endif - -- (id)andForwardToRealObject -{ - [NSException raise:NSInternalInconsistencyException format:@"Method %@ can only be used with partial mocks.", - NSStringFromSelector(_cmd)]; - return self; // keep compiler happy -} - - -- (NSArray *)invocationHandlers -{ - return invocationHandlers; -} - - -#pragma mark Recording the actual invocation - -- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector -{ - return [signatureResolver methodSignatureForSelector:aSelector]; -} - -- (void)forwardInvocation:(NSInvocation *)anInvocation -{ - if(recordedInvocation != nil) - [NSException raise:NSInternalInconsistencyException format:@"Recorder received two methods to record."]; - [anInvocation setTarget:nil]; - [anInvocation retainArguments]; - recordedInvocation = [anInvocation retain]; -} - - - -#pragma mark Checking the invocation - -- (BOOL)matchesInvocation:(NSInvocation *)anInvocation -{ - id recordedArg, passedArg; - int i, n; - - if([anInvocation selector] != [recordedInvocation selector]) - return NO; - - n = (int)[[recordedInvocation methodSignature] numberOfArguments]; - for(i = 2; i < n; i++) - { - recordedArg = [recordedInvocation getArgumentAtIndexAsObject:i]; - passedArg = [anInvocation getArgumentAtIndexAsObject:i]; - - if([recordedArg isProxy]) - { - if(![recordedArg isEqual:passedArg]) - return NO; - continue; - } - - if([recordedArg isKindOfClass:[NSValue class]]) - recordedArg = [OCMArg resolveSpecialValues:recordedArg]; - - if([recordedArg isKindOfClass:[OCMConstraint class]]) - { - if([recordedArg evaluate:passedArg] == NO) - return NO; - } - else if([recordedArg isKindOfClass:[OCMPassByRefSetter class]]) - { - // side effect but easier to do here than in handleInvocation - *(id *)[passedArg pointerValue] = [(OCMPassByRefSetter *)recordedArg value]; - } - else if([recordedArg conformsToProtocol:objc_getProtocol("HCMatcher")]) - { - if([recordedArg matches:passedArg] == NO) - return NO; - } - else - { - if(([recordedArg class] == [NSNumber class]) && - ([(NSNumber*)recordedArg compare:(NSNumber*)passedArg] != NSOrderedSame)) - return NO; - if(([recordedArg isEqual:passedArg] == NO) && - !((recordedArg == nil) && (passedArg == nil))) - return NO; - } - } - return YES; -} - - - - -@end
diff --git a/third_party/ocmock/OCMock/OCObserverMockObject.h b/third_party/ocmock/OCMock/OCObserverMockObject.h index 908ad2f3..7b70e3656 100644 --- a/third_party/ocmock/OCMock/OCObserverMockObject.h +++ b/third_party/ocmock/OCMock/OCObserverMockObject.h
@@ -1,14 +1,29 @@ -//--------------------------------------------------------------------------------------- -// $Id$ -// Copyright (c) 2009 by Mulle Kybernetik. See License file for details. -//--------------------------------------------------------------------------------------- +/* + * Copyright (c) 2009-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ #import <Foundation/Foundation.h> +@class OCMLocation; + + @interface OCObserverMockObject : NSObject { - BOOL expectationOrderMatters; - NSMutableArray *recorders; + BOOL expectationOrderMatters; + NSMutableArray *recorders; + NSMutableArray *centers; } - (void)setExpectationOrderMatters:(BOOL)flag; @@ -16,7 +31,13 @@ - (id)expect; - (void)verify; +- (void)verifyAtLocation:(OCMLocation *)location; - (void)handleNotification:(NSNotification *)aNotification; +// internal use + +- (void)autoRemoveFromCenter:(NSNotificationCenter *)aCenter; +- (void)notificationWithName:(NSString *)name object:(id)sender; + @end
diff --git a/third_party/ocmock/OCMock/OCObserverMockObject.m b/third_party/ocmock/OCMock/OCObserverMockObject.m index 74be80da0..c02c8ac 100644 --- a/third_party/ocmock/OCMock/OCObserverMockObject.m +++ b/third_party/ocmock/OCMock/OCObserverMockObject.m
@@ -1,10 +1,23 @@ -//--------------------------------------------------------------------------------------- -// $Id$ -// Copyright (c) 2009 by Mulle Kybernetik. See License file for details. -//--------------------------------------------------------------------------------------- +/* + * Copyright (c) 2009-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ #import "OCObserverMockObject.h" #import "OCMObserverRecorder.h" +#import "OCMLocation.h" +#import "OCMFunctions.h" @implementation OCObserverMockObject @@ -13,13 +26,25 @@ - (id)init { - self = [super init]; - recorders = [[NSMutableArray alloc] init]; + if ((self = [super init])) + { + recorders = [[NSMutableArray alloc] init]; + centers = [[NSMutableArray alloc] init]; + } + return self; } +- (id)retain +{ + return [super retain]; +} + - (void)dealloc { + for(NSNotificationCenter *c in centers) + [c removeObserver:self]; + [centers release]; [recorders release]; [super dealloc]; } @@ -34,6 +59,11 @@ expectationOrderMatters = flag; } +- (void)autoRemoveFromCenter:(NSNotificationCenter *)aCenter +{ + [centers addObject:aCenter]; +} + #pragma mark Public API @@ -46,18 +76,32 @@ - (void)verify { - if([recorders count] == 1) - { - [NSException raise:NSInternalInconsistencyException format:@"%@: expected notification was not observed: %@", - [self description], [[recorders lastObject] description]]; - } - if([recorders count] > 0) - { - [NSException raise:NSInternalInconsistencyException format:@"%@ : %ld expected notifications were not observed.", - [self description], (unsigned long)[recorders count]]; - } + [self verifyAtLocation:nil]; } +- (void)verifyAtLocation:(OCMLocation *)location +{ + if([recorders count] == 1) + { + NSString *description = [NSString stringWithFormat:@"%@: expected notification was not observed: %@", + [self description], [[recorders lastObject] description]]; + OCMReportFailure(location, description); + } + else if([recorders count] > 0) + { + NSString *description = [NSString stringWithFormat:@"%@ : %@ expected notifications were not observed.", + [self description], @([recorders count])]; + OCMReportFailure(location, description); + } +} + + +#pragma mark Receiving recording requests via macro + +- (void)notificationWithName:(NSString *)name object:(id)sender +{ + [[self expect] notificationWithName:name object:sender]; +} #pragma mark Receiving notifications
diff --git a/third_party/ocmock/OCMock/OCPartialMockObject.h b/third_party/ocmock/OCMock/OCPartialMockObject.h index bd549ed..646aa6a 100644 --- a/third_party/ocmock/OCMock/OCPartialMockObject.h +++ b/third_party/ocmock/OCMock/OCPartialMockObject.h
@@ -1,7 +1,18 @@ -//--------------------------------------------------------------------------------------- -// $Id$ -// Copyright (c) 2009 by Mulle Kybernetik. See License file for details. -//--------------------------------------------------------------------------------------- +/* + * Copyright (c) 2009-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ #import "OCClassMockObject.h" @@ -14,12 +25,4 @@ - (NSObject *)realObject; -- (void)stop; - -- (void)setupSubclassForObject:(id)anObject; -- (void)setupForwarderForSelector:(SEL)selector; - @end - - -extern NSString *OCMRealMethodAliasPrefix;
diff --git a/third_party/ocmock/OCMock/OCPartialMockObject.m b/third_party/ocmock/OCMock/OCPartialMockObject.m index fb0b8bd..82f376a 100644 --- a/third_party/ocmock/OCMock/OCPartialMockObject.m +++ b/third_party/ocmock/OCMock/OCPartialMockObject.m
@@ -1,74 +1,52 @@ -//--------------------------------------------------------------------------------------- -// $Id$ -// Copyright (c) 2009 by Mulle Kybernetik. See License file for details. -//--------------------------------------------------------------------------------------- +/* + * Copyright (c) 2009-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ #import <objc/runtime.h> -#import "OCPartialMockRecorder.h" +#import "OCMockObject.h" #import "OCPartialMockObject.h" +#import "NSMethodSignature+OCMAdditions.h" +#import "NSObject+OCMAdditions.h" +#import "OCMFunctions.h" +#import "OCMInvocationStub.h" -@interface OCPartialMockObject (Private) -- (void)forwardInvocationForRealObject:(NSInvocation *)anInvocation; -@end - - -NSString *OCMRealMethodAliasPrefix = @"ocmock_replaced_"; - @implementation OCPartialMockObject - -#pragma mark Mock table - -static NSMutableDictionary *mockTable; - -+ (void)initialize -{ - if(self == [OCPartialMockObject class]) - mockTable = [[NSMutableDictionary alloc] init]; -} - -+ (void)rememberPartialMock:(OCPartialMockObject *)mock forObject:(id)anObject -{ - [mockTable setObject:[NSValue valueWithNonretainedObject:mock] forKey:[NSValue valueWithNonretainedObject:anObject]]; -} - -+ (void)forgetPartialMockForObject:(id)anObject -{ - [mockTable removeObjectForKey:[NSValue valueWithNonretainedObject:anObject]]; -} - -+ (OCPartialMockObject *)existingPartialMockForObject:(id)anObject -{ - OCPartialMockObject *mock = [[mockTable objectForKey:[NSValue valueWithNonretainedObject:anObject]] nonretainedObjectValue]; - if(mock == nil) - [NSException raise:NSInternalInconsistencyException format:@"No partial mock for object %p", anObject]; - return mock; -} - - - #pragma mark Initialisers, description, accessors, etc. - (id)initWithObject:(NSObject *)anObject { + NSParameterAssert(anObject != nil); + [self assertClassIsSupported:[anObject class]]; [super initWithClass:[anObject class]]; realObject = [anObject retain]; - [[self class] rememberPartialMock:self forObject:anObject]; - [self setupSubclassForObject:realObject]; + [self prepareObjectForInstanceMethodMocking]; return self; } - (void)dealloc { - if(realObject != nil) - [self stop]; + [self stopMocking]; + [realObject release]; [super dealloc]; } - (NSString *)description { - return [NSString stringWithFormat:@"OCPartialMockObject[%@]", NSStringFromClass(mockedClass)]; + return [NSString stringWithFormat:@"OCPartialMockObject(%@)", NSStringFromClass(mockedClass)]; } - (NSObject *)realObject @@ -76,61 +54,41 @@ return realObject; } -- (void)stop +#pragma mark Helper methods + +- (void)assertClassIsSupported:(Class)class { - object_setClass(realObject, [self mockedClass]); - [realObject release]; - [[self class] forgetPartialMockForObject:realObject]; - realObject = nil; + NSString *classname = NSStringFromClass(class); + NSString *reason = nil; + if([classname hasPrefix:@"__NSTagged"] || [classname hasPrefix:@"NSTagged"]) + reason = [NSString stringWithFormat:@"OCMock does not support partially mocking tagged classes; got %@", classname]; + else if([classname hasPrefix:@"__NSCF"]) + reason = [NSString stringWithFormat:@"OCMock does not support partially mocking toll-free bridged classes; got %@", classname]; + + if(reason != nil) + [[NSException exceptionWithName:NSInvalidArgumentException reason:reason userInfo:nil] raise]; } -#pragma mark Subclass management +#pragma mark Extending/overriding superclass behaviour -- (void)setupSubclassForObject:(id)anObject +- (void)stopMocking { - Class realClass = [anObject class]; - double timestamp = [NSDate timeIntervalSinceReferenceDate]; - const char *className = [[NSString stringWithFormat:@"%@-%p-%f", realClass, anObject, timestamp] UTF8String]; - Class subclass = objc_allocateClassPair(realClass, className, 0); - objc_registerClassPair(subclass); - object_setClass(anObject, subclass); - - Method forwardInvocationMethod = class_getInstanceMethod([self class], @selector(forwardInvocationForRealObject:)); - IMP forwardInvocationImp = method_getImplementation(forwardInvocationMethod); - const char *forwardInvocationTypes = method_getTypeEncoding(forwardInvocationMethod); - class_addMethod(subclass, @selector(forwardInvocation:), forwardInvocationImp, forwardInvocationTypes); + if(realObject != nil) + { + OCMSetAssociatedMockForObject(nil, realObject); + object_setClass(realObject, [self mockedClass]); + [realObject release]; + realObject = nil; + } + [super stopMocking]; } -- (void)setupForwarderForSelector:(SEL)selector +- (void)addStub:(OCMInvocationStub *)aStub { - Class subclass = [[self realObject] class]; - Method originalMethod = class_getInstanceMethod([subclass superclass], selector); - IMP originalImp = method_getImplementation(originalMethod); - - IMP forwarderImp = [subclass instanceMethodForSelector:@selector(aMethodThatMustNotExist)]; - class_addMethod(subclass, method_getName(originalMethod), forwarderImp, method_getTypeEncoding(originalMethod)); - - SEL aliasSelector = NSSelectorFromString([OCMRealMethodAliasPrefix stringByAppendingString:NSStringFromSelector(selector)]); - class_addMethod(subclass, aliasSelector, originalImp, method_getTypeEncoding(originalMethod)); -} - -- (void)forwardInvocationForRealObject:(NSInvocation *)anInvocation -{ - // in here "self" is a reference to the real object, not the mock - OCPartialMockObject *mock = [OCPartialMockObject existingPartialMockForObject:self]; - if([mock handleInvocation:anInvocation] == NO) - [NSException raise:NSInternalInconsistencyException format:@"Ended up in subclass forwarder for %@ with unstubbed method %@", - [self class], NSStringFromSelector([anInvocation selector])]; -} - - - -#pragma mark Overrides - -- (id)getNewRecorder -{ - return [[[OCPartialMockRecorder alloc] initWithSignatureResolver:self] autorelease]; + [super addStub:aStub]; + if(![aStub recordedAsClassMethod]) + [self setupForwarderForSelector:[[aStub recordedInvocation] selector]]; } - (void)handleUnRecordedInvocation:(NSInvocation *)anInvocation @@ -139,4 +97,122 @@ } +#pragma mark Subclass management + +- (void)prepareObjectForInstanceMethodMocking +{ + OCMSetAssociatedMockForObject(self, realObject); + + /* dynamically create a subclass and set it as the class of the object */ + Class subclass = OCMCreateSubclass(mockedClass, realObject); + object_setClass(realObject, subclass); + + /* point forwardInvocation: of the object to the implementation in the mock */ + Method myForwardMethod = class_getInstanceMethod([self mockObjectClass], @selector(forwardInvocationForRealObject:)); + IMP myForwardIMP = method_getImplementation(myForwardMethod); + class_addMethod(subclass, @selector(forwardInvocation:), myForwardIMP, method_getTypeEncoding(myForwardMethod)); + + /* do the same for forwardingTargetForSelector, remember existing imp with alias selector */ + Method myForwardingTargetMethod = class_getInstanceMethod([self mockObjectClass], @selector(forwardingTargetForSelectorForRealObject:)); + IMP myForwardingTargetIMP = method_getImplementation(myForwardingTargetMethod); + IMP originalForwardingTargetIMP = [mockedClass instanceMethodForSelector:@selector(forwardingTargetForSelector:)]; + class_addMethod(subclass, @selector(forwardingTargetForSelector:), myForwardingTargetIMP, method_getTypeEncoding(myForwardingTargetMethod)); + class_addMethod(subclass, @selector(ocmock_replaced_forwardingTargetForSelector:), originalForwardingTargetIMP, method_getTypeEncoding(myForwardingTargetMethod)); + + /* We also override the -class method to return the original class */ + Method myObjectClassMethod = class_getInstanceMethod([self mockObjectClass], @selector(classForRealObject)); + const char *objectClassTypes = method_getTypeEncoding(myObjectClassMethod); + IMP myObjectClassImp = method_getImplementation(myObjectClassMethod); + class_addMethod(subclass, @selector(class), myObjectClassImp, objectClassTypes); + + /* Adding forwarder for most instance methods to allow for verify after run */ + NSArray *methodBlackList = @[@"class", @"forwardingTargetForSelector:", @"methodSignatureForSelector:", @"forwardInvocation:", + @"allowsWeakReference", @"retainWeakReference", @"isBlock"]; + [NSObject enumerateMethodsInClass:mockedClass usingBlock:^(Class cls, SEL sel) { + if((cls == [NSObject class]) || (cls == [NSProxy class])) + return; + NSString *className = NSStringFromClass(cls); + NSString *selName = NSStringFromSelector(sel); + if(([className hasPrefix:@"NS"] || [className hasPrefix:@"UI"]) && + ([selName hasPrefix:@"_"] || [selName hasSuffix:@"_"])) + return; + if([methodBlackList containsObject:selName]) + return; + @try + { + [self setupForwarderForSelector:sel]; + } + @catch(NSException *e) + { + // ignore for now + } + }]; +} + +- (void)setupForwarderForSelector:(SEL)sel +{ + SEL aliasSelector = OCMAliasForOriginalSelector(sel); + if(class_getInstanceMethod(object_getClass(realObject), aliasSelector) != NULL) + return; + + Method originalMethod = class_getInstanceMethod(mockedClass, sel); + IMP originalIMP = method_getImplementation(originalMethod); + const char *types = method_getTypeEncoding(originalMethod); + /* Might be NULL if the selector is forwarded to another class */ + // TODO: check the fallback implementation is actually sufficient + if(types == NULL) + types = ([[mockedClass instanceMethodSignatureForSelector:sel] fullObjCTypes]); + + Class subclass = object_getClass([self realObject]); + IMP forwarderIMP = [mockedClass instanceMethodForwarderForSelector:sel]; + class_replaceMethod(subclass, sel, forwarderIMP, types); + class_addMethod(subclass, aliasSelector, originalIMP, types); +} + + +// Implementation of the -class method; return the Class that was reported with [realObject class] prior to mocking +- (Class)classForRealObject +{ + // in here "self" is a reference to the real object, not the mock + OCPartialMockObject *mock = OCMGetAssociatedMockForObject(self); + if(mock == nil) + [NSException raise:NSInternalInconsistencyException format:@"No partial mock for object %p", self]; + return [mock mockedClass]; +} + + +- (id)forwardingTargetForSelectorForRealObject:(SEL)sel +{ + // in here "self" is a reference to the real object, not the mock + OCPartialMockObject *mock = OCMGetAssociatedMockForObject(self); + if(mock == nil) + [NSException raise:NSInternalInconsistencyException format:@"No partial mock for object %p", self]; + if([mock handleSelector:sel]) + return self; + + return [self ocmock_replaced_forwardingTargetForSelector:sel]; +} + +// Make the compiler happy in -forwardingTargetForSelectorForRealObject: because it can't find the message… +- (id)ocmock_replaced_forwardingTargetForSelector:(SEL)sel +{ + return nil; +} + + +- (void)forwardInvocationForRealObject:(NSInvocation *)anInvocation +{ + // in here "self" is a reference to the real object, not the mock + OCPartialMockObject *mock = OCMGetAssociatedMockForObject(self); + if(mock == nil) + [NSException raise:NSInternalInconsistencyException format:@"No partial mock for object %p", self]; + + if([mock handleInvocation:anInvocation] == NO) + { + [anInvocation setSelector:OCMAliasForOriginalSelector([anInvocation selector])]; + [anInvocation invoke]; + } +} + + @end
diff --git a/third_party/ocmock/OCMock/OCPartialMockRecorder.h b/third_party/ocmock/OCMock/OCPartialMockRecorder.h deleted file mode 100644 index 95ce4e6..0000000 --- a/third_party/ocmock/OCMock/OCPartialMockRecorder.h +++ /dev/null
@@ -1,12 +0,0 @@ -//--------------------------------------------------------------------------------------- -// $Id$ -// Copyright (c) 2009 by Mulle Kybernetik. See License file for details. -//--------------------------------------------------------------------------------------- - -#import "OCMockRecorder.h" - -@interface OCPartialMockRecorder : OCMockRecorder -{ -} - -@end
diff --git a/third_party/ocmock/OCMock/OCPartialMockRecorder.m b/third_party/ocmock/OCMock/OCPartialMockRecorder.m deleted file mode 100644 index f40cb7c..0000000 --- a/third_party/ocmock/OCMock/OCPartialMockRecorder.m +++ /dev/null
@@ -1,27 +0,0 @@ -//--------------------------------------------------------------------------------------- -// $Id$ -// Copyright (c) 2009-2010 by Mulle Kybernetik. See License file for details. -//--------------------------------------------------------------------------------------- - -#import "OCPartialMockObject.h" -#import "OCMRealObjectForwarder.h" -#import "OCPartialMockRecorder.h" - - -@implementation OCPartialMockRecorder - -- (id)andForwardToRealObject -{ - [invocationHandlers addObject:[[[OCMRealObjectForwarder alloc] init] autorelease]]; - return self; -} - - -- (void)forwardInvocation:(NSInvocation *)anInvocation -{ - [super forwardInvocation:anInvocation]; - // not as clean as I'd wish... - [(OCPartialMockObject *)signatureResolver setupForwarderForSelector:[anInvocation selector]]; -} - -@end
diff --git a/third_party/ocmock/OCMock/OCProtocolMockObject.h b/third_party/ocmock/OCMock/OCProtocolMockObject.h index 88f3229..9a102b3 100644 --- a/third_party/ocmock/OCMock/OCProtocolMockObject.h +++ b/third_party/ocmock/OCMock/OCProtocolMockObject.h
@@ -1,7 +1,18 @@ -//--------------------------------------------------------------------------------------- -// $Id$ -// Copyright (c) 2005-2008 by Mulle Kybernetik. See License file for details. -//--------------------------------------------------------------------------------------- +/* + * Copyright (c) 2005-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ #import <OCMock/OCMockObject.h>
diff --git a/third_party/ocmock/OCMock/OCProtocolMockObject.m b/third_party/ocmock/OCMock/OCProtocolMockObject.m index 19e25e8..82f61679 100644 --- a/third_party/ocmock/OCMock/OCProtocolMockObject.m +++ b/third_party/ocmock/OCMock/OCProtocolMockObject.m
@@ -1,7 +1,18 @@ -//--------------------------------------------------------------------------------------- -// $Id$ -// Copyright (c) 2005-2008 by Mulle Kybernetik. See License file for details. -//--------------------------------------------------------------------------------------- +/* + * Copyright (c) 2005-2015 Erik Doernenburg and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use these files except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ #import <objc/runtime.h> #import "NSMethodSignature+OCMAdditions.h" @@ -13,6 +24,7 @@ - (id)initWithProtocol:(Protocol *)aProtocol { + NSParameterAssert(aProtocol != nil); [super init]; mockedProtocol = aProtocol; return self; @@ -21,23 +33,21 @@ - (NSString *)description { const char* name = protocol_getName(mockedProtocol); - return [NSString stringWithFormat:@"OCMockObject[%s]", name]; + return [NSString stringWithFormat:@"OCMockObject(%s)", name]; } #pragma mark Proxy API - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { - struct objc_method_description methodDescription = protocol_getMethodDescription(mockedProtocol, aSelector, YES, YES); - if(methodDescription.name == NULL) - { - methodDescription = protocol_getMethodDescription(mockedProtocol, aSelector, NO, YES); + struct { BOOL isRequired; BOOL isInstance; } opts[4] = { {YES, YES}, {NO, YES}, {YES, NO}, {NO, NO} }; + for(int i = 0; i < 4; i++) + { + struct objc_method_description methodDescription = protocol_getMethodDescription(mockedProtocol, aSelector, opts[i].isRequired, opts[i].isInstance); + if(methodDescription.name != NULL) + return [NSMethodSignature signatureWithObjCTypes:methodDescription.types]; } - if(methodDescription.name == NULL) - { - return nil; - } - return [NSMethodSignature signatureWithObjCTypes:methodDescription.types]; + return nil; } - (BOOL)conformsToProtocol:(Protocol *)aProtocol
diff --git a/third_party/ocmock/README.chromium b/third_party/ocmock/README.chromium index a31f4a7..4b609000 100644 --- a/third_party/ocmock/README.chromium +++ b/third_party/ocmock/README.chromium
@@ -1,8 +1,8 @@ Name: ocmock URL: https://github.com/erikdoe/ocmock -Version: unknown -Revision: 8b8793cf29a5bac0541421ea06c7dff071afb25e -License: BSD with advertising clause +Version: 3.1.5 +Revision: f03b3cc126edc8d6a2d4466d227fb41a1b2c2a14 +License: Apache Version 2.0 License File: NOT_SHIPPED Security Critical: no @@ -17,22 +17,15 @@ The License.txt and Changes.txt file are also present from /Source. Chromium can no longer sync to the tip-of-tree because upstream OCMock requires -10.7 as of b5773084d56d2573caf6a2d98d5b56e301673de1. +OS X 10.11. Chromium adds gtest_support.h/.mm and ocmock_extensions.h/.mm. -Chromium alters all NSLogging of -[NSArray count] to compile under both 32 and -64 bits. +Chromium patches in 3e193f3c2d4ea4ada63df54c8ce98e7ea4cf768f to use OCMock with +libc++'s string for return types having vtables. -Chromium also patches in e8a9cc97936bfa8be97706c5092110603745e708 for fixing -unit tests broken with Xcode 5 due to the lack of copyWithZone: selector. - -Chromium also patches in 77400c22b5bafd8aaaa75f90a1d997838204577a that fixes -the comparison of types between invocation return type description (which -changed from "c" to "B" on arm64) and NSValue objCType (which didn't change) -on arm64 for BOOL values. - -Chromium also patches in 65ee89744bc1fbb9461f26d83e08243068cb212b that fixes -the comparison between opaque (forward-declared) and non-opaque types. - -Chromium adds the patch suggested in https://github.com/erikdoe/ocmock/issues/247 +Chromium patches in c32abcaba428921d8ac12ac98b272ecf8241f9bb and +33aeea46b0912d9f57ff0ee2763f2667ac099acc to allow the use of .andReturn(...) +because our version of clang does not support __builtin_types_compatible_p. +As we are building with Objective-C++, we need to add 'extern "C"' in +OCMFunctions.h to have C linkage.
diff --git a/third_party/ocmock/ocmock_extensions.h b/third_party/ocmock/ocmock_extensions.h index d817cbdd..bc52a140 100644 --- a/third_party/ocmock/ocmock_extensions.h +++ b/third_party/ocmock/ocmock_extensions.h
@@ -13,7 +13,7 @@ // Pointers to objects still have to be handled with // - (id)andReturnValue:OCMOCK_VALUE(blah) // to keep the types working correctly. -@interface OCMockRecorder(CrExtensions) +@interface OCMStubRecorder(CrExtensions) - (id)andReturnChar:(char)value; - (id)andReturnUnsignedChar:(unsigned char)value; - (id)andReturnShort:(short)value;
diff --git a/third_party/ocmock/ocmock_extensions.mm b/third_party/ocmock/ocmock_extensions.mm index bcf11b3..1f4c977b 100644 --- a/third_party/ocmock/ocmock_extensions.mm +++ b/third_party/ocmock/ocmock_extensions.mm
@@ -11,7 +11,7 @@ return [self andReturnValue:OCMOCK_VALUE(value)]; \ } -@implementation OCMockRecorder(CrExtensions) +@implementation OCMStubRecorder(CrExtensions) CR_OCMOCK_RETURN_IMPL(Char, char); CR_OCMOCK_RETURN_IMPL(UnsignedChar, unsigned char);