From 685bb88433b7520946e30a1e520d37bfc2288151 Mon Sep 17 00:00:00 2001 From: dsinclair Date: Wed, 20 Apr 2016 07:32:39 -0700 Subject: Standalone GN build. This CL setups up a standalone GN build. You'll need to set gn args similar to: use_goma = true clang_use_chrome_plugins = false pdf_enable_xfa = true pdf_enable_v8 = true pdf_is_standalone = true The third_party/pymock files are needed to make git cl upload work correctly. BUG=pdfium:106 Review URL: https://codereview.chromium.org/1904563002 --- .gitignore | 4 +- .gn | 6 + BUILD.gn | 11 +- DEPS | 65 +- build_gyp/gyp_pdfium | 2 +- build_gyp/standalone.gypi | 2 +- build_overrides/README.md | 28 + build_overrides/build.gni | 9 + build_overrides/v8.gni | 16 + core/fpdfapi/fpdf_parser/cpdf_object_unittest.cpp | 3 +- core/fxcrt/fx_basic_wstring.cpp | 4 + core/fxcrt/include/fx_string.h | 2 +- pdfium.gni | 3 + samples/BUILD.gn | 8 +- testing/multiprocess_func_list.cc | 55 + testing/multiprocess_func_list.h | 72 + testing/test.gni | 232 ++ third_party/BUILD.gn | 20 +- third_party/pymock/LICENSE.txt | 26 + third_party/pymock/OWNERS | 1 + third_party/pymock/README.chromium | 10 + third_party/pymock/mock.py | 2367 +++++++++++++++++++++ 22 files changed, 2919 insertions(+), 27 deletions(-) create mode 100644 .gn create mode 100644 build_overrides/README.md create mode 100644 build_overrides/build.gni create mode 100644 build_overrides/v8.gni create mode 100644 testing/multiprocess_func_list.cc create mode 100644 testing/multiprocess_func_list.h create mode 100644 testing/test.gni create mode 100644 third_party/pymock/LICENSE.txt create mode 100644 third_party/pymock/OWNERS create mode 100644 third_party/pymock/README.chromium create mode 100644 third_party/pymock/mock.py diff --git a/.gitignore b/.gitignore index ae54040af9..2ecaf97ecd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,15 @@ -/build_gyp/gyp +/build /buildtools /out /testing/corpus /testing/gmock /testing/gtest +/third_party/icu /third_party/llvm /third_party/llvm-build /third_party/skia /tools/clang +/tools/gyp /v8 /xcodebuild Makefile diff --git a/.gn b/.gn new file mode 100644 index 0000000000..fbcbf4bb4f --- /dev/null +++ b/.gn @@ -0,0 +1,6 @@ +# Copyright 2016 PDFium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +buildconfig = "//build/config/BUILDCONFIG.gn" +secondary_source = "//build/secondary/" diff --git a/BUILD.gn b/BUILD.gn index c54d57f66a..3ecc9a97e5 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -1695,7 +1695,7 @@ test("pdfium_embeddertests") { "//testing/gmock", "//testing/gtest", ] - include_dirs = [] + include_dirs = [ "testing/gmock/include" ] if (pdf_enable_v8) { sources += [ "fpdfsdk/javascript/public_methods_embeddertest.cpp", @@ -1715,3 +1715,12 @@ test("pdfium_embeddertests") { } configs += [ ":pdfium_config" ] } + +if (pdf_is_standalone) { + source_set("samples") { + testonly = true + deps = [ + "//samples", + ] + } +} diff --git a/DEPS b/DEPS index d846d20906..1e6ec803b0 100644 --- a/DEPS +++ b/DEPS @@ -4,7 +4,8 @@ vars = { 'chromium_git': 'https://chromium.googlesource.com', 'pdfium_git': 'https://pdfium.googlesource.com', - 'buildtools_revision': 'c2f259809d5ede3275df5ea0842f0431990c4f98', + 'build_revision': '4d35c0d78ccf35b1597b9dd4adad52203916a01f', + 'buildtools_revision': '5378d73123b64907773cc5c1bb027b2f765ff00a', 'clang_revision': '9dc1904d214a77f081362c1b848b5f28d2192748', 'cygwin_revision': 'c89e446b273697fadf3a10ff1007a97c0b7de6df', 'gmock_revision': '29763965ab52f24565299976b936d1265cb6a271', @@ -18,8 +19,8 @@ vars = { } deps = { - "build_gyp/gyp": - Var('chromium_git') + "/external/gyp", + "build": + Var('chromium_git') + "/chromium/src/build.git@" + Var('build_revision'), "buildtools": Var('chromium_git') + "/chromium/buildtools.git@" + Var('buildtools_revision'), @@ -33,20 +34,23 @@ deps = { "testing/gtest": Var('chromium_git') + "/external/googletest.git@" + Var('gtest_revision'), + "third_party/icu": + Var('chromium_git') + "/chromium/deps/icu.git@" + Var('icu_revision'), + "third_party/skia": Var('chromium_git') + '/skia.git' + '@' + Var('skia_revision'), "tools/clang": Var('chromium_git') + "/chromium/src/tools/clang@" + Var('clang_revision'), + "tools/gyp": + Var('chromium_git') + "/external/gyp", + "v8": Var('chromium_git') + "/v8/v8.git@" + Var('v8_revision'), "v8/base/trace_event/common": Var('chromium_git') + "/chromium/src/base/trace_event/common.git@" + Var('trace_event_revision'), - - "v8/third_party/icu": - Var('chromium_git') + "/chromium/deps/icu.git@" + Var('icu_revision'), } deps_os = { @@ -64,6 +68,50 @@ include_rules = [ ] hooks = [ + # Pull GN binaries. This needs to be before running GYP below. + { + 'action': [ 'download_from_google_storage', + '--no_resume', + '--platform=win32', + '--no_auth', + '--bucket', 'chromium-gn', + '-s', 'pdfium/buildtools/win/gn.exe.sha1', + ], + }, + { + 'name': 'gn_mac', + 'pattern': '.', + 'action': [ 'download_from_google_storage', + '--no_resume', + '--platform=darwin', + '--no_auth', + '--bucket', 'chromium-gn', + '-s', 'pdfium/buildtools/mac/gn.sha1', + ], + }, + { + 'name': 'gn_linux64', + 'pattern': '.', + 'action': [ 'download_from_google_storage', + '--no_resume', + '--platform=linux*', + '--no_auth', + '--bucket', 'chromium-gn', + '-s', 'pdfium/buildtools/linux64/gn.sha1', + ], + }, + { + # Downloads the current stable linux sysroot to build/linux/ if needed. + # This sysroot updates at about the same rate that the chrome build deps + # change. This script is a no-op except for linux users who are doing + # official chrome builds or cross compiling. + 'name': 'sysroot', + 'pattern': '.', + 'action': ['python', + 'pdfium/build/linux/sysroot_scripts/install-sysroot.py', + '--running-as-hook' + ], + }, { # A change to a .gyp, .gypi, or to GYP itself should run the generator. 'name': 'gyp', @@ -142,6 +190,9 @@ hooks = [ # Pull clang if needed or requested via GYP_DEFINES. 'name': 'clang', 'pattern': '.', - 'action': ['python', 'pdfium/tools/clang/scripts/update.py', '--if-needed'], + 'action': ['python', + 'pdfium/tools/clang/scripts/update.py', + '--if-needed' + ], }, ] diff --git a/build_gyp/gyp_pdfium b/build_gyp/gyp_pdfium index 3dbbbeea75..c65337861e 100755 --- a/build_gyp/gyp_pdfium +++ b/build_gyp/gyp_pdfium @@ -14,7 +14,7 @@ import sys script_dir = os.path.dirname(os.path.realpath(__file__)) pdfium_root = os.path.abspath(os.path.join(script_dir, os.pardir)) -sys.path.insert(0, os.path.join(pdfium_root, 'build_gyp', 'gyp', 'pylib')) +sys.path.insert(0, os.path.join(pdfium_root, 'tools', 'gyp', 'pylib')) import gyp diff --git a/build_gyp/standalone.gypi b/build_gyp/standalone.gypi index 7292947226..4b5b74072a 100644 --- a/build_gyp/standalone.gypi +++ b/build_gyp/standalone.gypi @@ -49,7 +49,7 @@ 'werror%': '-Werror', 'v8_optimized_debug%': 0, 'v8_use_external_startup_data%': 0, - 'icu_gyp_path': '../v8/third_party/icu/icu.gyp', + 'icu_gyp_path': '../third_party/icu/icu.gyp', 'libjpeg_gyp_path': '../third_party/third_party.gyp', 'conditions': [ ['OS == "win"', { diff --git a/build_overrides/README.md b/build_overrides/README.md new file mode 100644 index 0000000000..cc0efbf407 --- /dev/null +++ b/build_overrides/README.md @@ -0,0 +1,28 @@ +# Build overrides in GN + +This directory is used to allow different products to customize settings +for repos which are DEPS'ed in or shared. + +For example: V8 can be built on its own (in a "standalone" configuration), +and it can be built as part of Chromium. V8 defines a top-level +target, //v8:d8 (a simple executable), which will only be built in the +standalone configuration. To indiate itis a standalone configuration, v8 can +create a file, build_overrides/v8.gni, containing a variable, +`build_standalone_d8 = true` and import it (as +import("//build_overrides/v8.gni") from its top-level BUILD.gn file. + +Chromium, on the other hand, does not need to build d8, and so it would +create its own build_overrides/v8.gni file, and in it set +`build_standalone_d8 = false`. + +The two files should define the same set of variables, but the values may +vary as appropriate to suit the the needs of the two different builds. + +The build.gni file provides a way for projects to override defaults for +variables used in //build itself (which we want to be shareable between +projects). + +TODO(crbug.com/588513): Ideally //build_overrides and, in particular, +//build_overrides/build.gni will go away completely in favor of some +mechanism that can re-use other required files like //.gn, so that we don't +have to keep requiring projects to create a bunch of different files to use GN. diff --git a/build_overrides/build.gni b/build_overrides/build.gni new file mode 100644 index 0000000000..fe286e5fcf --- /dev/null +++ b/build_overrides/build.gni @@ -0,0 +1,9 @@ +# Copyright 2016 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. + +# See https://bugs.chromium.org/p/webrtc/issues/detail?id=5453. +# Some WebRTC targets require the 10.7 deployment version of the Mac SDK +# and a 10.11 min SDK, but those targets are only used in non-Chromium +# builds. +mac_sdk_min_build_override = "10.10" diff --git a/build_overrides/v8.gni b/build_overrides/v8.gni new file mode 100644 index 0000000000..2f3c07962f --- /dev/null +++ b/build_overrides/v8.gni @@ -0,0 +1,16 @@ +# Copyright 2015 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. + +if (is_android) { + import("//build/config/android/config.gni") +} + +v8_use_external_startup_data = false + +# V8 extras +# Adding V8 extras files requires API owners review +# Be sure to synchronize with build/common.gypi + +v8_extra_library_files = [] +v8_experimental_extra_library_files = [] diff --git a/core/fpdfapi/fpdf_parser/cpdf_object_unittest.cpp b/core/fpdfapi/fpdf_parser/cpdf_object_unittest.cpp index 62f1357572..d3dc5257a3 100644 --- a/core/fpdfapi/fpdf_parser/cpdf_object_unittest.cpp +++ b/core/fpdfapi/fpdf_parser/cpdf_object_unittest.cpp @@ -204,9 +204,10 @@ TEST_F(PDFObjectsTest, GetUnicodeText) { L"\t\n", L"space", L"", L"", L"abcdefghijklmnopqrstuvwxyz", L""}; // Check for direct objects. - for (size_t i = 0; i < m_DirectObjs.size(); ++i) + for (size_t i = 0; i < m_DirectObjs.size(); ++i) { EXPECT_STREQ(direct_obj_results[i], m_DirectObjs[i]->GetUnicodeText().c_str()); + } // Check indirect references. for (const auto& it : m_RefObjs) diff --git a/core/fxcrt/fx_basic_wstring.cpp b/core/fxcrt/fx_basic_wstring.cpp index 069b9fa42d..1e8f1f3232 100644 --- a/core/fxcrt/fx_basic_wstring.cpp +++ b/core/fxcrt/fx_basic_wstring.cpp @@ -579,6 +579,10 @@ void CFX_WideString::FormatV(const FX_WCHAR* pFormat, va_list argList) { va_end(argListSave); } +const FX_WCHAR* CFX_WideString::c_str() const { + return m_pData ? m_pData->m_String : L""; +} + void CFX_WideString::Format(const FX_WCHAR* pFormat, ...) { va_list argList; va_start(argList, pFormat); diff --git a/core/fxcrt/include/fx_string.h b/core/fxcrt/include/fx_string.h index 10a70d9334..8041522472 100644 --- a/core/fxcrt/include/fx_string.h +++ b/core/fxcrt/include/fx_string.h @@ -496,7 +496,7 @@ class CFX_WideString { // Explicit conversion to C-style wide string. // Note: Any subsequent modification of |this| will invalidate the result. - const FX_WCHAR* c_str() const { return m_pData ? m_pData->m_String : L""; } + const FX_WCHAR* c_str() const; // Explicit conversion to CFX_WideStringC. // Note: Any subsequent modification of |this| will invalidate the result. diff --git a/pdfium.gni b/pdfium.gni index ec71db9e2b..b85ac74d0f 100644 --- a/pdfium.gni +++ b/pdfium.gni @@ -17,4 +17,7 @@ declare_args() { # Build PDFium against skia (experimental) rather than agg. pdf_use_skia = false + + # Build PDFium standalone + pdf_is_standalone = false } diff --git a/samples/BUILD.gn b/samples/BUILD.gn index 5e300158c9..83c510d7c8 100644 --- a/samples/BUILD.gn +++ b/samples/BUILD.gn @@ -39,14 +39,14 @@ executable("pdfium_test") { "pdfium_test.cc", ] deps = [ + "../:pdfium", + "../:test_support", "//build/config/sanitizers:deps", - "//third_party/pdfium:pdfium", - "//third_party/pdfium:test_support", # Regardless of whether the library ships against system freetype, # always link this binary against the bundled one for consistency # of results across platforms. - "//third_party/pdfium/third_party:fx_freetype", + "../third_party:fx_freetype", ] if (!pdf_enable_xfa) { deps += [ "../third_party:fx_lpng" ] @@ -73,8 +73,8 @@ executable("pdfium_diff") { "image_diff_png.h", ] deps = [ + "../:pdfium", "//build/config/sanitizers:deps", - "//third_party/pdfium", ] if (!pdf_enable_xfa) { deps += [ "../third_party:fx_lpng" ] diff --git a/testing/multiprocess_func_list.cc b/testing/multiprocess_func_list.cc new file mode 100644 index 0000000000..a400557445 --- /dev/null +++ b/testing/multiprocess_func_list.cc @@ -0,0 +1,55 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "multiprocess_func_list.h" + +#include + +// Helper functions to maintain mapping of "test name"->test func. +// The information is accessed via a global map. +namespace multi_process_function_list { + +namespace { + +struct ProcessFunctions { + ProcessFunctions() : main(NULL), setup(NULL) {} + ProcessFunctions(TestMainFunctionPtr main, SetupFunctionPtr setup) + : main(main), setup(setup) {} + TestMainFunctionPtr main; + SetupFunctionPtr setup; +}; + +typedef std::map MultiProcessTestMap; + +// Retrieve a reference to the global 'func name' -> func ptr map. +MultiProcessTestMap& GetMultiprocessFuncMap() { + static MultiProcessTestMap test_name_to_func_ptr_map; + return test_name_to_func_ptr_map; +} + +} // namespace + +AppendMultiProcessTest::AppendMultiProcessTest( + std::string test_name, + TestMainFunctionPtr main_func_ptr, + SetupFunctionPtr setup_func_ptr) { + GetMultiprocessFuncMap()[test_name] = + ProcessFunctions(main_func_ptr, setup_func_ptr); +} + +int InvokeChildProcessTest(std::string test_name) { + MultiProcessTestMap& func_lookup_table = GetMultiprocessFuncMap(); + MultiProcessTestMap::iterator it = func_lookup_table.find(test_name); + if (it != func_lookup_table.end()) { + const ProcessFunctions& process_functions = it->second; + if (process_functions.setup) + (*process_functions.setup)(); + if (process_functions.main) + return (*process_functions.main)(); + } + + return -1; +} + +} // namespace multi_process_function_list diff --git a/testing/multiprocess_func_list.h b/testing/multiprocess_func_list.h new file mode 100644 index 0000000000..2ec4d7d975 --- /dev/null +++ b/testing/multiprocess_func_list.h @@ -0,0 +1,72 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TESTING_MULTIPROCESS_FUNC_LIST_H_ +#define TESTING_MULTIPROCESS_FUNC_LIST_H_ + +#include + +// This file provides the plumbing to register functions to be executed +// as the main function of a child process in a multi-process test. +// This complements the MultiProcessTest class which provides facilities +// for launching such tests. +// +// The MULTIPROCESS_TEST_MAIN() macro registers a string -> func_ptr mapping +// by creating a new global instance of the AppendMultiProcessTest() class +// this means that by the time that we reach our main() function the mapping +// is already in place. +// +// Example usage: +// MULTIPROCESS_TEST_MAIN(a_test_func) { +// // Code here runs in a child process. +// return 0; +// } +// +// The prototype of a_test_func is implicitly +// int test_main_func_name(); + +namespace multi_process_function_list { + +// Type for child process main functions. +typedef int (*TestMainFunctionPtr)(); + +// Type for child setup functions. +typedef void (*SetupFunctionPtr)(); + +// Helper class to append a test function to the global mapping. +// Used by the MULTIPROCESS_TEST_MAIN macro. +class AppendMultiProcessTest { + public: + // |main_func_ptr| is the main function that is run in the child process. + // |setup_func_ptr| is a function run when the global mapping is added. + AppendMultiProcessTest(std::string test_name, + TestMainFunctionPtr main_func_ptr, + SetupFunctionPtr setup_func_ptr); +}; + +// Invoke the main function of a test previously registered with +// MULTIPROCESS_TEST_MAIN() +int InvokeChildProcessTest(std::string test_name); + +// This macro creates a global MultiProcessTest::AppendMultiProcessTest object +// whose constructor does the work of adding the global mapping. +#define MULTIPROCESS_TEST_MAIN(test_main) \ + MULTIPROCESS_TEST_MAIN_WITH_SETUP(test_main, NULL) + +// Same as above but lets callers specify a setup method that is run in the +// child process, just before the main function is run. This facilitates +// adding a generic one-time setup function for multiple tests. +#define MULTIPROCESS_TEST_MAIN_WITH_SETUP(test_main, test_setup) \ + int test_main(); \ + namespace { \ + multi_process_function_list::AppendMultiProcessTest \ + AddMultiProcessTest##_##test_main(#test_main, \ + (test_main), \ + (test_setup)); \ + } \ + int test_main() + +} // namespace multi_process_function_list + +#endif // TESTING_MULTIPROCESS_FUNC_LIST_H_ diff --git a/testing/test.gni b/testing/test.gni new file mode 100644 index 0000000000..13c434238a --- /dev/null +++ b/testing/test.gni @@ -0,0 +1,232 @@ +# Copyright 2015 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. + +# ============================================================================== +# TEST SETUP +# ============================================================================== + +# Define a test as an executable (or apk on Android) with the "testonly" flag +# set. +# Variable: +# use_raw_android_executable: Use executable() rather than android_apk(). +template("test") { + if (is_android) { + import("//build/config/android/config.gni") + import("//build/config/android/rules.gni") + + _use_raw_android_executable = defined(invoker.use_raw_android_executable) && + invoker.use_raw_android_executable + + # output_name is used to allow targets with the same name but in different + # packages to still produce unique runner scripts. + _output_name = invoker.target_name + if (defined(invoker.output_name)) { + _output_name = invoker.output_name + } + + if (_use_raw_android_executable) { + _exec_target = "${target_name}__exec" + _dist_target = "${target_name}__dist" + _exec_output = + "$target_out_dir/${invoker.target_name}/${invoker.target_name}" + + executable(_exec_target) { + # Configs will always be defined since we set_defaults in BUILDCONFIG.gn. + configs = [] + data_deps = [] + forward_variables_from(invoker, "*", [ "extra_dist_files" ]) + testonly = true + + # Thanks to the set_defaults() for test(), configs are initialized with + # the default shared_library configs rather than executable configs. + configs -= [ + "//build/config:shared_library_config", + "//build/config/android:hide_native_jni_exports", + ] + configs += [ "//build/config:executable_config" ] + + # Don't output to the root or else conflict with the group() below. + output_name = rebase_path(_exec_output, root_out_dir) + if (is_component_build || is_asan) { + data_deps += [ "//build/android:cpplib_stripped" ] + } + } + + create_native_executable_dist(_dist_target) { + testonly = true + dist_dir = "$root_out_dir/$target_name" + binary = _exec_output + deps = [ + ":$_exec_target", + ] + if (defined(invoker.extra_dist_files)) { + extra_files = invoker.extra_dist_files + } + } + } else { + _library_target = "_${target_name}__library" + _apk_target = "${target_name}_apk" + _apk_specific_vars = [ + "android_manifest", + "enable_multidex", + "use_default_launcher", + "write_asset_list", + ] + shared_library(_library_target) { + # Configs will always be defined since we set_defaults in BUILDCONFIG.gn. + configs = [] # Prevent list overwriting warning. + configs = invoker.configs + testonly = true + + deps = [] + forward_variables_from(invoker, + "*", + _apk_specific_vars + [ + "isolate_file", + "visibility", + ]) + + if (!defined(invoker.use_default_launcher) || + invoker.use_default_launcher) { + deps += [ "//testing/android/native_test:native_test_native_code" ] + } + } + unittest_apk(_apk_target) { + forward_variables_from(invoker, _apk_specific_vars + [ "deps" ]) + unittests_dep = ":$_library_target" + apk_name = invoker.target_name + if (defined(invoker.output_name)) { + apk_name = invoker.output_name + unittests_binary = "lib${apk_name}.so" + install_script_name = "install_${invoker.output_name}" + } + deps += [ ":$_library_target" ] + } + + # Incremental test targets work only for .apks. + _incremental_test_runner_target = + "${_output_name}_incremental__test_runner_script" + test_runner_script(_incremental_test_runner_target) { + forward_variables_from(invoker, [ "isolate_file" ]) + apk_target = ":$_apk_target" + test_name = "${_output_name}_incremental" + test_type = "gtest" + test_suite = _output_name + incremental_install = true + } + group("${target_name}_incremental") { + testonly = true + datadeps = [ + ":$_incremental_test_runner_target", + ] + deps = [ + ":${_apk_target}_incremental", + ] + } + } + + _test_runner_target = "${_output_name}__test_runner_script" + test_runner_script(_test_runner_target) { + forward_variables_from(invoker, [ "isolate_file" ]) + if (_use_raw_android_executable) { + executable_dist_dir = "$root_out_dir/$_dist_target" + } else { + apk_target = ":$_apk_target" + } + test_name = _output_name + test_type = "gtest" + test_suite = _output_name + } + + group(target_name) { + testonly = true + deps = [ + ":$_test_runner_target", + ] + if (_use_raw_android_executable) { + deps += [ ":$_dist_target" ] + } else { + deps += [ ":$_apk_target" ] + } + } + + # TODO(GYP): Delete this after we've converted everything to GN. + # The _run targets exist only for compatibility w/ GYP. + group("${target_name}_apk_run") { + testonly = true + deps = [ + ":${invoker.target_name}", + ] + } + } else if (is_ios) { + import("//build/config/ios/rules.gni") + + _test_target = target_name + _resources_bundle_data = target_name + "_resources_bundle_data" + + bundle_data(_resources_bundle_data) { + visibility = [ + ":${_test_target}", + ":${_test_target}_generate_executable", + ] + sources = [ + "//testing/gtest_ios/Default.png", + ] + outputs = [ + "{{bundle_resources_dir}}/{{source_file_part}}", + ] + } + + app(_test_target) { + # TODO(GYP): Make this configurable and only provide a default + # that can be overridden. + info_plist = "//testing/gtest_ios/unittest-Info.plist" + app_name = target_name + entitlements_path = "//testing/gtest_ios" + code_signing_identity = "" + testonly = true + extra_substitutions = [ "BUNDLE_ID_TEST_NAME=$app_name" ] + + # See above call. + set_sources_assignment_filter([]) + + forward_variables_from(invoker, "*") + + if (!defined(deps)) { + deps = [] + } + deps += [ + ":$_resources_bundle_data", + + # All shared libraries must have the sanitizer deps to properly link in + # asan mode (this target will be empty in other cases). + "//build/config/sanitizers:deps", + ] + } + } else { + executable(target_name) { + deps = [] + forward_variables_from(invoker, "*") + + testonly = true + deps += [ + # All shared libraries must have the sanitizer deps to properly link in + # asan mode (this target will be empty in other cases). + "//build/config/sanitizers:deps", + + # Give tests the default manifest on Windows (a no-op elsewhere). + "//build/win:default_exe_manifest", + ] + } + + # TODO(GYP): Delete this after we've converted everything to GN. + # The _run targets exist only for compatibility with GYP. + group("${target_name}_run") { + testonly = true + deps = [ + ":${invoker.target_name}", + ] + } + } +} diff --git a/third_party/BUILD.gn b/third_party/BUILD.gn index f899bd1481..e0c9bf116e 100644 --- a/third_party/BUILD.gn +++ b/third_party/BUILD.gn @@ -16,7 +16,7 @@ source_set("bigint") { configs -= [ "//build/config/compiler:chromium_code" ] configs += [ "//build/config/compiler:no_chromium_code", - "//third_party/pdfium:pdfium_config", + "../:pdfium_config", ] sources = [ "bigint/BigInteger.cc", @@ -47,7 +47,7 @@ static_library("fx_freetype") { configs -= [ "//build/config/compiler:chromium_code" ] configs += [ "//build/config/compiler:no_chromium_code", - "//third_party/pdfium:pdfium_config", + "../:pdfium_config", # Must be after no_chromium_code for warning flags to be ordered correctly. ":fx_freetype_warnings", @@ -98,7 +98,7 @@ source_set("fx_agg") { configs -= [ "//build/config/compiler:chromium_code" ] configs += [ "//build/config/compiler:no_chromium_code", - "//third_party/pdfium:pdfium_config", + "../:pdfium_config", # Must be after no_chromium_code for warning flags to be ordered correctly. ":fx_agg_warnings", @@ -140,7 +140,7 @@ source_set("fx_lcms2") { configs -= [ "//build/config/compiler:chromium_code" ] configs += [ "//build/config/compiler:no_chromium_code", - "//third_party/pdfium:pdfium_config", + "../:pdfium_config", # Must be after no_chromium_code for warning flags to be ordered correctly. ":fx_lcms2_warnings", @@ -188,7 +188,7 @@ source_set("jpeg") { configs -= [ "//build/config/compiler:chromium_code" ] configs += [ "//build/config/compiler:no_chromium_code", - "//third_party/pdfium:pdfium_config", + "../:pdfium_config", # Must be after no_chromium_code for warning flags to be ordered correctly. ":jpeg_warnings", @@ -266,7 +266,7 @@ source_set("fx_libopenjpeg") { configs -= [ "//build/config/compiler:chromium_code" ] configs += [ "//build/config/compiler:no_chromium_code", - "//third_party/pdfium:pdfium_config", + "../:pdfium_config", # Must be after no_chromium_code for warning flags to be ordered correctly. ":fx_libopenjpeg_warnings", @@ -298,7 +298,7 @@ source_set("fx_lpng") { configs -= [ "//build/config/compiler:chromium_code" ] configs += [ "//build/config/compiler:no_chromium_code", - "//third_party/pdfium:pdfium_config", + "../:pdfium_config", ] sources = [ "libpng16/png.c", @@ -332,7 +332,7 @@ if (pdf_enable_xfa) { configs -= [ "//build/config/compiler:chromium_code" ] configs += [ "//build/config/compiler:no_chromium_code", - "//third_party/pdfium:pdfium_config", + "../:pdfium_config", ] deps = [ "//third_party:jpeg", @@ -391,7 +391,7 @@ source_set("fx_zlib") { configs -= [ "//build/config/compiler:chromium_code" ] configs += [ "//build/config/compiler:no_chromium_code", - "//third_party/pdfium:pdfium_config", + "../:pdfium_config", # Must be after no_chromium_code for warning flags to be ordered correctly. ":fx_zlib_warnings", @@ -419,7 +419,7 @@ source_set("pdfium_base") { configs -= [ "//build/config/compiler:chromium_code" ] configs += [ "//build/config/compiler:no_chromium_code", - "//third_party/pdfium:pdfium_config", + "../:pdfium_config", ] sources = [ "base/logging.h", diff --git a/third_party/pymock/LICENSE.txt b/third_party/pymock/LICENSE.txt new file mode 100644 index 0000000000..7891703b13 --- /dev/null +++ b/third_party/pymock/LICENSE.txt @@ -0,0 +1,26 @@ +Copyright (c) 2003-2012, Michael Foord +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/third_party/pymock/OWNERS b/third_party/pymock/OWNERS new file mode 100644 index 0000000000..9946560376 --- /dev/null +++ b/third_party/pymock/OWNERS @@ -0,0 +1 @@ +sbc@chromium.org diff --git a/third_party/pymock/README.chromium b/third_party/pymock/README.chromium new file mode 100644 index 0000000000..255c2bfeb8 --- /dev/null +++ b/third_party/pymock/README.chromium @@ -0,0 +1,10 @@ +Name: mock +URL: http://pypi.python.org/pypi/mock +Version: 1.0.1 +Security Critical: no +License: BSD +License File: NOT_SHIPPED +Description: +Python mock library, currently used by native_client_sdk. This is the +same mock library that is now part of python 3.3 where it is know as +unittest.mock. diff --git a/third_party/pymock/mock.py b/third_party/pymock/mock.py new file mode 100644 index 0000000000..c8fc5d1d25 --- /dev/null +++ b/third_party/pymock/mock.py @@ -0,0 +1,2367 @@ +# mock.py +# Test tools for mocking and patching. +# Copyright (C) 2007-2012 Michael Foord & the mock team +# E-mail: fuzzyman AT voidspace DOT org DOT uk + +# mock 1.0 +# http://www.voidspace.org.uk/python/mock/ + +# Released subject to the BSD License +# Please see http://www.voidspace.org.uk/python/license.shtml + +# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml +# Comments, suggestions and bug reports welcome. + + +__all__ = ( + 'Mock', + 'MagicMock', + 'patch', + 'sentinel', + 'DEFAULT', + 'ANY', + 'call', + 'create_autospec', + 'FILTER_DIR', + 'NonCallableMock', + 'NonCallableMagicMock', + 'mock_open', + 'PropertyMock', +) + + +__version__ = '1.0.1' + + +import pprint +import sys + +try: + import inspect +except ImportError: + # for alternative platforms that + # may not have inspect + inspect = None + +try: + from functools import wraps as original_wraps +except ImportError: + # Python 2.4 compatibility + def wraps(original): + def inner(f): + f.__name__ = original.__name__ + f.__doc__ = original.__doc__ + f.__module__ = original.__module__ + f.__wrapped__ = original + return f + return inner +else: + if sys.version_info[:2] >= (3, 3): + wraps = original_wraps + else: + def wraps(func): + def inner(f): + f = original_wraps(func)(f) + f.__wrapped__ = func + return f + return inner + +try: + unicode +except NameError: + # Python 3 + basestring = unicode = str + +try: + long +except NameError: + # Python 3 + long = int + +try: + BaseException +except NameError: + # Python 2.4 compatibility + BaseException = Exception + +try: + next +except NameError: + def next(obj): + return obj.next() + + +BaseExceptions = (BaseException,) +if 'java' in sys.platform: + # jython + import java + BaseExceptions = (BaseException, java.lang.Throwable) + +try: + _isidentifier = str.isidentifier +except AttributeError: + # Python 2.X + import keyword + import re + regex = re.compile(r'^[a-z_][a-z0-9_]*$', re.I) + def _isidentifier(string): + if string in keyword.kwlist: + return False + return regex.match(string) + + +inPy3k = sys.version_info[0] == 3 + +# Needed to work around Python 3 bug where use of "super" interferes with +# defining __class__ as a descriptor +_super = super + +self = 'im_self' +builtin = '__builtin__' +if inPy3k: + self = '__self__' + builtin = 'builtins' + +FILTER_DIR = True + + +def _is_instance_mock(obj): + # can't use isinstance on Mock objects because they override __class__ + # The base class for all mocks is NonCallableMock + return issubclass(type(obj), NonCallableMock) + + +def _is_exception(obj): + return ( + isinstance(obj, BaseExceptions) or + isinstance(obj, ClassTypes) and issubclass(obj, BaseExceptions) + ) + + +class _slotted(object): + __slots__ = ['a'] + + +DescriptorTypes = ( + type(_slotted.a), + property, +) + + +def _getsignature(func, skipfirst, instance=False): + if inspect is None: + raise ImportError('inspect module not available') + + if isinstance(func, ClassTypes) and not instance: + try: + func = func.__init__ + except AttributeError: + return + skipfirst = True + elif not isinstance(func, FunctionTypes): + # for classes where instance is True we end up here too + try: + func = func.__call__ + except AttributeError: + return + + if inPy3k: + try: + argspec = inspect.getfullargspec(func) + except TypeError: + # C function / method, possibly inherited object().__init__ + return + regargs, varargs, varkw, defaults, kwonly, kwonlydef, ann = argspec + else: + try: + regargs, varargs, varkwargs, defaults = inspect.getargspec(func) + except TypeError: + # C function / method, possibly inherited object().__init__ + return + + # instance methods and classmethods need to lose the self argument + if getattr(func, self, None) is not None: + regargs = regargs[1:] + if skipfirst: + # this condition and the above one are never both True - why? + regargs = regargs[1:] + + if inPy3k: + signature = inspect.formatargspec( + regargs, varargs, varkw, defaults, + kwonly, kwonlydef, ann, formatvalue=lambda value: "") + else: + signature = inspect.formatargspec( + regargs, varargs, varkwargs, defaults, + formatvalue=lambda value: "") + return signature[1:-1], func + + +def _check_signature(func, mock, skipfirst, instance=False): + if not _callable(func): + return + + result = _getsignature(func, skipfirst, instance) + if result is None: + return + signature, func = result + + # can't use self because "self" is common as an argument name + # unfortunately even not in the first place + src = "lambda _mock_self, %s: None" % signature + checksig = eval(src, {}) + _copy_func_details(func, checksig) + type(mock)._mock_check_sig = checksig + + +def _copy_func_details(func, funcopy): + funcopy.__name__ = func.__name__ + funcopy.__doc__ = func.__doc__ + #funcopy.__dict__.update(func.__dict__) + funcopy.__module__ = func.__module__ + if not inPy3k: + funcopy.func_defaults = func.func_defaults + return + funcopy.__defaults__ = func.__defaults__ + funcopy.__kwdefaults__ = func.__kwdefaults__ + + +def _callable(obj): + if isinstance(obj, ClassTypes): + return True + if getattr(obj, '__call__', None) is not None: + return True + return False + + +def _is_list(obj): + # checks for list or tuples + # XXXX badly named! + return type(obj) in (list, tuple) + + +def _instance_callable(obj): + """Given an object, return True if the object is callable. + For classes, return True if instances would be callable.""" + if not isinstance(obj, ClassTypes): + # already an instance + return getattr(obj, '__call__', None) is not None + + klass = obj + # uses __bases__ instead of __mro__ so that we work with old style classes + if klass.__dict__.get('__call__') is not None: + return True + + for base in klass.__bases__: + if _instance_callable(base): + return True + return False + + +def _set_signature(mock, original, instance=False): + # creates a function with signature (*args, **kwargs) that delegates to a + # mock. It still does signature checking by calling a lambda with the same + # signature as the original. + if not _callable(original): + return + + skipfirst = isinstance(original, ClassTypes) + result = _getsignature(original, skipfirst, instance) + if result is None: + # was a C function (e.g. object().__init__ ) that can't be mocked + return + + signature, func = result + + src = "lambda %s: None" % signature + checksig = eval(src, {}) + _copy_func_details(func, checksig) + + name = original.__name__ + if not _isidentifier(name): + name = 'funcopy' + context = {'_checksig_': checksig, 'mock': mock} + src = """def %s(*args, **kwargs): + _checksig_(*args, **kwargs) + return mock(*args, **kwargs)""" % name + exec (src, context) + funcopy = context[name] + _setup_func(funcopy, mock) + return funcopy + + +def _setup_func(funcopy, mock): + funcopy.mock = mock + + # can't use isinstance with mocks + if not _is_instance_mock(mock): + return + + def assert_called_with(*args, **kwargs): + return mock.assert_called_with(*args, **kwargs) + def assert_called_once_with(*args, **kwargs): + return mock.assert_called_once_with(*args, **kwargs) + def assert_has_calls(*args, **kwargs): + return mock.assert_has_calls(*args, **kwargs) + def assert_any_call(*args, **kwargs): + return mock.assert_any_call(*args, **kwargs) + def reset_mock(): + funcopy.method_calls = _CallList() + funcopy.mock_calls = _CallList() + mock.reset_mock() + ret = funcopy.return_value + if _is_instance_mock(ret) and not ret is mock: + ret.reset_mock() + + funcopy.called = False + funcopy.call_count = 0 + funcopy.call_args = None + funcopy.call_args_list = _CallList() + funcopy.method_calls = _CallList() + funcopy.mock_calls = _CallList() + + funcopy.return_value = mock.return_value + funcopy.side_effect = mock.side_effect + funcopy._mock_children = mock._mock_children + + funcopy.assert_called_with = assert_called_with + funcopy.assert_called_once_with = assert_called_once_with + funcopy.assert_has_calls = assert_has_calls + funcopy.assert_any_call = assert_any_call + funcopy.reset_mock = reset_mock + + mock._mock_delegate = funcopy + + +def _is_magic(name): + return '__%s__' % name[2:-2] == name + + +class _SentinelObject(object): + "A unique, named, sentinel object." + def __init__(self, name): + self.name = name + + def __repr__(self): + return 'sentinel.%s' % self.name + + +class _Sentinel(object): + """Access attributes to return a named object, usable as a sentinel.""" + def __init__(self): + self._sentinels = {} + + def __getattr__(self, name): + if name == '__bases__': + # Without this help(mock) raises an exception + raise AttributeError + return self._sentinels.setdefault(name, _SentinelObject(name)) + + +sentinel = _Sentinel() + +DEFAULT = sentinel.DEFAULT +_missing = sentinel.MISSING +_deleted = sentinel.DELETED + + +class OldStyleClass: + pass +ClassType = type(OldStyleClass) + + +def _copy(value): + if type(value) in (dict, list, tuple, set): + return type(value)(value) + return value + + +ClassTypes = (type,) +if not inPy3k: + ClassTypes = (type, ClassType) + +_allowed_names = set( + [ + 'return_value', '_mock_return_value', 'side_effect', + '_mock_side_effect', '_mock_parent', '_mock_new_parent', + '_mock_name', '_mock_new_name' + ] +) + + +def _delegating_property(name): + _allowed_names.add(name) + _the_name = '_mock_' + name + def _get(self, name=name, _the_name=_the_name): + sig = self._mock_delegate + if sig is None: + return getattr(self, _the_name) + return getattr(sig, name) + def _set(self, value, name=name, _the_name=_the_name): + sig = self._mock_delegate + if sig is None: + self.__dict__[_the_name] = value + else: + setattr(sig, name, value) + + return property(_get, _set) + + + +class _CallList(list): + + def __contains__(self, value): + if not isinstance(value, list): + return list.__contains__(self, value) + len_value = len(value) + len_self = len(self) + if len_value > len_self: + return False + + for i in range(0, len_self - len_value + 1): + sub_list = self[i:i+len_value] + if sub_list == value: + return True + return False + + def __repr__(self): + return pprint.pformat(list(self)) + + +def _check_and_set_parent(parent, value, name, new_name): + if not _is_instance_mock(value): + return False + if ((value._mock_name or value._mock_new_name) or + (value._mock_parent is not None) or + (value._mock_new_parent is not None)): + return False + + _parent = parent + while _parent is not None: + # setting a mock (value) as a child or return value of itself + # should not modify the mock + if _parent is value: + return False + _parent = _parent._mock_new_parent + + if new_name: + value._mock_new_parent = parent + value._mock_new_name = new_name + if name: + value._mock_parent = parent + value._mock_name = name + return True + + + +class Base(object): + _mock_return_value = DEFAULT + _mock_side_effect = None + def __init__(self, *args, **kwargs): + pass + + + +class NonCallableMock(Base): + """A non-callable version of `Mock`""" + + def __new__(cls, *args, **kw): + # every instance has its own class + # so we can create magic methods on the + # class without stomping on other mocks + new = type(cls.__name__, (cls,), {'__doc__': cls.__doc__}) + instance = object.__new__(new) + return instance + + + def __init__( + self, spec=None, wraps=None, name=None, spec_set=None, + parent=None, _spec_state=None, _new_name='', _new_parent=None, + **kwargs + ): + if _new_parent is None: + _new_parent = parent + + __dict__ = self.__dict__ + __dict__['_mock_parent'] = parent + __dict__['_mock_name'] = name + __dict__['_mock_new_name'] = _new_name + __dict__['_mock_new_parent'] = _new_parent + + if spec_set is not None: + spec = spec_set + spec_set = True + + self._mock_add_spec(spec, spec_set) + + __dict__['_mock_children'] = {} + __dict__['_mock_wraps'] = wraps + __dict__['_mock_delegate'] = None + + __dict__['_mock_called'] = False + __dict__['_mock_call_args'] = None + __dict__['_mock_call_count'] = 0 + __dict__['_mock_call_args_list'] = _CallList() + __dict__['_mock_mock_calls'] = _CallList() + + __dict__['method_calls'] = _CallList() + + if kwargs: + self.configure_mock(**kwargs) + + _super(NonCallableMock, self).__init__( + spec, wraps, name, spec_set, parent, + _spec_state + ) + + + def attach_mock(self, mock, attribute): + """ + Attach a mock as an attribute of this one, replacing its name and + parent. Calls to the attached mock will be recorded in the + `method_calls` and `mock_calls` attributes of this one.""" + mock._mock_parent = None + mock._mock_new_parent = None + mock._mock_name = '' + mock._mock_new_name = None + + setattr(self, attribute, mock) + + + def mock_add_spec(self, spec, spec_set=False): + """Add a spec to a mock. `spec` can either be an object or a + list of strings. Only attributes on the `spec` can be fetched as + attributes from the mock. + + If `spec_set` is True then only attributes on the spec can be set.""" + self._mock_add_spec(spec, spec_set) + + + def _mock_add_spec(self, spec, spec_set): + _spec_class = None + + if spec is not None and not _is_list(spec): + if isinstance(spec, ClassTypes): + _spec_class = spec + else: + _spec_class = _get_class(spec) + + spec = dir(spec) + + __dict__ = self.__dict__ + __dict__['_spec_class'] = _spec_class + __dict__['_spec_set'] = spec_set + __dict__['_mock_methods'] = spec + + + def __get_return_value(self): + ret = self._mock_return_value + if self._mock_delegate is not None: + ret = self._mock_delegate.return_value + + if ret is DEFAULT: + ret = self._get_child_mock( + _new_parent=self, _new_name='()' + ) + self.return_value = ret + return ret + + + def __set_return_value(self, value): + if self._mock_delegate is not None: + self._mock_delegate.return_value = value + else: + self._mock_return_value = value + _check_and_set_parent(self, value, None, '()') + + __return_value_doc = "The value to be returned when the mock is called." + return_value = property(__get_return_value, __set_return_value, + __return_value_doc) + + + @property + def __class__(self): + if self._spec_class is None: + return type(self) + return self._spec_class + + called = _delegating_property('called') + call_count = _delegating_property('call_count') + call_args = _delegating_property('call_args') + call_args_list = _delegating_property('call_args_list') + mock_calls = _delegating_property('mock_calls') + + + def __get_side_effect(self): + sig = self._mock_delegate + if sig is None: + return self._mock_side_effect + return sig.side_effect + + def __set_side_effect(self, value): + value = _try_iter(value) + sig = self._mock_delegate + if sig is None: + self._mock_side_effect = value + else: + sig.side_effect = value + + side_effect = property(__get_side_effect, __set_side_effect) + + + def reset_mock(self): + "Restore the mock object to its initial state." + self.called = False + self.call_args = None + self.call_count = 0 + self.mock_calls = _CallList() + self.call_args_list = _CallList() + self.method_calls = _CallList() + + for child in self._mock_children.values(): + if isinstance(child, _SpecState): + continue + child.reset_mock() + + ret = self._mock_return_value + if _is_instance_mock(ret) and ret is not self: + ret.reset_mock() + + + def configure_mock(self, **kwargs): + """Set attributes on the mock through keyword arguments. + + Attributes plus return values and side effects can be set on child + mocks using standard dot notation and unpacking a dictionary in the + method call: + + >>> attrs = {'method.return_value': 3, 'other.side_effect': KeyError} + >>> mock.configure_mock(**attrs)""" + for arg, val in sorted(kwargs.items(), + # we sort on the number of dots so that + # attributes are set before we set attributes on + # attributes + key=lambda entry: entry[0].count('.')): + args = arg.split('.') + final = args.pop() + obj = self + for entry in args: + obj = getattr(obj, entry) + setattr(obj, final, val) + + + def __getattr__(self, name): + if name == '_mock_methods': + raise AttributeError(name) + elif self._mock_methods is not None: + if name not in self._mock_methods or name in _all_magics: + raise AttributeError("Mock object has no attribute %r" % name) + elif _is_magic(name): + raise AttributeError(name) + + result = self._mock_children.get(name) + if result is _deleted: + raise AttributeError(name) + elif result is None: + wraps = None + if self._mock_wraps is not None: + # XXXX should we get the attribute without triggering code + # execution? + wraps = getattr(self._mock_wraps, name) + + result = self._get_child_mock( + parent=self, name=name, wraps=wraps, _new_name=name, + _new_parent=self + ) + self._mock_children[name] = result + + elif isinstance(result, _SpecState): + result = create_autospec( + result.spec, result.spec_set, result.instance, + result.parent, result.name + ) + self._mock_children[name] = result + + return result + + + def __repr__(self): + _name_list = [self._mock_new_name] + _parent = self._mock_new_parent + last = self + + dot = '.' + if _name_list == ['()']: + dot = '' + seen = set() + while _parent is not None: + last = _parent + + _name_list.append(_parent._mock_new_name + dot) + dot = '.' + if _parent._mock_new_name == '()': + dot = '' + + _parent = _parent._mock_new_parent + + # use ids here so as not to call __hash__ on the mocks + if id(_parent) in seen: + break + seen.add(id(_parent)) + + _name_list = list(reversed(_name_list)) + _first = last._mock_name or 'mock' + if len(_name_list) > 1: + if _name_list[1] not in ('()', '().'): + _first += '.' + _name_list[0] = _first + name = ''.join(_name_list) + + name_string = '' + if name not in ('mock', 'mock.'): + name_string = ' name=%r' % name + + spec_string = '' + if self._spec_class is not None: + spec_string = ' spec=%r' + if self._spec_set: + spec_string = ' spec_set=%r' + spec_string = spec_string % self._spec_class.__name__ + return "<%s%s%s id='%s'>" % ( + type(self).__name__, + name_string, + spec_string, + id(self) + ) + + + def __dir__(self): + """Filter the output of `dir(mock)` to only useful members. + XXXX + """ + extras = self._mock_methods or [] + from_type = dir(type(self)) + from_dict = list(self.__dict__) + + if FILTER_DIR: + from_type = [e for e in from_type if not e.startswith('_')] + from_dict = [e for e in from_dict if not e.startswith('_') or + _is_magic(e)] + return sorted(set(extras + from_type + from_dict + + list(self._mock_children))) + + + def __setattr__(self, name, value): + if name in _allowed_names: + # property setters go through here + return object.__setattr__(self, name, value) + elif (self._spec_set and self._mock_methods is not None and + name not in self._mock_methods and + name not in self.__dict__): + raise AttributeError("Mock object has no attribute '%s'" % name) + elif name in _unsupported_magics: + msg = 'Attempting to set unsupported magic method %r.' % name + raise AttributeError(msg) + elif name in _all_magics: + if self._mock_methods is not None and name not in self._mock_methods: + raise AttributeError("Mock object has no attribute '%s'" % name) + + if not _is_instance_mock(value): + setattr(type(self), name, _get_method(name, value)) + original = value + value = lambda *args, **kw: original(self, *args, **kw) + else: + # only set _new_name and not name so that mock_calls is tracked + # but not method calls + _check_and_set_parent(self, value, None, name) + setattr(type(self), name, value) + self._mock_children[name] = value + elif name == '__class__': + self._spec_class = value + return + else: + if _check_and_set_parent(self, value, name, name): + self._mock_children[name] = value + return object.__setattr__(self, name, value) + + + def __delattr__(self, name): + if name in _all_magics and name in type(self).__dict__: + delattr(type(self), name) + if name not in self.__dict__: + # for magic methods that are still MagicProxy objects and + # not set on the instance itself + return + + if name in self.__dict__: + object.__delattr__(self, name) + + obj = self._mock_children.get(name, _missing) + if obj is _deleted: + raise AttributeError(name) + if obj is not _missing: + del self._mock_children[name] + self._mock_children[name] = _deleted + + + + def _format_mock_call_signature(self, args, kwargs): + name = self._mock_name or 'mock' + return _format_call_signature(name, args, kwargs) + + + def _format_mock_failure_message(self, args, kwargs): + message = 'Expected call: %s\nActual call: %s' + expected_string = self._format_mock_call_signature(args, kwargs) + call_args = self.call_args + if len(call_args) == 3: + call_args = call_args[1:] + actual_string = self._format_mock_call_signature(*call_args) + return message % (expected_string, actual_string) + + + def assert_called_with(_mock_self, *args, **kwargs): + """assert that the mock was called with the specified arguments. + + Raises an AssertionError if the args and keyword args passed in are + different to the last call to the mock.""" + self = _mock_self + if self.call_args is None: + expected = self._format_mock_call_signature(args, kwargs) + raise AssertionError('Expected call: %s\nNot called' % (expected,)) + + if self.call_args != (args, kwargs): + msg = self._format_mock_failure_message(args, kwargs) + raise AssertionError(msg) + + + def assert_called_once_with(_mock_self, *args, **kwargs): + """assert that the mock was called exactly once and with the specified + arguments.""" + self = _mock_self + if not self.call_count == 1: + msg = ("Expected to be called once. Called %s times." % + self.call_count) + raise AssertionError(msg) + return self.assert_called_with(*args, **kwargs) + + + def assert_has_calls(self, calls, any_order=False): + """assert the mock has been called with the specified calls. + The `mock_calls` list is checked for the calls. + + If `any_order` is False (the default) then the calls must be + sequential. There can be extra calls before or after the + specified calls. + + If `any_order` is True then the calls can be in any order, but + they must all appear in `mock_calls`.""" + if not any_order: + if calls not in self.mock_calls: + raise AssertionError( + 'Calls not found.\nExpected: %r\n' + 'Actual: %r' % (calls, self.mock_calls) + ) + return + + all_calls = list(self.mock_calls) + + not_found = [] + for kall in calls: + try: + all_calls.remove(kall) + except ValueError: + not_found.append(kall) + if not_found: + raise AssertionError( + '%r not all found in call list' % (tuple(not_found),) + ) + + + def assert_any_call(self, *args, **kwargs): + """assert the mock has been called with the specified arguments. + + The assert passes if the mock has *ever* been called, unlike + `assert_called_with` and `assert_called_once_with` that only pass if + the call is the most recent one.""" + kall = call(*args, **kwargs) + if kall not in self.call_args_list: + expected_string = self._format_mock_call_signature(args, kwargs) + raise AssertionError( + '%s call not found' % expected_string + ) + + + def _get_child_mock(self, **kw): + """Create the child mocks for attributes and return value. + By default child mocks will be the same type as the parent. + Subclasses of Mock may want to override this to customize the way + child mocks are made. + + For non-callable mocks the callable variant will be used (rather than + any custom subclass).""" + _type = type(self) + if not issubclass(_type, CallableMixin): + if issubclass(_type, NonCallableMagicMock): + klass = MagicMock + elif issubclass(_type, NonCallableMock) : + klass = Mock + else: + klass = _type.__mro__[1] + return klass(**kw) + + + +def _try_iter(obj): + if obj is None: + return obj + if _is_exception(obj): + return obj + if _callable(obj): + return obj + try: + return iter(obj) + except TypeError: + # XXXX backwards compatibility + # but this will blow up on first call - so maybe we should fail early? + return obj + + + +class CallableMixin(Base): + + def __init__(self, spec=None, side_effect=None, return_value=DEFAULT, + wraps=None, name=None, spec_set=None, parent=None, + _spec_state=None, _new_name='', _new_parent=None, **kwargs): + self.__dict__['_mock_return_value'] = return_value + + _super(CallableMixin, self).__init__( + spec, wraps, name, spec_set, parent, + _spec_state, _new_name, _new_parent, **kwargs + ) + + self.side_effect = side_effect + + + def _mock_check_sig(self, *args, **kwargs): + # stub method that can be replaced with one with a specific signature + pass + + + def __call__(_mock_self, *args, **kwargs): + # can't use self in-case a function / method we are mocking uses self + # in the signature + _mock_self._mock_check_sig(*args, **kwargs) + return _mock_self._mock_call(*args, **kwargs) + + + def _mock_call(_mock_self, *args, **kwargs): + self = _mock_self + self.called = True + self.call_count += 1 + self.call_args = _Call((args, kwargs), two=True) + self.call_args_list.append(_Call((args, kwargs), two=True)) + + _new_name = self._mock_new_name + _new_parent = self._mock_new_parent + self.mock_calls.append(_Call(('', args, kwargs))) + + seen = set() + skip_next_dot = _new_name == '()' + do_method_calls = self._mock_parent is not None + name = self._mock_name + while _new_parent is not None: + this_mock_call = _Call((_new_name, args, kwargs)) + if _new_parent._mock_new_name: + dot = '.' + if skip_next_dot: + dot = '' + + skip_next_dot = False + if _new_parent._mock_new_name == '()': + skip_next_dot = True + + _new_name = _new_parent._mock_new_name + dot + _new_name + + if do_method_calls: + if _new_name == name: + this_method_call = this_mock_call + else: + this_method_call = _Call((name, args, kwargs)) + _new_parent.method_calls.append(this_method_call) + + do_method_calls = _new_parent._mock_parent is not None + if do_method_calls: + name = _new_parent._mock_name + '.' + name + + _new_parent.mock_calls.append(this_mock_call) + _new_parent = _new_parent._mock_new_parent + + # use ids here so as not to call __hash__ on the mocks + _new_parent_id = id(_new_parent) + if _new_parent_id in seen: + break + seen.add(_new_parent_id) + + ret_val = DEFAULT + effect = self.side_effect + if effect is not None: + if _is_exception(effect): + raise effect + + if not _callable(effect): + result = next(effect) + if _is_exception(result): + raise result + return result + + ret_val = effect(*args, **kwargs) + if ret_val is DEFAULT: + ret_val = self.return_value + + if (self._mock_wraps is not None and + self._mock_return_value is DEFAULT): + return self._mock_wraps(*args, **kwargs) + if ret_val is DEFAULT: + ret_val = self.return_value + return ret_val + + + +class Mock(CallableMixin, NonCallableMock): + """ + Create a new `Mock` object. `Mock` takes several optional arguments + that specify the behaviour of the Mock object: + + * `spec`: This can be either a list of strings or an existing object (a + class or instance) that acts as the specification for the mock object. If + you pass in an object then a list of strings is formed by calling dir on + the object (excluding unsupported magic attributes and methods). Accessing + any attribute not in this list will raise an `AttributeError`. + + If `spec` is an object (rather than a list of strings) then + `mock.__class__` returns the class of the spec object. This allows mocks + to pass `isinstance` tests. + + * `spec_set`: A stricter variant of `spec`. If used, attempting to *set* + or get an attribute on the mock that isn't on the object passed as + `spec_set` will raise an `AttributeError`. + + * `side_effect`: A function to be called whenever the Mock is called. See + the `side_effect` attribute. Useful for raising exceptions or + dynamically changing return values. The function is called with the same + arguments as the mock, and unless it returns `DEFAULT`, the return + value of this function is used as the return value. + + Alternatively `side_effect` can be an exception class or instance. In + this case the exception will be raised when the mock is called. + + If `side_effect` is an iterable then each call to the mock will return + the next value from the iterable. If any of the members of the iterable + are exceptions they will be raised instead of returned. + + * `return_value`: The value returned when the mock is called. By default + this is a new Mock (created on first access). See the + `return_value` attribute. + + * `wraps`: Item for the mock object to wrap. If `wraps` is not None then + calling the Mock will pass the call through to the wrapped object + (returning the real result). Attribute access on the mock will return a + Mock object that wraps the corresponding attribute of the wrapped object + (so attempting to access an attribute that doesn't exist will raise an + `AttributeError`). + + If the mock has an explicit `return_value` set then calls are not passed + to the wrapped object and the `return_value` is returned instead. + + * `name`: If the mock has a name then it will be used in the repr of the + mock. This can be useful for debugging. The name is propagated to child + mocks. + + Mocks can also be called with arbitrary keyword arguments. These will be + used to set attributes on the mock after it is created. + """ + + + +def _dot_lookup(thing, comp, import_path): + try: + return getattr(thing, comp) + except AttributeError: + __import__(import_path) + return getattr(thing, comp) + + +def _importer(target): + components = target.split('.') + import_path = components.pop(0) + thing = __import__(import_path) + + for comp in components: + import_path += ".%s" % comp + thing = _dot_lookup(thing, comp, import_path) + return thing + + +def _is_started(patcher): + # XXXX horrible + return hasattr(patcher, 'is_local') + + +class _patch(object): + + attribute_name = None + _active_patches = set() + + def __init__( + self, getter, attribute, new, spec, create, + spec_set, autospec, new_callable, kwargs + ): + if new_callable is not None: + if new is not DEFAULT: + raise ValueError( + "Cannot use 'new' and 'new_callable' together" + ) + if autospec is not None: + raise ValueError( + "Cannot use 'autospec' and 'new_callable' together" + ) + + self.getter = getter + self.attribute = attribute + self.new = new + self.new_callable = new_callable + self.spec = spec + self.create = create + self.has_local = False + self.spec_set = spec_set + self.autospec = autospec + self.kwargs = kwargs + self.additional_patchers = [] + + + def copy(self): + patcher = _patch( + self.getter, self.attribute, self.new, self.spec, + self.create, self.spec_set, + self.autospec, self.new_callable, self.kwargs + ) + patcher.attribute_name = self.attribute_name + patcher.additional_patchers = [ + p.copy() for p in self.additional_patchers + ] + return patcher + + + def __call__(self, func): + if isinstance(func, ClassTypes): + return self.decorate_class(func) + return self.decorate_callable(func) + + + def decorate_class(self, klass): + for attr in dir(klass): + if not attr.startswith(patch.TEST_PREFIX): + continue + + attr_value = getattr(klass, attr) + if not hasattr(attr_value, "__call__"): + continue + + patcher = self.copy() + setattr(klass, attr, patcher(attr_value)) + return klass + + + def decorate_callable(self, func): + if hasattr(func, 'patchings'): + func.patchings.append(self) + return func + + @wraps(func) + def patched(*args, **keywargs): + # don't use a with here (backwards compatability with Python 2.4) + extra_args = [] + entered_patchers = [] + + # can't use try...except...finally because of Python 2.4 + # compatibility + exc_info = tuple() + try: + try: + for patching in patched.patchings: + arg = patching.__enter__() + entered_patchers.append(patching) + if patching.attribute_name is not None: + keywargs.update(arg) + elif patching.new is DEFAULT: + extra_args.append(arg) + + args += tuple(extra_args) + return func(*args, **keywargs) + except: + if (patching not in entered_patchers and + _is_started(patching)): + # the patcher may have been started, but an exception + # raised whilst entering one of its additional_patchers + entered_patchers.append(patching) + # Pass the exception to __exit__ + exc_info = sys.exc_info() + # re-raise the exception + raise + finally: + for patching in reversed(entered_patchers): + patching.__exit__(*exc_info) + + patched.patchings = [self] + if hasattr(func, 'func_code'): + # not in Python 3 + patched.compat_co_firstlineno = getattr( + func, "compat_co_firstlineno", + func.func_code.co_firstlineno + ) + return patched + + + def get_original(self): + target = self.getter() + name = self.attribute + + original = DEFAULT + local = False + + try: + original = target.__dict__[name] + except (AttributeError, KeyError): + original = getattr(target, name, DEFAULT) + else: + local = True + + if not self.create and original is DEFAULT: + raise AttributeError( + "%s does not have the attribute %r" % (target, name) + ) + return original, local + + + def __enter__(self): + """Perform the patch.""" + new, spec, spec_set = self.new, self.spec, self.spec_set + autospec, kwargs = self.autospec, self.kwargs + new_callable = self.new_callable + self.target = self.getter() + + # normalise False to None + if spec is False: + spec = None + if spec_set is False: + spec_set = None + if autospec is False: + autospec = None + + if spec is not None and autospec is not None: + raise TypeError("Can't specify spec and autospec") + if ((spec is not None or autospec is not None) and + spec_set not in (True, None)): + raise TypeError("Can't provide explicit spec_set *and* spec or autospec") + + original, local = self.get_original() + + if new is DEFAULT and autospec is None: + inherit = False + if spec is True: + # set spec to the object we are replacing + spec = original + if spec_set is True: + spec_set = original + spec = None + elif spec is not None: + if spec_set is True: + spec_set = spec + spec = None + elif spec_set is True: + spec_set = original + + if spec is not None or spec_set is not None: + if original is DEFAULT: + raise TypeError("Can't use 'spec' with create=True") + if isinstance(original, ClassTypes): + # If we're patching out a class and there is a spec + inherit = True + + Klass = MagicMock + _kwargs = {} + if new_callable is not None: + Klass = new_callable + elif spec is not None or spec_set is not None: + this_spec = spec + if spec_set is not None: + this_spec = spec_set + if _is_list(this_spec): + not_callable = '__call__' not in this_spec + else: + not_callable = not _callable(this_spec) + if not_callable: + Klass = NonCallableMagicMock + + if spec is not None: + _kwargs['spec'] = spec + if spec_set is not None: + _kwargs['spec_set'] = spec_set + + # add a name to mocks + if (isinstance(Klass, type) and + issubclass(Klass, NonCallableMock) and self.attribute): + _kwargs['name'] = self.attribute + + _kwargs.update(kwargs) + new = Klass(**_kwargs) + + if inherit and _is_instance_mock(new): + # we can only tell if the instance should be callable if the + # spec is not a list + this_spec = spec + if spec_set is not None: + this_spec = spec_set + if (not _is_list(this_spec) and not + _instance_callable(this_spec)): + Klass = NonCallableMagicMock + + _kwargs.pop('name') + new.return_value = Klass(_new_parent=new, _new_name='()', + **_kwargs) + elif autospec is not None: + # spec is ignored, new *must* be default, spec_set is treated + # as a boolean. Should we check spec is not None and that spec_set + # is a bool? + if new is not DEFAULT: + raise TypeError( + "autospec creates the mock for you. Can't specify " + "autospec and new." + ) + if original is DEFAULT: + raise TypeError("Can't use 'autospec' with create=True") + spec_set = bool(spec_set) + if autospec is True: + autospec = original + + new = create_autospec(autospec, spec_set=spec_set, + _name=self.attribute, **kwargs) + elif kwargs: + # can't set keyword args when we aren't creating the mock + # XXXX If new is a Mock we could call new.configure_mock(**kwargs) + raise TypeError("Can't pass kwargs to a mock we aren't creating") + + new_attr = new + + self.temp_original = original + self.is_local = local + setattr(self.target, self.attribute, new_attr) + if self.attribute_name is not None: + extra_args = {} + if self.new is DEFAULT: + extra_args[self.attribute_name] = new + for patching in self.additional_patchers: + arg = patching.__enter__() + if patching.new is DEFAULT: + extra_args.update(arg) + return extra_args + + return new + + + def __exit__(self, *exc_info): + """Undo the patch.""" + if not _is_started(self): + raise RuntimeError('stop called on unstarted patcher') + + if self.is_local and self.temp_original is not DEFAULT: + setattr(self.target, self.attribute, self.temp_original) + else: + delattr(self.target, self.attribute) + if not self.create and not hasattr(self.target, self.attribute): + # needed for proxy objects like django settings + setattr(self.target, self.attribute, self.temp_original) + + del self.temp_original + del self.is_local + del self.target + for patcher in reversed(self.additional_patchers): + if _is_started(patcher): + patcher.__exit__(*exc_info) + + + def start(self): + """Activate a patch, returning any created mock.""" + result = self.__enter__() + self._active_patches.add(self) + return result + + + def stop(self): + """Stop an active patch.""" + self._active_patches.discard(self) + return self.__exit__() + + + +def _get_target(target): + try: + target, attribute = target.rsplit('.', 1) + except (TypeError, ValueError): + raise TypeError("Need a valid target to patch. You supplied: %r" % + (target,)) + getter = lambda: _importer(target) + return getter, attribute + + +def _patch_object( + target, attribute, new=DEFAULT, spec=None, + create=False, spec_set=None, autospec=None, + new_callable=None, **kwargs + ): + """ + patch.object(target, attribute, new=DEFAULT, spec=None, create=False, + spec_set=None, autospec=None, new_callable=None, **kwargs) + + patch the named member (`attribute`) on an object (`target`) with a mock + object. + + `patch.object` can be used as a decorator, class decorator or a context + manager. Arguments `new`, `spec`, `create`, `spec_set`, + `autospec` and `new_callable` have the same meaning as for `patch`. Like + `patch`, `patch.object` takes arbitrary keyword arguments for configuring + the mock object it creates. + + When used as a class decorator `patch.object` honours `patch.TEST_PREFIX` + for choosing which methods to wrap. + """ + getter = lambda: target + return _patch( + getter, attribute, new, spec, create, + spec_set, autospec, new_callable, kwargs + ) + + +def _patch_multiple(target, spec=None, create=False, spec_set=None, + autospec=None, new_callable=None, **kwargs): + """Perform multiple patches in a single call. It takes the object to be + patched (either as an object or a string to fetch the object by importing) + and keyword arguments for the patches:: + + with patch.multiple(settings, FIRST_PATCH='one', SECOND_PATCH='two'): + ... + + Use `DEFAULT` as the value if you want `patch.multiple` to create + mocks for you. In this case the created mocks are passed into a decorated + function by keyword, and a dictionary is returned when `patch.multiple` is + used as a context manager. + + `patch.multiple` can be used as a decorator, class decorator or a context + manager. The arguments `spec`, `spec_set`, `create`, + `autospec` and `new_callable` have the same meaning as for `patch`. These + arguments will be applied to *all* patches done by `patch.multiple`. + + When used as a class decorator `patch.multiple` honours `patch.TEST_PREFIX` + for choosing which methods to wrap. + """ + if type(target) in (unicode, str): + getter = lambda: _importer(target) + else: + getter = lambda: target + + if not kwargs: + raise ValueError( + 'Must supply at least one keyword argument with patch.multiple' + ) + # need to wrap in a list for python 3, where items is a view + items = list(kwargs.items()) + attribute, new = items[0] + patcher = _patch( + getter, attribute, new, spec, create, spec_set, + autospec, new_callable, {} + ) + patcher.attribute_name = attribute + for attribute, new in items[1:]: + this_patcher = _patch( + getter, attribute, new, spec, create, spec_set, + autospec, new_callable, {} + ) + this_patcher.attribute_name = attribute + patcher.additional_patchers.append(this_patcher) + return patcher + + +def patch( + target, new=DEFAULT, spec=None, create=False, + spec_set=None, autospec=None, new_callable=None, **kwargs + ): + """ + `patch` acts as a function decorator, class decorator or a context + manager. Inside the body of the function or with statement, the `target` + is patched with a `new` object. When the function/with statement exits + the patch is undone. + + If `new` is omitted, then the target is replaced with a + `MagicMock`. If `patch` is used as a decorator and `new` is + omitted, the created mock is passed in as an extra argument to the + decorated function. If `patch` is used as a context manager the created + mock is returned by the context manager. + + `target` should be a string in the form `'package.module.ClassName'`. The + `target` is imported and the specified object replaced with the `new` + object, so the `target` must be importable from the environment you are + calling `patch` from. The target is imported when the decorated function + is executed, not at decoration time. + + The `spec` and `spec_set` keyword arguments are passed to the `MagicMock` + if patch is creating one for you. + + In addition you can pass `spec=True` or `spec_set=True`, which causes + patch to pass in the object being mocked as the spec/spec_set object. + + `new_callable` allows you to specify a different class, or callable object, + that will be called to create the `new` object. By default `MagicMock` is + used. + + A more powerful form of `spec` is `autospec`. If you set `autospec=True` + then the mock with be created with a spec from the object being replaced. + All attributes of the mock will also have the spec of the corresponding + attribute of the object being replaced. Methods and functions being + mocked will have their arguments checked and will raise a `TypeError` if + they are called with the wrong signature. For mocks replacing a class, + their return value (the 'instance') will have the same spec as the class. + + Instead of `autospec=True` you can pass `autospec=some_object` to use an + arbitrary object as the spec instead of the one being replaced. + + By default `patch` will fail to replace attributes that don't exist. If + you pass in `create=True`, and the attribute doesn't exist, patch will + create the attribute for you when the patched function is called, and + delete it again afterwards. This is useful for writing tests against + attributes that your production code creates at runtime. It is off by by + default because it can be dangerous. With it switched on you can write + passing tests against APIs that don't actually exist! + + Patch can be used as a `TestCase` class decorator. It works by + decorating each test method in the class. This reduces the boilerplate + code when your test methods share a common patchings set. `patch` finds + tests by looking for method names that start with `patch.TEST_PREFIX`. + By default this is `test`, which matches the way `unittest` finds tests. + You can specify an alternative prefix by setting `patch.TEST_PREFIX`. + + Patch can be used as a context manager, with the with statement. Here the + patching applies to the indented block after the with statement. If you + use "as" then the patched object will be bound to the name after the + "as"; very useful if `patch` is creating a mock object for you. + + `patch` takes arbitrary keyword arguments. These will be passed to + the `Mock` (or `new_callable`) on construction. + + `patch.dict(...)`, `patch.multiple(...)` and `patch.object(...)` are + available for alternate use-cases. + """ + getter, attribute = _get_target(target) + return _patch( + getter, attribute, new, spec, create, + spec_set, autospec, new_callable, kwargs + ) + + +class _patch_dict(object): + """ + Patch a dictionary, or dictionary like object, and restore the dictionary + to its original state after the test. + + `in_dict` can be a dictionary or a mapping like container. If it is a + mapping then it must at least support getting, setting and deleting items + plus iterating over keys. + + `in_dict` can also be a string specifying the name of the dictionary, which + will then be fetched by importing it. + + `values` can be a dictionary of values to set in the dictionary. `values` + can also be an iterable of `(key, value)` pairs. + + If `clear` is True then the dictionary will be cleared before the new + values are set. + + `patch.dict` can also be called with arbitrary keyword arguments to set + values in the dictionary:: + + with patch.dict('sys.modules', mymodule=Mock(), other_module=Mock()): + ... + + `patch.dict` can be used as a context manager, decorator or class + decorator. When used as a class decorator `patch.dict` honours + `patch.TEST_PREFIX` for choosing which methods to wrap. + """ + + def __init__(self, in_dict, values=(), clear=False, **kwargs): + if isinstance(in_dict, basestring): + in_dict = _importer(in_dict) + self.in_dict = in_dict + # support any argument supported by dict(...) constructor + self.values = dict(values) + self.values.update(kwargs) + self.clear = clear + self._original = None + + + def __call__(self, f): + if isinstance(f, ClassTypes): + return self.decorate_class(f) + @wraps(f) + def _inner(*args, **kw): + self._patch_dict() + try: + return f(*args, **kw) + finally: + self._unpatch_dict() + + return _inner + + + def decorate_class(self, klass): + for attr in dir(klass): + attr_value = getattr(klass, attr) + if (attr.startswith(patch.TEST_PREFIX) and + hasattr(attr_value, "__call__")): + decorator = _patch_dict(self.in_dict, self.values, self.clear) + decorated = decorator(attr_value) + setattr(klass, attr, decorated) + return klass + + + def __enter__(self): + """Patch the dict.""" + self._patch_dict() + + + def _patch_dict(self): + values = self.values + in_dict = self.in_dict + clear = self.clear + + try: + original = in_dict.copy() + except AttributeError: + # dict like object with no copy method + # must support iteration over keys + original = {} + for key in in_dict: + original[key] = in_dict[key] + self._original = original + + if clear: + _clear_dict(in_dict) + + try: + in_dict.update(values) + except AttributeError: + # dict like object with no update method + for key in values: + in_dict[key] = values[key] + + + def _unpatch_dict(self): + in_dict = self.in_dict + original = self._original + + _clear_dict(in_dict) + + try: + in_dict.update(original) + except AttributeError: + for key in original: + in_dict[key] = original[key] + + + def __exit__(self, *args): + """Unpatch the dict.""" + self._unpatch_dict() + return False + + start = __enter__ + stop = __exit__ + + +def _clear_dict(in_dict): + try: + in_dict.clear() + except AttributeError: + keys = list(in_dict) + for key in keys: + del in_dict[key] + + +def _patch_stopall(): + """Stop all active patches.""" + for patch in list(_patch._active_patches): + patch.stop() + + +patch.object = _patch_object +patch.dict = _patch_dict +patch.multiple = _patch_multiple +patch.stopall = _patch_stopall +patch.TEST_PREFIX = 'test' + +magic_methods = ( + "lt le gt ge eq ne " + "getitem setitem delitem " + "len contains iter " + "hash str sizeof " + "enter exit " + "divmod neg pos abs invert " + "complex int float index " + "trunc floor ceil " +) + +numerics = "add sub mul div floordiv mod lshift rshift and xor or pow " +inplace = ' '.join('i%s' % n for n in numerics.split()) +right = ' '.join('r%s' % n for n in numerics.split()) +extra = '' +if inPy3k: + extra = 'bool next ' +else: + extra = 'unicode long nonzero oct hex truediv rtruediv ' + +# not including __prepare__, __instancecheck__, __subclasscheck__ +# (as they are metaclass methods) +# __del__ is not supported at all as it causes problems if it exists + +_non_defaults = set('__%s__' % method for method in [ + 'cmp', 'getslice', 'setslice', 'coerce', 'subclasses', + 'format', 'get', 'set', 'delete', 'reversed', + 'missing', 'reduce', 'reduce_ex', 'getinitargs', + 'getnewargs', 'getstate', 'setstate', 'getformat', + 'setformat', 'repr', 'dir' +]) + + +def _get_method(name, func): + "Turns a callable object (like a mock) into a real function" + def method(self, *args, **kw): + return func(self, *args, **kw) + method.__name__ = name + return method + + +_magics = set( + '__%s__' % method for method in + ' '.join([magic_methods, numerics, inplace, right, extra]).split() +) + +_all_magics = _magics | _non_defaults + +_unsupported_magics = set([ + '__getattr__', '__setattr__', + '__init__', '__new__', '__prepare__' + '__instancecheck__', '__subclasscheck__', + '__del__' +]) + +_calculate_return_value = { + '__hash__': lambda self: object.__hash__(self), + '__str__': lambda self: object.__str__(self), + '__sizeof__': lambda self: object.__sizeof__(self), + '__unicode__': lambda self: unicode(object.__str__(self)), +} + +_return_values = { + '__lt__': NotImplemented, + '__gt__': NotImplemented, + '__le__': NotImplemented, + '__ge__': NotImplemented, + '__int__': 1, + '__contains__': False, + '__len__': 0, + '__exit__': False, + '__complex__': 1j, + '__float__': 1.0, + '__bool__': True, + '__nonzero__': True, + '__oct__': '1', + '__hex__': '0x1', + '__long__': long(1), + '__index__': 1, +} + + +def _get_eq(self): + def __eq__(other): + ret_val = self.__eq__._mock_return_value + if ret_val is not DEFAULT: + return ret_val + return self is other + return __eq__ + +def _get_ne(self): + def __ne__(other): + if self.__ne__._mock_return_value is not DEFAULT: + return DEFAULT + return self is not other + return __ne__ + +def _get_iter(self): + def __iter__(): + ret_val = self.__iter__._mock_return_value + if ret_val is DEFAULT: + return iter([]) + # if ret_val was already an iterator, then calling iter on it should + # return the iterator unchanged + return iter(ret_val) + return __iter__ + +_side_effect_methods = { + '__eq__': _get_eq, + '__ne__': _get_ne, + '__iter__': _get_iter, +} + + + +def _set_return_value(mock, method, name): + fixed = _return_values.get(name, DEFAULT) + if fixed is not DEFAULT: + method.return_value = fixed + return + + return_calulator = _calculate_return_value.get(name) + if return_calulator is not None: + try: + return_value = return_calulator(mock) + except AttributeError: + # XXXX why do we return AttributeError here? + # set it as a side_effect instead? + return_value = AttributeError(name) + method.return_value = return_value + return + + side_effector = _side_effect_methods.get(name) + if side_effector is not None: + method.side_effect = side_effector(mock) + + + +class MagicMixin(object): + def __init__(self, *args, **kw): + _super(MagicMixin, self).__init__(*args, **kw) + self._mock_set_magics() + + + def _mock_set_magics(self): + these_magics = _magics + + if self._mock_methods is not None: + these_magics = _magics.intersection(self._mock_methods) + + remove_magics = set() + remove_magics = _magics - these_magics + + for entry in remove_magics: + if entry in type(self).__dict__: + # remove unneeded magic methods + delattr(self, entry) + + # don't overwrite existing attributes if called a second time + these_magics = these_magics - set(type(self).__dict__) + + _type = type(self) + for entry in these_magics: + setattr(_type, entry, MagicProxy(entry, self)) + + + +class NonCallableMagicMock(MagicMixin, NonCallableMock): + """A version of `MagicMock` that isn't callable.""" + def mock_add_spec(self, spec, spec_set=False): + """Add a spec to a mock. `spec` can either be an object or a + list of strings. Only attributes on the `spec` can be fetched as + attributes from the mock. + + If `spec_set` is True then only attributes on the spec can be set.""" + self._mock_add_spec(spec, spec_set) + self._mock_set_magics() + + + +class MagicMock(MagicMixin, Mock): + """ + MagicMock is a subclass of Mock with default implementations + of most of the magic methods. You can use MagicMock without having to + configure the magic methods yourself. + + If you use the `spec` or `spec_set` arguments then *only* magic + methods that exist in the spec will be created. + + Attributes and the return value of a `MagicMock` will also be `MagicMocks`. + """ + def mock_add_spec(self, spec, spec_set=False): + """Add a spec to a mock. `spec` can either be an object or a + list of strings. Only attributes on the `spec` can be fetched as + attributes from the mock. + + If `spec_set` is True then only attributes on the spec can be set.""" + self._mock_add_spec(spec, spec_set) + self._mock_set_magics() + + + +class MagicProxy(object): + def __init__(self, name, parent): + self.name = name + self.parent = parent + + def __call__(self, *args, **kwargs): + m = self.create_mock() + return m(*args, **kwargs) + + def create_mock(self): + entry = self.name + parent = self.parent + m = parent._get_child_mock(name=entry, _new_name=entry, + _new_parent=parent) + setattr(parent, entry, m) + _set_return_value(parent, m, entry) + return m + + def __get__(self, obj, _type=None): + return self.create_mock() + + + +class _ANY(object): + "A helper object that compares equal to everything." + + def __eq__(self, other): + return True + + def __ne__(self, other): + return False + + def __repr__(self): + return '' + +ANY = _ANY() + + + +def _format_call_signature(name, args, kwargs): + message = '%s(%%s)' % name + formatted_args = '' + args_string = ', '.join([repr(arg) for arg in args]) + kwargs_string = ', '.join([ + '%s=%r' % (key, value) for key, value in kwargs.items() + ]) + if args_string: + formatted_args = args_string + if kwargs_string: + if formatted_args: + formatted_args += ', ' + formatted_args += kwargs_string + + return message % formatted_args + + + +class _Call(tuple): + """ + A tuple for holding the results of a call to a mock, either in the form + `(args, kwargs)` or `(name, args, kwargs)`. + + If args or kwargs are empty then a call tuple will compare equal to + a tuple without those values. This makes comparisons less verbose:: + + _Call(('name', (), {})) == ('name',) + _Call(('name', (1,), {})) == ('name', (1,)) + _Call(((), {'a': 'b'})) == ({'a': 'b'},) + + The `_Call` object provides a useful shortcut for comparing with call:: + + _Call(((1, 2), {'a': 3})) == call(1, 2, a=3) + _Call(('foo', (1, 2), {'a': 3})) == call.foo(1, 2, a=3) + + If the _Call has no name then it will match any name. + """ + def __new__(cls, value=(), name=None, parent=None, two=False, + from_kall=True): + name = '' + args = () + kwargs = {} + _len = len(value) + if _len == 3: + name, args, kwargs = value + elif _len == 2: + first, second = value + if isinstance(first, basestring): + name = first + if isinstance(second, tuple): + args = second + else: + kwargs = second + else: + args, kwargs = first, second + elif _len == 1: + value, = value + if isinstance(value, basestring): + name = value + elif isinstance(value, tuple): + args = value + else: + kwargs = value + + if two: + return tuple.__new__(cls, (args, kwargs)) + + return tuple.__new__(cls, (name, args, kwargs)) + + + def __init__(self, value=(), name=None, parent=None, two=False, + from_kall=True): + self.name = name + self.parent = parent + self.from_kall = from_kall + + + def __eq__(self, other): + if other is ANY: + return True + try: + len_other = len(other) + except TypeError: + return False + + self_name = '' + if len(self) == 2: + self_args, self_kwargs = self + else: + self_name, self_args, self_kwargs = self + + other_name = '' + if len_other == 0: + other_args, other_kwargs = (), {} + elif len_other == 3: + other_name, other_args, other_kwargs = other + elif len_other == 1: + value, = other + if isinstance(value, tuple): + other_args = value + other_kwargs = {} + elif isinstance(value, basestring): + other_name = value + other_args, other_kwargs = (), {} + else: + other_args = () + other_kwargs = value + else: + # len 2 + # could be (name, args) or (name, kwargs) or (args, kwargs) + first, second = other + if isinstance(first, basestring): + other_name = first + if isinstance(second, tuple): + other_args, other_kwargs = second, {} + else: + other_args, other_kwargs = (), second + else: + other_args, other_kwargs = first, second + + if self_name and other_name != self_name: + return False + + # this order is important for ANY to work! + return (other_args, other_kwargs) == (self_args, self_kwargs) + + + def __ne__(self, other): + return not self.__eq__(other) + + + def __call__(self, *args, **kwargs): + if self.name is None: + return _Call(('', args, kwargs), name='()') + + name = self.name + '()' + return _Call((self.name, args, kwargs), name=name, parent=self) + + + def __getattr__(self, attr): + if self.name is None: + return _Call(name=attr, from_kall=False) + name = '%s.%s' % (self.name, attr) + return _Call(name=name, parent=self, from_kall=False) + + + def __repr__(self): + if not self.from_kall: + name = self.name or 'call' + if name.startswith('()'): + name = 'call%s' % name + return name + + if len(self) == 2: + name = 'call' + args, kwargs = self + else: + name, args, kwargs = self + if not name: + name = 'call' + elif not name.startswith('()'): + name = 'call.%s' % name + else: + name = 'call%s' % name + return _format_call_signature(name, args, kwargs) + + + def call_list(self): + """For a call object that represents multiple calls, `call_list` + returns a list of all the intermediate calls as well as the + final call.""" + vals = [] + thing = self + while thing is not None: + if thing.from_kall: + vals.append(thing) + thing = thing.parent + return _CallList(reversed(vals)) + + +call = _Call(from_kall=False) + + + +def create_autospec(spec, spec_set=False, instance=False, _parent=None, + _name=None, **kwargs): + """Create a mock object using another object as a spec. Attributes on the + mock will use the corresponding attribute on the `spec` object as their + spec. + + Functions or methods being mocked will have their arguments checked + to check that they are called with the correct signature. + + If `spec_set` is True then attempting to set attributes that don't exist + on the spec object will raise an `AttributeError`. + + If a class is used as a spec then the return value of the mock (the + instance of the class) will have the same spec. You can use a class as the + spec for an instance object by passing `instance=True`. The returned mock + will only be callable if instances of the mock are callable. + + `create_autospec` also takes arbitrary keyword arguments that are passed to + the constructor of the created mock.""" + if _is_list(spec): + # can't pass a list instance to the mock constructor as it will be + # interpreted as a list of strings + spec = type(spec) + + is_type = isinstance(spec, ClassTypes) + + _kwargs = {'spec': spec} + if spec_set: + _kwargs = {'spec_set': spec} + elif spec is None: + # None we mock with a normal mock without a spec + _kwargs = {} + + _kwargs.update(kwargs) + + Klass = MagicMock + if type(spec) in DescriptorTypes: + # descriptors don't have a spec + # because we don't know what type they return + _kwargs = {} + elif not _callable(spec): + Klass = NonCallableMagicMock + elif is_type and instance and not _instance_callable(spec): + Klass = NonCallableMagicMock + + _new_name = _name + if _parent is None: + # for a top level object no _new_name should be set + _new_name = '' + + mock = Klass(parent=_parent, _new_parent=_parent, _new_name=_new_name, + name=_name, **_kwargs) + + if isinstance(spec, FunctionTypes): + # should only happen at the top level because we don't + # recurse for functions + mock = _set_signature(mock, spec) + else: + _check_signature(spec, mock, is_type, instance) + + if _parent is not None and not instance: + _parent._mock_children[_name] = mock + + if is_type and not instance and 'return_value' not in kwargs: + mock.return_value = create_autospec(spec, spec_set, instance=True, + _name='()', _parent=mock) + + for entry in dir(spec): + if _is_magic(entry): + # MagicMock already does the useful magic methods for us + continue + + if isinstance(spec, FunctionTypes) and entry in FunctionAttributes: + # allow a mock to actually be a function + continue + + # XXXX do we need a better way of getting attributes without + # triggering code execution (?) Probably not - we need the actual + # object to mock it so we would rather trigger a property than mock + # the property descriptor. Likewise we want to mock out dynamically + # provided attributes. + # XXXX what about attributes that raise exceptions other than + # AttributeError on being fetched? + # we could be resilient against it, or catch and propagate the + # exception when the attribute is fetched from the mock + try: + original = getattr(spec, entry) + except AttributeError: + continue + + kwargs = {'spec': original} + if spec_set: + kwargs = {'spec_set': original} + + if not isinstance(original, FunctionTypes): + new = _SpecState(original, spec_set, mock, entry, instance) + mock._mock_children[entry] = new + else: + parent = mock + if isinstance(spec, FunctionTypes): + parent = mock.mock + + new = MagicMock(parent=parent, name=entry, _new_name=entry, + _new_parent=parent, **kwargs) + mock._mock_children[entry] = new + skipfirst = _must_skip(spec, entry, is_type) + _check_signature(original, new, skipfirst=skipfirst) + + # so functions created with _set_signature become instance attributes, + # *plus* their underlying mock exists in _mock_children of the parent + # mock. Adding to _mock_children may be unnecessary where we are also + # setting as an instance attribute? + if isinstance(new, FunctionTypes): + setattr(mock, entry, new) + + return mock + + +def _must_skip(spec, entry, is_type): + if not isinstance(spec, ClassTypes): + if entry in getattr(spec, '__dict__', {}): + # instance attribute - shouldn't skip + return False + spec = spec.__class__ + if not hasattr(spec, '__mro__'): + # old style class: can't have descriptors anyway + return is_type + + for klass in spec.__mro__: + result = klass.__dict__.get(entry, DEFAULT) + if result is DEFAULT: + continue + if isinstance(result, (staticmethod, classmethod)): + return False + return is_type + + # shouldn't get here unless function is a dynamically provided attribute + # XXXX untested behaviour + return is_type + + +def _get_class(obj): + try: + return obj.__class__ + except AttributeError: + # in Python 2, _sre.SRE_Pattern objects have no __class__ + return type(obj) + + +class _SpecState(object): + + def __init__(self, spec, spec_set=False, parent=None, + name=None, ids=None, instance=False): + self.spec = spec + self.ids = ids + self.spec_set = spec_set + self.parent = parent + self.instance = instance + self.name = name + + +FunctionTypes = ( + # python function + type(create_autospec), + # instance method + type(ANY.__eq__), + # unbound method + type(_ANY.__eq__), +) + +FunctionAttributes = set([ + 'func_closure', + 'func_code', + 'func_defaults', + 'func_dict', + 'func_doc', + 'func_globals', + 'func_name', +]) + + +file_spec = None + + +def mock_open(mock=None, read_data=''): + """ + A helper function to create a mock to replace the use of `open`. It works + for `open` called directly or used as a context manager. + + The `mock` argument is the mock object to configure. If `None` (the + default) then a `MagicMock` will be created for you, with the API limited + to methods or attributes available on standard file handles. + + `read_data` is a string for the `read` method of the file handle to return. + This is an empty string by default. + """ + global file_spec + if file_spec is None: + # set on first use + if inPy3k: + import _io + file_spec = list(set(dir(_io.TextIOWrapper)).union(set(dir(_io.BytesIO)))) + else: + file_spec = file + + if mock is None: + mock = MagicMock(name='open', spec=open) + + handle = MagicMock(spec=file_spec) + handle.write.return_value = None + handle.__enter__.return_value = handle + handle.read.return_value = read_data + + mock.return_value = handle + return mock + + +class PropertyMock(Mock): + """ + A mock intended to be used as a property, or other descriptor, on a class. + `PropertyMock` provides `__get__` and `__set__` methods so you can specify + a return value when it is fetched. + + Fetching a `PropertyMock` instance from an object calls the mock, with + no args. Setting it calls the mock with the value being set. + """ + def _get_child_mock(self, **kwargs): + return MagicMock(**kwargs) + + def __get__(self, obj, obj_type): + return self() + def __set__(self, obj, val): + self(val) -- cgit v1.2.3