diff --git a/libwvdrmengine/Android.mk b/libwvdrmengine/Android.mk index cb863389..8666ef0a 100644 --- a/libwvdrmengine/Android.mk +++ b/libwvdrmengine/Android.mk @@ -1,9 +1,63 @@ -# widevine prebuilts only available for ARM -# To build this dir you must define BOARD_WIDEVINE_OEMCRYPTO_LEVEL in the board config. -ifdef BOARD_WIDEVINE_OEMCRYPTO_LEVEL +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) -# TODO: add back in when we have make files that compile. -# include $(call all-subdir-makefiles) +LOCAL_SRC_FILES := \ + src/WVCDMSingleton.cpp \ + src/WVCreatePluginFactories.cpp \ + src/WVCryptoFactory.cpp \ + src/WVDrmFactory.cpp \ + src/WVUUID.cpp \ -endif # BOARD_WIDEVINE_OEMCRYPTO_LEVEL +LOCAL_C_INCLUDES := \ + bionic \ + external/stlport/stlport \ + frameworks/av/include \ + frameworks/native/include \ + vendor/widevine/libwvdrmengine/cdm/core/include \ + vendor/widevine/libwvdrmengine/cdm/include \ + vendor/widevine/libwvdrmengine/include \ + vendor/widevine/libwvdrmengine/mediacrypto/include \ + vendor/widevine/libwvdrmengine/mediadrm/include \ +LOCAL_STATIC_LIBRARIES := \ + libcdm \ + libprotobuf-cpp-2.3.0-lite \ + libwvdrmcryptoplugin \ + libwvdrmdrmplugin \ + +LOCAL_SHARED_LIBRARIES := \ + libdl \ + liblog \ + liboemcrypto \ + libstlport \ + libutils \ + +# CDM's protobuffers are not part of the library +PROTO_SRC_DIR := $(proto_generated_cc_sources_dir)/$(LOCAL_PATH)/core/src + +LOCAL_SRC_FILES += \ + $(PROTO_SRC_DIR)/license_protocol.pb.cc \ + +LOCAL_C_INCLUDES += \ + $(proto_generated_cc_sources_dir)/$(LOCAL_PATH)/core/src \ + external/protobuf/src \ + +LOCAL_ADDITIONAL_DEPENDENCIES += $(proto_generated_headers) + +LOCAL_WHOLE_STATIC_LIBRARIES := \ + license_protocol_protos \ + +# End protobuf section + +LOCAL_MODULE := libwvdrmengine + +LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_SHARED_LIBRARIES)/mediadrm + +LOCAL_MODULE_TAGS := optional + +include $(BUILD_SHARED_LIBRARY) + +include vendor/widevine/libwvdrmengine/oemcrypto/mock/Android.mk +include vendor/widevine/libwvdrmengine/cdm/Android.mk +include vendor/widevine/libwvdrmengine/mediacrypto/Android.mk +include vendor/widevine/libwvdrmengine/mediadrm/Android.mk diff --git a/libwvdrmengine/cdm/Android.mk b/libwvdrmengine/cdm/Android.mk new file mode 100644 index 00000000..97f6b4a3 --- /dev/null +++ b/libwvdrmengine/cdm/Android.mk @@ -0,0 +1,63 @@ +LOCAL_PATH := $(call my-dir) + +# ---------------------------------------------------------------- +# Builds the protobuf static library and generate .pb.cc and .pb.h +# license_protocol.pb.cc +# license_protocol.pb.h +# license_protocol.a +# +include $(CLEAR_VARS) +LOCAL_MODULE := license_protocol_protos +LOCAL_MODULE_CLASS := STATIC_LIBRARIES + +LOCAL_C_INCLUDES := \ + bionic \ + external/stlport/stlport + +LOCAL_SRC_FILES := \ + $(call all-proto-files-under, core/src) + +include $(BUILD_STATIC_LIBRARY) + +# ---------------------------------------------------------------- +# Builds libcdm.a +# +include $(CLEAR_VARS) +LOCAL_C_INCLUDES := \ + bionic \ + external/stlport/stlport \ + vendor/widevine/libwvdrmengine/cdm/core/include \ + vendor/widevine/libwvdrmengine/cdm/include \ + vendor/widevine/libwvdrmengine/oemcrypto/include + +# Add protocol buffer generated headers +# +LOCAL_C_INCLUDES += \ + $(proto_generated_cc_sources_dir)/$(LOCAL_PATH)/core/src \ + external/protobuf/src \ + ../oemcrypto/include + +LOCAL_ADDITIONAL_DEPENDENCIES += $(proto_generated_headers) + +SRC_DIR := src +CORE_SRC_DIR := core/src +LOCAL_SRC_FILES := \ + $(CORE_SRC_DIR)/buffer_reader.cpp \ + $(CORE_SRC_DIR)/cdm_engine.cpp \ + $(CORE_SRC_DIR)/cdm_session.cpp \ + $(CORE_SRC_DIR)/crypto_engine.cpp \ + $(CORE_SRC_DIR)/crypto_session.cpp \ + $(CORE_SRC_DIR)/license.cpp \ + $(CORE_SRC_DIR)/policy_engine.cpp \ + $(CORE_SRC_DIR)/string_conversions.cpp \ + $(SRC_DIR)/clock.cpp \ + $(SRC_DIR)/lock.cpp \ + $(SRC_DIR)/log.cpp \ + $(SRC_DIR)/timer.cpp \ + $(SRC_DIR)/wv_content_decryption_module.cpp \ + +LOCAL_MODULE := libcdm +LOCAL_MODULE_TAGS := optional + +include $(BUILD_STATIC_LIBRARY) + diff --git a/libwvdrmengine/cdm/TODO b/libwvdrmengine/cdm/TODO deleted file mode 100644 index d4292a81..00000000 --- a/libwvdrmengine/cdm/TODO +++ /dev/null @@ -1,2 +0,0 @@ -Rahul and Edwin fill this out -Rahul and Edwin write tests diff --git a/libwvdrmengine/cdm/core/include/buffer_reader.h b/libwvdrmengine/cdm/core/include/buffer_reader.h new file mode 100644 index 00000000..9d06a9ab --- /dev/null +++ b/libwvdrmengine/cdm/core/include/buffer_reader.h @@ -0,0 +1,68 @@ +// Copyright 2012 Google Inc. All Rights Reserved. + +#ifndef CDM_BASE_BUFFER_READER_H_ +#define CDM_BASE_BUFFER_READER_H_ + +#include +#include +#include + +#include "wv_cdm_types.h" + +namespace wvcdm { + +// Annotate a function indicating the caller must examine the return value. +// Use like: +// int foo() WARN_UNUSED_RESULT; +// To explicitly ignore a result, see |ignore_result()| in . +#if defined(COMPILER_GCC) +#define WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +#else +#define WARN_UNUSED_RESULT +#endif + +class BufferReader { + public: + BufferReader(const uint8_t* buf, size_t size) + : buf_(buf), size_(size), pos_(0) {} + + bool HasBytes(int count) { return (pos() + count <= size()); } + + // Read a value from the stream, performing endian correction, + // and advance the stream pointer. + bool Read1(uint8_t* v) WARN_UNUSED_RESULT; + bool Read2(uint16_t* v) WARN_UNUSED_RESULT; + bool Read2s(int16_t* v) WARN_UNUSED_RESULT; + bool Read4(uint32_t* v) WARN_UNUSED_RESULT; + bool Read4s(int32_t* v) WARN_UNUSED_RESULT; + bool Read8(uint64_t* v) WARN_UNUSED_RESULT; + bool Read8s(int64_t* v) WARN_UNUSED_RESULT; + + bool ReadString(std::string* str, int count) WARN_UNUSED_RESULT; + bool ReadVec(std::vector* t, int count) WARN_UNUSED_RESULT; + + // These variants read a 4-byte integer of the corresponding signedness and + // store it in the 8-byte return type. + bool Read4Into8(uint64_t* v) WARN_UNUSED_RESULT; + bool Read4sInto8s(int64_t* v) WARN_UNUSED_RESULT; + + // Advance the stream by this many bytes. + bool SkipBytes(int nbytes) WARN_UNUSED_RESULT; + + const uint8_t* data() const { return buf_; } + size_t size() const { return size_; } + size_t pos() const { return pos_; } + + protected: + const uint8_t* buf_; + size_t size_; + size_t pos_; + + template bool Read(T* t) WARN_UNUSED_RESULT; + + CORE_DISALLOW_COPY_AND_ASSIGN(BufferReader); +}; + +} // namespace wvcdm + +#endif // CDM_BASE_BUFFER_READER_H_ diff --git a/libwvdrmengine/cdm/core/include/cdm_engine.h b/libwvdrmengine/cdm/core/include/cdm_engine.h new file mode 100644 index 00000000..8bd58e18 --- /dev/null +++ b/libwvdrmengine/cdm/core/include/cdm_engine.h @@ -0,0 +1,123 @@ +// Copyright 2013 Google Inc. All Rights Reserved. + +#ifndef CDM_BASE_CDM_ENGINE_H_ +#define CDM_BASE_CDM_ENGINE_H_ + +#include "timer.h" +#include "wv_cdm_types.h" + +namespace wvcdm { + +class CdmSession; +class WvCdmEventListener; + +typedef std::map CdmSessionMap; + +class CdmEngine : public TimerHandler { + public: + CdmEngine() {} + ~CdmEngine(); + + // Session related methods + CdmResponseType OpenSession(const CdmKeySystem& key_system, + CdmSessionId* session_id); + CdmResponseType CloseSession(CdmSessionId& session_id); + + // License related methods + // Construct a valid license request + CdmResponseType GenerateKeyRequest(const CdmSessionId& session_id, + bool is_key_system_present, + const CdmKeySystem& key_system, + const CdmInitData& init_data, + const CdmLicenseType license_type, + CdmNameValueMap& app_parameters, + CdmKeyMessage* key_request); + + // Accept license response and extract key info. + CdmResponseType AddKey(const CdmSessionId& session_id, + bool is_key_system_init_data_present, + const CdmKeySystem& key_system, + const CdmInitData& init_data, + const CdmKeyResponse& key_data); + + // Cancel session and unload keys. + CdmResponseType CancelKeyRequest(const CdmSessionId& session_id, + bool is_key_system_present, + const CdmKeySystem& key_system); + + // Construct valid renewal request for the current session keys. + CdmResponseType GenerateRenewalRequest(const CdmSessionId& session_id, + bool is_key_system_init_data_present, + const CdmKeySystem& key_system, + const CdmInitData& init_data, + CdmKeyMessage* key_request); + + // Accept renewal response and update key info. + CdmResponseType RenewKey(const CdmSessionId& session_id, + bool is_key_system_init_data_present, + const CdmKeySystem& key_system, + const CdmInitData& init_data, + const CdmKeyResponse& key_data); + + // Query license information + CdmResponseType QueryKeyStatus(const CdmSessionId& session_id, + CdmNameValueMap* key_info); + + + // Provisioning related methods + CdmResponseType GetProvisioningRequest(CdmProvisioningRequest* request, + std::string* default_url); + + CdmResponseType HandleProvisioningResponse(CdmProvisioningResponse& response); + + // Secure stop related methods + CdmResponseType GetSecureStops(CdmSecureStops* secure_stops); + CdmResponseType ReleaseSecureStops(const CdmSecureStopReleaseMessage& message); + + // Decryption and key related methods + // Accept encrypted buffer and return decrypted data. + CdmResponseType Decrypt(const CdmSessionId& session_id, + bool is_encrypted, + const KeyId& key_id, + const uint8_t* encrypted_buffer, + size_t encrypted_size, + const std::vector& iv, + size_t block_offset, + void* decrypted_buffer); + + // Is the key known to any session? + bool IsKeyValid(const KeyId& key_id); + + // Event listener related methods + bool AttachEventListener(CdmSessionId& session_id, + WvCdmEventListener* listener); + bool DetachEventListener(CdmSessionId& session_id, + WvCdmEventListener* listener); + private: + // private methods + bool ValidateKeySystem(const CdmKeySystem& key_system); + // Cancel all sessions + bool CancelSessions(); + // Parse a blob of multiple concatenated PSSH atoms to extract the first + // widevine pssh + // TODO(gmorgan): This should be done by the user of this class. + bool ExtractWidevinePssh(const CdmInitData& init_data, + CdmInitData* output); + + // timer related methods to drive policy decisions + void EnablePolicyTimer(); + void DisablePolicyTimer(); + virtual void OnTimerEvent(); + + // instance variables + CdmSessionMap sessions_; + + // policy timer + Timer policy_timer_; + + CORE_DISALLOW_COPY_AND_ASSIGN(CdmEngine); +}; + +} // namespace wvcdm + +#endif // CDM_BASE_CDM_ENGINE_H_ diff --git a/libwvdrmengine/cdm/core/include/cdm_session.h b/libwvdrmengine/cdm/core/include/cdm_session.h new file mode 100644 index 00000000..4f950089 --- /dev/null +++ b/libwvdrmengine/cdm/core/include/cdm_session.h @@ -0,0 +1,100 @@ +// Copyright 2012 Google Inc. All Rights Reserved. + +#ifndef CDM_BASE_CDM_SESSION_H_ +#define CDM_BASE_CDM_SESSION_H_ + +#include + +#include "crypto_session.h" +#include "license.h" +#include "policy_engine.h" +#include "wv_cdm_event_listener.h" +#include "wv_cdm_types.h" + +namespace wvcdm { + +// TODO(kqyang): Do we need it? CdmKey not defined yet +// typedef std::map CdmSessionKeys; + +class CdmSession { + public: + CdmSession() : state_(INITIAL), session_id_(GenerateSessionId()) {} + ~CdmSession() {} + + bool Init(); + + bool DestroySession(); + + void set_key_system(const CdmKeySystem& ksystem) { key_system_ = ksystem; } + const CdmKeySystem& key_system() { return key_system_; } + + const CdmSessionId& session_id() { return session_id_; } + + bool VerifySession(const CdmKeySystem& key_system, + const CdmInitData& init_data); + + CdmResponseType GenerateKeyRequest(const CdmInitData& init_data, + CdmKeyMessage* key_request); + + // AddKey() - Accept license response and extract key info. + CdmResponseType AddKey(const CdmKeyResponse& key_response); + + // CancelKeyRequest() - Cancel session. + CdmResponseType CancelKeyRequest(); + + // Decrypt() - Accept encrypted buffer and return decrypted data. + CdmResponseType Decrypt(const uint8_t* encrypted_buffer, + size_t encrypted_size, + size_t block_offset, + const std::string& iv, + const KeyId& key_id, + uint8_t* decrypted_buffer); + + // License renewal + // GenerateRenewalRequest() - Construct valid renewal request for the current + // session keys. + CdmResponseType GenerateRenewalRequest(CdmKeyMessage* key_request); + + // RenewKey() - Accept renewal response and update key info. + CdmResponseType RenewKey(const CdmKeyResponse& key_response); + + bool IsKeyValid(const KeyId& key_id); + + bool AttachEventListener(WvCdmEventListener* listener); + bool DetachEventListener(WvCdmEventListener* listener); + + void OnTimerEvent(); + + private: + + // Generate unique ID for each new session. + CdmSessionId GenerateSessionId(); + + typedef enum { + INITIAL, + LICENSE_REQUESTED, + LICENSE_RESPONSE_DONE, + RENEWAL_ENABLED, + RENEWAL_REQUESTED, + LICENSE_EXPIRED + } CdmSessionState; + + // instance variables + CdmSessionState state_; + const CdmSessionId session_id_; + CdmKeySystem key_system_; + CdmLicense license_parser_; + CryptoSession* crypto_session_; + PolicyEngine policy_engine_; + + std::set listeners_; + + // TODO(kqyang): CdmKey not defined yet + // CdmSessionKeys session_keys_; + + CORE_DISALLOW_COPY_AND_ASSIGN(CdmSession); +}; + +} // namespace wvcdm + +#endif // CDM_BASE_CDM_SESSION_H_ diff --git a/libwvdrmengine/cdm/core/include/clock.h b/libwvdrmengine/cdm/core/include/clock.h new file mode 100644 index 00000000..0fb0a379 --- /dev/null +++ b/libwvdrmengine/cdm/core/include/clock.h @@ -0,0 +1,19 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Clock - Platform independent interface for a time library +// +#ifndef CDM_BASE_CLOCK_H_ +#define CDM_BASE_CLOCK_H_ + +#include + +namespace wvcdm { + +// Provides time related information. The implementation is platform dependent. + +// Provides the number of seconds since an epoch (00:00 hours, Jan 1, 1970 UTC) +int64_t GetCurrentTime(); + +}; // namespace wvcdm + +#endif // CDM_BASE_CLOCK_H_ diff --git a/libwvdrmengine/cdm/core/include/crypto_engine.h b/libwvdrmengine/cdm/core/include/crypto_engine.h new file mode 100644 index 00000000..930a7f88 --- /dev/null +++ b/libwvdrmengine/cdm/core/include/crypto_engine.h @@ -0,0 +1,61 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// OEMCrypto Client - wrapper class for C-style OEMCrypto interface +// +#ifndef CDM_BASE_CRYPTO_ENGINE_H_ +#define CDM_BASE_CRYPTO_ENGINE_H_ + +#include "crypto_session.h" +#include "lock.h" +#include "wv_cdm_types.h" + +namespace wvcdm { + +typedef std::map CryptoSessionMap; + +class CryptoEngine { + + friend class CryptoSession; + + private: + + CryptoEngine(); + ~CryptoEngine(); + + public: + + // get an instance of Crypto engine + static CryptoEngine* GetInstance(); + + bool Init(); + bool Terminate(); + bool ValidateKeybox(); + + CryptoSession* CreateSession(const CdmSessionId& session_id); + CryptoSession* FindSession(const CdmSessionId& session_id); + bool DestroySession(const CdmSessionId& session_id); + bool DestroySessions(); + + bool GetToken(std::string* token); + +private: + + void DeleteInstance(); + static CryptoEngine* CreateSingleton(); + + CryptoSession* FindSessionInternal(const CdmSessionId& session_id); + + static CryptoEngine* crypto_engine_; + static Lock crypto_engine_lock_; + + bool initialized_; + mutable Lock crypto_lock_; + mutable Lock sessions_lock_; + CryptoSessionMap sessions_; + + CORE_DISALLOW_COPY_AND_ASSIGN(CryptoEngine); +}; + +}; // namespace wvcdm + +#endif // CDM_BASE_CRYPTO_ENGINE_H_ diff --git a/libwvdrmengine/cdm/core/include/crypto_key.h b/libwvdrmengine/cdm/core/include/crypto_key.h new file mode 100644 index 00000000..40d83b1d --- /dev/null +++ b/libwvdrmengine/cdm/core/include/crypto_key.h @@ -0,0 +1,44 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// OEMCrypto Client - wrapper class for C-style OEMCrypto interface +// +#ifndef CDM_BASE_CRYPTO_KEY_H_ +#define CDM_BASE_CRYPTO_KEY_H_ + +#include "wv_cdm_types.h" + +namespace wvcdm { + +class CryptoKey { +public: + CryptoKey() {}; + ~CryptoKey() {}; + + const std::string& key_id() const { return key_id_; } + const std::string& key_data() const { return key_data_; } + const std::string& key_data_iv() const { return key_data_iv_; } + const std::string& key_control() const { return key_control_; } + const std::string& key_control_iv() const { return key_control_iv_; } + void set_key_id(const std::string& key_id) { key_id_ = key_id; } + void set_key_data(const std::string& key_data) { key_data_ = key_data; } + void set_key_data_iv(const std::string& iv) { key_data_iv_ = iv; } + void set_key_control(const std::string& ctl) { key_control_ = ctl; } + void set_key_control_iv(const std::string& ctl_iv) { + key_control_iv_ = ctl_iv; + } + + bool HasKeyControl() const { return key_control_.size() >= 16; } + +private: + std::string key_id_; + std::string key_data_iv_; + std::string key_data_; + std::string key_control_; + std::string key_control_iv_; + + CORE_DISALLOW_COPY_AND_ASSIGN(CryptoKey); +}; + +}; // namespace wvcdm + +#endif // CDM_BASE_CRYPTO_KEY_H_ diff --git a/libwvdrmengine/cdm/core/include/crypto_session.h b/libwvdrmengine/cdm/core/include/crypto_session.h new file mode 100644 index 00000000..c4b8e210 --- /dev/null +++ b/libwvdrmengine/cdm/core/include/crypto_session.h @@ -0,0 +1,81 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// OEMCrypto Client - wrapper class for C-style OEMCrypto interface +// +#ifndef CDM_BASE_CRYPTO_SESSSION_H_ +#define CDM_BASE_CRYPTO_SESSSION_H_ + +#include +#include + +#include "crypto_key.h" +#include "wv_cdm_types.h" + +namespace wvcdm { + +typedef std::map CryptoKeyMap; + +// TODO(gmorgan): fill out input and output descriptors +typedef void* InputDescriptor; +typedef void* OutputDescriptor; + +class CryptoSession { + public: + CryptoSession(); + explicit CryptoSession(const std::string& sname); + ~CryptoSession(); + + bool Open(); + void Close(); + + bool IsValid() { return valid_; } + bool IsOpen() { return open_; } + bool SuccessStatus(); + CryptoResult session_status() { return session_status_; } + CryptoSessionId oec_session_id() { return oec_session_id_; } + CdmSessionId cdm_session_id() { return cdm_session_id_; } + + // Key request/response + void GenerateRequestId(std::string& req_id_str); + bool PrepareRequest(const std::string& key_deriv_message, + std::string* signature); + bool PrepareRenewalRequest(const std::string& message, + std::string* signature); + bool LoadKeys(const std::string& message, + const std::string& signature, + const std::string& mac_key_iv, + const std::string& mac_key, + int num_keys, + const CryptoKey* key_array); + bool RefreshKeys(const std::string& message, + const std::string& signature, + int num_keys, + const CryptoKey* key_array); + bool GenerateNonce(uint32_t* nonce); + + // Media data path + bool SelectKey(const std::string& key_id); + bool Decrypt(const InputDescriptor input, OutputDescriptor output); + + private: + + void GenerateMacContext(const std::string& input_context, + std::string* deriv_context); + void GenerateEncryptContext(const std::string& input_context, + std::string* deriv_context); + size_t GetOffset(std::string message, std::string field); + + bool valid_; + bool open_; + CdmSessionId cdm_session_id_; + CryptoSessionId oec_session_id_; + CryptoResult session_status_; + + CryptoKeyMap keys_; + + CORE_DISALLOW_COPY_AND_ASSIGN(CryptoSession); +}; + +}; // namespace wvcdm + +#endif // CDM_BASE_CRYPTO_SESSSION_H_ diff --git a/libwvdrmengine/cdm/core/include/license.h b/libwvdrmengine/cdm/core/include/license.h new file mode 100644 index 00000000..ee10efff --- /dev/null +++ b/libwvdrmengine/cdm/core/include/license.h @@ -0,0 +1,41 @@ +// Copyright 2012 Google Inc. All Rights Reserved. + +#ifndef CDM_BASE_LICENSE_H_ +#define CDM_BASE_LICENSE_H_ + +#include "license_protocol.pb.h" +#include "wv_cdm_types.h" + +namespace wvcdm { + +using video_widevine_server::sdk::LicenseIdentification; + +class CryptoSession; + +class CdmLicense { + + public: + + CdmLicense(); + ~CdmLicense(); + + bool Init(const std::string& token, CryptoSession* session); + + bool PrepareKeyRequest(const CdmInitData& init_data, + CdmKeyMessage* signed_request); + bool PrepareKeyRenewalRequest(CdmKeyMessage* signed_request); + bool HandleKeyResponse(const CdmKeyResponse& license_response); + bool HandleKeyRenewalResponse(const CdmKeyResponse& license_response); + +private: + + LicenseIdentification license_id_; + CryptoSession* session_; + std::string token_; + + CORE_DISALLOW_COPY_AND_ASSIGN(CdmLicense); +}; + +} // namespace wvcdm + +#endif // CDM_BASE_LICENSE_H_ diff --git a/libwvdrmengine/cdm/core/include/lock.h b/libwvdrmengine/cdm/core/include/lock.h new file mode 100644 index 00000000..d9f43efd --- /dev/null +++ b/libwvdrmengine/cdm/core/include/lock.h @@ -0,0 +1,54 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Lock - Platform independent interface for a Mutex class +// +#ifndef CDM_BASE_LOCK_H_ +#define CDM_BASE_LOCK_H_ + +#include "wv_cdm_types.h" + +namespace wvcdm { + +// Simple lock class. The implementation is platform dependent. +// +// The lock must be unlocked by the thread that locked it. +// The lock is also not recursive (ie. cannot be taken multiple times). +class Lock { + public: + Lock(); + ~Lock(); + + void Acquire(); + void Release(); + + // Acquires a lock if not held and returns true. + // Returns false if the lock is held by another thread. + bool Try(); + + friend class AutoLock; + + private: + class Impl; + Impl *impl_; + + CORE_DISALLOW_COPY_AND_ASSIGN(Lock); +}; + +// Manages the lock automatically. It will be locked when AutoLock +// is constructed and release when AutoLock goes out of scope +class AutoLock { + public: + explicit AutoLock(Lock& lock); + explicit AutoLock(Lock* lock); + ~AutoLock(); + + private: + class Impl; + Impl *impl_; + + CORE_DISALLOW_COPY_AND_ASSIGN(AutoLock); +}; + +}; // namespace wvcdm + +#endif // CDM_BASE_LOCK_H_ diff --git a/libwvdrmengine/cdm/core/include/log.h b/libwvdrmengine/cdm/core/include/log.h new file mode 100644 index 00000000..db66d2ab --- /dev/null +++ b/libwvdrmengine/cdm/core/include/log.h @@ -0,0 +1,31 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Log - Platform independent interface for a Logging class +// +#ifndef CDM_BASE_LOG_H_ +#define CDM_BASE_LOG_H_ + +namespace wvcdm { + +// Simple logging class. The implementation is platform dependent. + +typedef enum { + LOG_ERROR, + LOG_WARN, + LOG_INFO, + LOG_DEBUG, + LOG_VERBOSE +} LogPriority; + +void log_write(LogPriority priority, const char *fmt, ...); + +// Log APIs +#define LOGE(...) ((void)log_write(wvcdm::LOG_ERROR, __VA_ARGS__)) +#define LOGW(...) ((void)log_write(wvcdm::LOG_WARN, __VA_ARGS__)) +#define LOGI(...) ((void)log_write(wvcdm::LOG_INFO, __VA_ARGS__)) +#define LOGD(...) ((void)log_write(wvcdm::LOG_DEBUG, __VA_ARGS__)) +#define LOGV(...) ((void)log_write(wvcdm::LOG_VERBOSE, __VA_ARGS__)) + +}; // namespace wvcdm + +#endif // CDM_BASE_LOG_H_ diff --git a/libwvdrmengine/cdm/core/include/policy_engine.h b/libwvdrmengine/cdm/core/include/policy_engine.h new file mode 100644 index 00000000..2627d674 --- /dev/null +++ b/libwvdrmengine/cdm/core/include/policy_engine.h @@ -0,0 +1,83 @@ +// Copyright 2013 Google Inc. All Rights Reserved. + +#ifndef CDM_BASE_POLICY_ENGINE_H_ +#define CDM_BASE_POLICY_ENGINE_H_ + +#include + +#include "license_protocol.pb.h" +#include "wv_cdm_types.h" + +namespace wvcdm { + +// This acts as an oracle that basically says "Yes(true) you may still decrypt +// or no(false) you may not decrypt this data anymore." +class PolicyEngine { + public: + PolicyEngine(); + ~PolicyEngine(); + + // |current_time| is used to check if license has to be renewed or expired. + void OnTimerEvent(int64_t current_time, bool event_occurred, CdmEventType& event); + + // SetLicense is used in handling the initial license response. It stores + // an exact copy of the policy information stored in the license. + // The license state transitions to kLicenseStateCanPlay if the license + // permits playback. + void SetLicense(const video_widevine_server::sdk::License& license); + + // UpdateLicense is used in handling a license response for a renewal request. + // The response may only contain any policy fields that have changed. In this + // case an exact copy is not what we want to happen. We also will receive an + // updated license_start_time from the server. The license will transition to + // kLicenseStateCanPlay if the license permits playback. + void UpdateLicense(const video_widevine_server::sdk::License& license); + + const video_widevine_server::sdk::LicenseIdentification& license_id() { + return license_id_; + } + + private: + typedef enum { + kLicenseStateInitial, + kLicenseStateCanPlay, + kLicenseStateCannotPlay, + kLicenseStateNeedRenewal, + kLicenseStateWaitingLicenseUpdate, + kLicenseStateExpired + } LicenseState; + + bool IsLicenseDurationExpired(int64_t current_time); + bool IsRenewalDelayExpired(int64_t current_time); + bool IsRenewalRecoveryDurationExpired(int64_t current_time); + bool IsRenewalRetryIntervalExpired(int64_t current_time); + + void UpdateRenewalRequest(int64_t current_time); + + LicenseState license_state_; + + // This is the current policy information for this license. This gets updated + // as license renewals occur. + video_widevine_server::sdk::License::Policy policy_; + + // This is the license id field from server response. This data gets passed + // back to the server in each renewal request. When we get a renewal response + // from the license server we will get an updated id field. + video_widevine_server::sdk::LicenseIdentification license_id_; + + // This is the license start time that gets sent from the server in each + // license request or renewal. + int64_t license_start_time_; + + // This is used as a reference point for policy management. This value + // represents an offset from license_start_time_. This is used to calculate + // the time where renewal retries should occur. + int64_t next_renewal_time_; + int64_t policy_max_duration_seconds_; + + CORE_DISALLOW_COPY_AND_ASSIGN(PolicyEngine); +}; +} // wvcdm + +#endif // CDM_BASE_POLICY_ENGINE_H_ + diff --git a/libwvdrmengine/cdm/core/include/string_conversions.h b/libwvdrmengine/cdm/core/include/string_conversions.h new file mode 100644 index 00000000..65579519 --- /dev/null +++ b/libwvdrmengine/cdm/core/include/string_conversions.h @@ -0,0 +1,25 @@ +// Copyright 2013 Google Inc. All Rights Reserved. + +#ifndef CDM_BASE_STRING_CONVERSIONS_H_ +#define CDM_BASE_STRING_CONVERSIONS_H_ + +#include +#include +#include +#include + +namespace wvcdm { + +std::vector a2b_hex(const std::string& b); +std::string a2bs_hex(const std::string& b); +std::string b2a_hex(const std::vector& b); +std::string b2a_hex(const std::string& b); +std::string Base64SafeEncode(const std::vector& bin_input); +std::vector Base64SafeDecode(const std::string& bin_input); +std::string HexEncode(const uint8_t* bytes, unsigned size); +std::string IntToString(int value); +std::string UintToString(unsigned int value); + +}; // namespace wvcdm + +#endif // CDM_BASE_STRING_CONVERSIONS_H_ diff --git a/libwvdrmengine/cdm/core/include/timer.h b/libwvdrmengine/cdm/core/include/timer.h new file mode 100644 index 00000000..4baa2330 --- /dev/null +++ b/libwvdrmengine/cdm/core/include/timer.h @@ -0,0 +1,50 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Timer - Platform independent interface for a Timer class +// +#ifndef CDM_BASE_TIMER_H_ +#define CDM_BASE_TIMER_H_ + +#include "wv_cdm_types.h" + +namespace wvcdm { + +// Timer Handler class. +// +// Derive from this class if you wish to receive events when the timer +// expires. Provide the handler when setting up a new Timer. + +class TimerHandler { + public: + TimerHandler() {}; + virtual ~TimerHandler() {}; + + virtual void OnTimerEvent() = 0; +}; + +// Timer class. The implementation is platform dependent. +// +// This class provides a simple recurring timer API. The class receiving +// timer expiry events should derive from TimerHandler. +// Specify the receiver class and the periodicty of timer events when +// the timer is initiated by calling Start. + +class Timer { + public: + Timer(); + ~Timer(); + + void Start(TimerHandler *handler, uint32_t time_in_secs); + void Stop(); + bool IsRunning(); + + private: + class Impl; + Impl *impl_; + + CORE_DISALLOW_COPY_AND_ASSIGN(Timer); +}; + +}; // namespace wvcdm + +#endif // CDM_BASE_TIMER_H_ diff --git a/libwvdrmengine/cdm/core/include/wv_cdm_constants.h b/libwvdrmengine/cdm/core/include/wv_cdm_constants.h new file mode 100644 index 00000000..8174602a --- /dev/null +++ b/libwvdrmengine/cdm/core/include/wv_cdm_constants.h @@ -0,0 +1,18 @@ +// Copyright 2012 Google Inc. All Rights Reserved. + +#ifndef CDM_BASE_WV_CDM_CONSTANTS_H_ +#define CDM_BASE_WV_CDM_CONSTANTS_H_ + +namespace wvcdm { +static const size_t KEY_CONTROL_SIZE = 16; +// TODO(kqyang): Key ID size is not fixed in spec, but conventionally we +// always use 16 bytes key id. We'll need to update oemcrypto to support +// variable size key id. +static const size_t KEY_ID_SIZE = 16; +static const size_t KEY_IV_SIZE = 16; +static const size_t KEY_PAD_SIZE = 16; +static const size_t KEY_SIZE = 16; +static const size_t MAC_KEY_SIZE = 32; +} // namespace wvcdm + +#endif // CDM_BASE_WV_CDM_CONSTANTS_H_ diff --git a/libwvdrmengine/cdm/core/include/wv_cdm_event_listener.h b/libwvdrmengine/cdm/core/include/wv_cdm_event_listener.h new file mode 100644 index 00000000..b9bfb476 --- /dev/null +++ b/libwvdrmengine/cdm/core/include/wv_cdm_event_listener.h @@ -0,0 +1,28 @@ +// Copyright 2012 Google Inc. All Rights Reserved. + +#ifndef CDM_BASE_WV_CDM_EVENT_LISTENER_H_ +#define CDM_BASE_WV_CDM_EVENT_LISTENER_H_ + +#include "wv_cdm_types.h" + +namespace wvcdm { + +// Listener for events from the Content Decryption Module. +// The caller of the CDM API must provide an implementation for onEvent +// and signal its intent by using the Attach/DetachEventListener methods +// in the WvContentDecryptionModule class. +class WvCdmEventListener { + public: + WvCdmEventListener() {} + virtual ~WvCdmEventListener() {} + + virtual void onEvent(const CdmSessionId& session_id, + CdmEventType cdm_event) = 0; + + private: + CORE_DISALLOW_COPY_AND_ASSIGN(WvCdmEventListener); +}; + +} // namespace wvcdm + +#endif // CDM_BASE_WV_CDM_EVENT_LISTENER_H_ diff --git a/libwvdrmengine/cdm/core/include/wv_cdm_types.h b/libwvdrmengine/cdm/core/include/wv_cdm_types.h new file mode 100644 index 00000000..f7a57ba9 --- /dev/null +++ b/libwvdrmengine/cdm/core/include/wv_cdm_types.h @@ -0,0 +1,60 @@ +// Copyright 2013 Google Inc. All Rights Reserved. + +#ifndef CDM_BASE_WV_CDM_TYPES_H_ +#define CDM_BASE_WV_CDM_TYPES_H_ + +#include +#include +#include +#include + +namespace wvcdm { + +typedef std::string CdmKeySystem; +typedef std::string CdmInitData; +typedef std::string CdmKeyMessage; +typedef std::string CdmKeyResponse; +typedef std::string KeyId; +typedef std::string CdmSessionId; +typedef std::string RequestId; +typedef uint32_t CryptoResult; +typedef uint32_t CryptoSessionId; +typedef std::string CryptoKeyId; +typedef std::map CdmNameValueMap; +typedef std::vector CdmSecureStops; +typedef std::vector CdmSecureStopReleaseMessage; +typedef std::string CdmProvisioningRequest; +typedef std::string CdmProvisioningResponse; + +enum CdmResponseType { + NO_ERROR, + UNKNOWN_ERROR, + KEY_ADDED, + KEY_ERROR, + KEY_MESSAGE, + NEED_KEY, + KEY_CANCELED, +}; + +#define CORE_DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) + +enum CdmEventType { + LICENSE_EXPIRED_EVENT, + LICENSE_RENEWAL_NEEDED_EVENT +}; + +enum CdmLicenseType { + kLicenseTypeOffline, + kLicenseTypeStreaming +}; + +// forward class references +class KeyMessage; +class Request; +class Key; + +} // namespace wvcdm + +#endif // CDM_BASE_WV_CDM_TYPES_H_ diff --git a/libwvdrmengine/cdm/core/src/buffer_reader.cpp b/libwvdrmengine/cdm/core/src/buffer_reader.cpp new file mode 100644 index 00000000..8890a31d --- /dev/null +++ b/libwvdrmengine/cdm/core/src/buffer_reader.cpp @@ -0,0 +1,94 @@ +// Copyright 2012 Google Inc. All Rights Reserved. + +#include "buffer_reader.h" + +#include "log.h" + +namespace wvcdm { + +bool BufferReader::Read1(uint8_t* v) { + if (!HasBytes(1)) { + LOGE("BufferReader::Read1 : Failure while parsing: Not enough bytes (1)"); + return false; + } + + *v = buf_[pos_++]; + return true; +} + +// Internal implementation of multi-byte reads +template bool BufferReader::Read(T* v) { + if (!HasBytes(sizeof(T))) { + LOGE("BufferReader::Read : Failure while parsing: Not enough bytes (%u)", sizeof(T)); + return false; + } + + T tmp = 0; + for (size_t i = 0; i < sizeof(T); i++) { + tmp <<= 8; + tmp += buf_[pos_++]; + } + *v = tmp; + return true; +} + +bool BufferReader::Read2(uint16_t* v) { return Read(v); } +bool BufferReader::Read2s(int16_t* v) { return Read(v); } +bool BufferReader::Read4(uint32_t* v) { return Read(v); } +bool BufferReader::Read4s(int32_t* v) { return Read(v); } +bool BufferReader::Read8(uint64_t* v) { return Read(v); } +bool BufferReader::Read8s(int64_t* v) { return Read(v); } + +bool BufferReader::ReadString(std::string* str, int count) { + if (!HasBytes(count)) { + LOGE("BufferReader::ReadString : Failure while parsing: Not enough bytes (%d)", count); + return false; + } + + str->assign(buf_ + pos_, buf_ + pos_ + count); + pos_ += count; + return true; +} + +bool BufferReader::ReadVec(std::vector* vec, int count) { + if (!HasBytes(count)) { + LOGE("BufferReader::ReadVec : Failure while parsing: Not enough bytes (%d)", count); + return false; + } + + vec->clear(); + vec->insert(vec->end(), buf_ + pos_, buf_ + pos_ + count); + pos_ += count; + return true; +} + +bool BufferReader::SkipBytes(int bytes) { + if (!HasBytes(bytes)) { + LOGE("BufferReader::SkipBytes : Failure while parsing: Not enough bytes (%d)", bytes); + return false; + } + + pos_ += bytes; + return true; +} + +bool BufferReader::Read4Into8(uint64_t* v) { + uint32_t tmp; + if (!Read4(&tmp)) { + return false; + } + *v = tmp; + return true; +} + +bool BufferReader::Read4sInto8s(int64_t* v) { + // Beware of the need for sign extension. + int32_t tmp; + if (!Read4s(&tmp)) { + return false; + } + *v = tmp; + return true; +} + +} // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/src/cdm_engine.cpp b/libwvdrmengine/cdm/core/src/cdm_engine.cpp new file mode 100644 index 00000000..fb4726a5 --- /dev/null +++ b/libwvdrmengine/cdm/core/src/cdm_engine.cpp @@ -0,0 +1,463 @@ +// Copyright 2013 Google Inc. All Rights Reserved. + +#include "cdm_engine.h" + +#include + +#include "buffer_reader.h" +#include "cdm_session.h" +#include "log.h" +#include "wv_cdm_constants.h" +#include "wv_cdm_event_listener.h" + +#ifndef CDM_POLICY_TIMER_DURATION_SECONDS +#define CDM_POLICY_TIMER_DURATION_SECONDS 1 +#endif + +namespace wvcdm { + +typedef std::map::iterator CdmSessionIter; + +CdmEngine::~CdmEngine() { + CancelSessions(); + + CdmSessionMap::iterator i(sessions_.begin()); + for (; i != sessions_.end(); ++i) + delete i->second; + sessions_.clear(); +} + +CdmResponseType CdmEngine::OpenSession( + const CdmKeySystem& key_system, + CdmSessionId* session_id) { + LOGI("CdmEngine::OpenSession"); + + if (!ValidateKeySystem(key_system)) { + LOGI("CdmEngine::OpenSession: invalid key_system = %s", key_system.c_str()); + return KEY_ERROR; + } + + if (!session_id) { + LOGE("CdmEngine::OpenSession: no session ID destination provided"); + return KEY_ERROR; + } + + // TODO(edwinwong, rfrias): Save key_system in session for validation checks + CdmSession* new_session = new CdmSession(); + if (!new_session) { + return KEY_ERROR; + } + + if (new_session->session_id().empty()) { + LOGE("CdmEngine::OpenSession: failure to generate session ID"); + delete(new_session); + return UNKNOWN_ERROR; + } + + CdmSessionId new_session_id = new_session->session_id(); + + if (!new_session->Init()) { + LOGE("CdmEngine::OpenSession: bad session init"); + delete(new_session); + return UNKNOWN_ERROR; + } + + sessions_[new_session_id] = new_session; + *session_id = new_session_id; + return NO_ERROR; +} + +CdmResponseType CdmEngine::CloseSession(CdmSessionId& session_id) { + LOGI("CdmEngine::CloseSession"); + + CdmSession* cdm_session = sessions_[session_id]; + + if (!cdm_session) { + LOGE("CdmEngine::CloseSession: session not found = %s", session_id.c_str()); + return KEY_ERROR; + } + + sessions_.erase(session_id); + cdm_session->DestroySession(); + delete cdm_session; + return NO_ERROR; +} + +CdmResponseType CdmEngine::GenerateKeyRequest( + const CdmSessionId& session_id, + bool is_key_system_present, + const CdmKeySystem& key_system, + const CdmInitData& init_data, + const CdmLicenseType license_type, + CdmNameValueMap& app_parameters, + CdmKeyMessage* key_request) { + LOGI("CdmEngine::GenerateKeyRequest"); + + CdmSession* session = sessions_[session_id]; + + if (!session) { + LOGE("CdmEngine::GenerateKeyRequest: session_id not found = %s", session_id.c_str()); + return KEY_ERROR; + } + + if (is_key_system_present) { + // TODO(edwinwong, rfrias): validate key_system has not changed + } + + if (init_data.empty()) { + LOGE("CdmEngine::GenerateKeyRequest: no init_data provided"); + return KEY_ERROR; + } + + if (!key_request) { + LOGE("CdmEngine::GenerateKeyRequest: no key request destination provided"); + return KEY_ERROR; + } + + CdmInitData extracted_pssh; + if (!ExtractWidevinePssh(init_data, &extracted_pssh)) { + key_request->clear(); + return KEY_ERROR; + } + + key_request->clear(); + + // TODO(edwinwong, rfrias): need to pass in license type and app parameters + CdmResponseType sts = session->GenerateKeyRequest(extracted_pssh, + key_request); + + if (KEY_MESSAGE != sts) { + LOGE("CdmEngine::GenerateKeyRequest: key request generation failed, sts=%d", + (int)sts); + return sts; + } + + // TODO(edwinwong, rfrias): persist init_data, license_type, app_parameters + // in session + + return KEY_MESSAGE; +} + +CdmResponseType CdmEngine::AddKey( + const CdmSessionId& session_id, + bool is_key_system_init_data_present, + const CdmKeySystem& key_system, + const CdmInitData& init_data, + const CdmKeyResponse& key_data) { + LOGI("CdmEngine::AddKey"); + + CdmSession* session = sessions_[session_id]; + + if (!session) { + LOGE("CdmEngine::AddKey: session_id not found = %s", session_id.c_str()); + return KEY_ERROR; + } + + if (is_key_system_init_data_present) { + // TODO(edwinwong, rfrias): validate key_system has not changed + } + + if (is_key_system_init_data_present) { + // TODO(edwinwong, rfrias): validate init_data has not changed + } + + if (key_data.empty()) { + LOGE("CdmEngine::AddKey: no key_data"); + return KEY_ERROR; + } + + CdmResponseType sts = session->AddKey(key_data); + if (KEY_ADDED != sts) { + LOGE("CdmEngine::AddKey: keys not added, result = %d", (int)sts); + } + + return sts; +} + +CdmResponseType CdmEngine::CancelKeyRequest( + const CdmSessionId& session_id, + bool is_key_system_present, + const CdmKeySystem& key_system) { + LOGI("CdmEngine::CancelKeyRequest"); + + //TODO(gmorgan): Issue: what is semantics of canceling a key request. Should + //this call cancel all keys for the session? + // TODO(jfore): We should disable the policy timer here if there are no + // active sessions. Sessions are currently not being destroyed here. We can + // add this logic once the semantics of canceling the key is worked out. + + CdmSession* session = sessions_[session_id]; + + if (!session) { + LOGE("CdmEngine::CancelKeyRequest: session_id not found = %s", session_id.c_str()); + return KEY_ERROR; + } + + if (is_key_system_present) { + // TODO(edwinwong, rfrias): validate key_system has not changed + } + + // TODO(edwinwong, rfrias): unload keys here + + return NO_ERROR; +} + +CdmResponseType CdmEngine::GenerateRenewalRequest( + const CdmSessionId& session_id, + bool is_key_system_init_data_present, + const CdmKeySystem& key_system, + const CdmInitData& init_data, + CdmKeyMessage* key_request) { + LOGI("CdmEngine::GenerateRenewalRequest"); + + CdmSession* session = sessions_[session_id]; + + if (!session) { + LOGE("CdmEngine::GenerateRenewalRequest: session_id not found = %s", session_id.c_str()); + return KEY_ERROR; + } + + if (is_key_system_init_data_present) { + // TODO(edwinwong, rfrias): validate key_system has not changed + } + + if (is_key_system_init_data_present) { + // TODO(edwinwong, rfrias): validate init_data has not changed + } + + if (!key_request) { + LOGE("CdmEngine::GenerateRenewalRequest: no key request destination provided"); + return KEY_ERROR; + } + + key_request->clear(); + + CdmResponseType sts = session->GenerateRenewalRequest(key_request); + + if (KEY_MESSAGE != sts) { + LOGE("CdmEngine::GenerateRenewalRequest: key request generation failed, sts=%d", + (int)sts); + return sts; + } + + return KEY_MESSAGE; +} + +CdmResponseType CdmEngine::RenewKey( + const CdmSessionId& session_id, + bool is_key_system_init_data_present, + const CdmKeySystem& key_system, + const CdmInitData& init_data, + const CdmKeyResponse& key_data) { + LOGI("CdmEngine::RenewKey"); + + CdmSession* session = sessions_[session_id]; + + if (!session) { + LOGE("CdmEngine::RenewKey: session_id not found = %s", session_id.c_str()); + return KEY_ERROR; + } + + if (is_key_system_init_data_present) { + // TODO(edwinwong, rfrias): validate key_system has not changed + } + + if (is_key_system_init_data_present) { + // TODO(edwinwong, rfrias): validate init_data has not changed + } + + if (key_data.empty()) { + LOGE("CdmEngine::RenewKey: no key_data"); + return KEY_ERROR; + } + + CdmResponseType sts = session->RenewKey(key_data); + if (KEY_ADDED != sts) { + LOGE("CdmEngine::RenewKey: keys not added, sts=%d", (int)sts); + return sts; + } + + return KEY_ADDED; +} + +CdmResponseType CdmEngine::QueryKeyStatus( + const CdmSessionId& session_id, + CdmNameValueMap* key_info) { + // TODO(edwinwong, rfrias): add implementation + return NO_ERROR; +} + +CdmResponseType CdmEngine::GetProvisioningRequest( + CdmProvisioningRequest* request, + std::string* default_url) { + // TODO(edwinwong, rfrias): add implementation + return NO_ERROR; +} + +CdmResponseType CdmEngine::HandleProvisioningResponse( + CdmProvisioningResponse& response) { + // TODO(edwinwong, rfrias): add implementation + return NO_ERROR; +} + +CdmResponseType CdmEngine::GetSecureStops( + CdmSecureStops* secure_stops) { + // TODO(edwinwong, rfrias): add implementation + return NO_ERROR; +} + +CdmResponseType CdmEngine::ReleaseSecureStops( + const CdmSecureStopReleaseMessage& message) { + // TODO(edwinwong, rfrias): add implementation + return NO_ERROR; +} + +CdmResponseType CdmEngine::Decrypt( + const CdmSessionId& session_id, + bool is_encrypted, + const KeyId& key_id, + const uint8_t* encrypted_buffer, + size_t encrypted_size, + const std::vector& iv, + size_t block_offset, + void* decrypted_buffer) { + CdmSession* session = sessions_[session_id]; + + if (!session) { + LOGW("CdmEngine::Decrypt: session_id not found = %s", session_id.c_str()); + return KEY_ERROR; + } + + // TODO(edwinwong, rfrias): Need to add implemenation and hook up + // decryption though to oem_crypto + return NO_ERROR; +} + +bool CdmEngine::IsKeyValid(const KeyId& key_id) { + for (CdmSessionIter iter = sessions_.begin(); + iter != sessions_.end(); ++iter) { + if (iter->second->IsKeyValid(key_id)) { + return true; + } + } + return false; +} + +bool CdmEngine::AttachEventListener( + CdmSessionId& session_id, + WvCdmEventListener* listener) { + + CdmSessionIter iter = sessions_.find(session_id); + if (iter == sessions_.end()) { + return false; + } + + return iter->second->AttachEventListener(listener); +} + +bool CdmEngine::DetachEventListener( + CdmSessionId& session_id, + WvCdmEventListener* listener) { + + CdmSessionIter iter = sessions_.find(session_id); + if (iter == sessions_.end()) { + return false; + } + + return iter->second->DetachEventListener(listener); +} + +bool CdmEngine::ValidateKeySystem(const CdmKeySystem& key_system) { + return (key_system.find("widevine") != std::string::npos); +} + +bool CdmEngine::CancelSessions() { + // TODO(gmorgan) Implement CancelSessions() + return true; +} + +// Parse a blob of multiple concatenated PSSH atoms to extract the first +// widevine pssh +// TODO(kqyang): temporary workaround - remove after b/7928472 is resolved +bool CdmEngine::ExtractWidevinePssh( + const CdmInitData& init_data, CdmInitData* output) { + + BufferReader reader( + reinterpret_cast(init_data.data()), init_data.length()); + + // TODO(kqyang): Extracted from an actual init_data; + // Need to find out where it comes from. + static const uint8_t kWidevineSystemId[] = { + 0xED, 0xEF, 0x8B, 0xA9, 0x79, 0xD6, 0x4A, 0xCE, + 0xA3, 0xC8, 0x27, 0xDC, 0xD5, 0x1D, 0x21, 0xED, + }; + + // one PSSH blob consists of: + // 4 byte size of the PSSH atom, inclusive + // "pssh" + // 4 byte flags, value 0 + // 16 byte system id + // 4 byte size of PSSH data, exclusive + while (1) { + // size of PSSH atom, used for skipping + uint32_t size; + if (!reader.Read4(&size)) return false; + + // "pssh" + std::vector pssh; + if (!reader.ReadVec(&pssh, 4)) return false; + if (memcmp(&pssh[0], "pssh", 4)) return false; + + // flags + uint32_t flags; + if (!reader.Read4(&flags)) return false; + if (flags != 0) return false; + + // system id + std::vector system_id; + if (!reader.ReadVec(&system_id, sizeof(kWidevineSystemId))) return false; + + if (memcmp(&system_id[0], kWidevineSystemId, + sizeof(kWidevineSystemId))) { + // skip the remaining contents of the atom, + // after size field, atom name, flags and system id + if (!reader.SkipBytes( + size - 4 - 4 - 4 - sizeof(kWidevineSystemId))) return false; + continue; + } + + // size of PSSH box + uint32_t pssh_length; + if (!reader.Read4(&pssh_length)) return false; + + output->clear(); + if (!reader.ReadString(output, pssh_length)) return false; + + return true; + } + + // we did not find a matching record + return false; +} + +void CdmEngine::EnablePolicyTimer() { + + if (!policy_timer_.IsRunning()) + policy_timer_.Start(this, CDM_POLICY_TIMER_DURATION_SECONDS); +} + +void CdmEngine::DisablePolicyTimer() { + + if (policy_timer_.IsRunning()) + policy_timer_.Stop(); +} + +void CdmEngine::OnTimerEvent() { + + for (CdmSessionIter iter = sessions_.begin(); + iter != sessions_.end(); ++iter) { + iter->second->OnTimerEvent(); + } +} + +} // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/src/cdm_session.cpp b/libwvdrmengine/cdm/core/src/cdm_session.cpp new file mode 100644 index 00000000..9086341e --- /dev/null +++ b/libwvdrmengine/cdm/core/src/cdm_session.cpp @@ -0,0 +1,144 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// Author: jfore@google.com (Jeff Fore), rkuroiwa@google.com (Rintaro Kuroiwa) + +#include "cdm_session.h" + +#include + +#include "crypto_engine.h" +#include "log.h" +#include "string_conversions.h" +#include "clock.h" +#include "wv_cdm_constants.h" + +namespace wvcdm { + +typedef std::set::iterator CdmEventListenerIter; + +bool CdmSession::Init() { + CryptoEngine* crypto_engine = CryptoEngine::GetInstance(); + if (!crypto_engine) { + LOGE("CdmSession::Init failed to get CryptoEngine instance."); + return false; + } + + crypto_session_ = crypto_engine->CreateSession(session_id_); + if (!crypto_session_) { + return false; + } + + std::string token; + if (!crypto_engine->GetToken(&token)) return false; + + return license_parser_.Init(token, crypto_session_); +} + +bool CdmSession::DestroySession() { + if (crypto_session_) { + delete crypto_session_; + crypto_session_ = NULL; + } + return true; +} + +bool CdmSession::VerifySession(const CdmKeySystem& key_system, + const CdmInitData& init_data) { + // TODO(gmorgan): Compare key_system and init_data with value received + // during session startup - they should be the same. + return true; +} + +CdmResponseType CdmSession::GenerateKeyRequest(const CdmInitData& init_data, + CdmKeyMessage* key_request) { + crypto_session_->Open(); + if(!license_parser_.PrepareKeyRequest(init_data, key_request)) { + return KEY_ERROR; + } else { + return KEY_MESSAGE; + } +} + +// AddKey() - Accept license response and extract key info. +CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response) { + if (!license_parser_.HandleKeyResponse(key_response)) { + return KEY_ERROR; + } else { + return KEY_ADDED; + } +} + +// CancelKeyRequest() - Cancel session. +CdmResponseType CdmSession::CancelKeyRequest() { + // TODO(gmorgan): cancel and clean up session + crypto_session_->Close(); + return NO_ERROR; +} + +// Decrypt() - Accept encrypted buffer and return decrypted data. +CdmResponseType CdmSession::Decrypt(const uint8_t* encrypted_buffer, + size_t encrypted_size, + size_t block_offset, + const std::string& iv, + const KeyId& key_id, + uint8_t* decrypted_buffer) { + return UNKNOWN_ERROR; +} + +// License renewal +// GenerateRenewalRequest() - Construct valid renewal request for the current +// session keys. +CdmResponseType CdmSession::GenerateRenewalRequest(CdmKeyMessage* key_request) { + if(!license_parser_.PrepareKeyRenewalRequest(key_request)) { + return KEY_ERROR; + } else { + return KEY_MESSAGE; + } +} + +// RenewKey() - Accept renewal response and update key info. +CdmResponseType CdmSession::RenewKey(const CdmKeyResponse& key_response) { + if (!license_parser_.HandleKeyRenewalResponse(key_response)) { + return KEY_ERROR; + } else { + return KEY_ADDED; + } +} + +bool CdmSession::IsKeyValid(const KeyId& key_id) { + // TODO(gmorgan): lookup key and determine if valid. + // return (session_keys_.find(key_id) != session_keys_.end()); + return true; +} + +CdmSessionId CdmSession::GenerateSessionId() { + static const std::string kSessionPrefix("Session"); + static int session_num = 1; + // TODO(rkuroiwa): Want this to be unique. Probably doing Hash(time+init_data) + // to get something that is reasonably unique. + return kSessionPrefix + IntToString(++session_num); +} + +bool CdmSession::AttachEventListener(WvCdmEventListener* listener) { + std::pair result = listeners_.insert(listener); + return result.second; +} + +bool CdmSession::DetachEventListener(WvCdmEventListener* listener) { + return (listeners_.erase(listener) == 1); +} + +void CdmSession::OnTimerEvent() { + bool event_occurred = false; + CdmEventType event; + + policy_engine_.OnTimerEvent(GetCurrentTime(), event_occurred, event); + + if (event_occurred) { + for (CdmEventListenerIter iter = listeners_.begin(); + iter != listeners_.end(); ++iter) { + (*iter)->onEvent(session_id(), event); + } + } +} + +} // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/src/crypto_engine.cpp b/libwvdrmengine/cdm/core/src/crypto_engine.cpp new file mode 100644 index 00000000..feed17ba --- /dev/null +++ b/libwvdrmengine/cdm/core/src/crypto_engine.cpp @@ -0,0 +1,178 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Crypto - wrapper classes for OEMCrypto interface +// + +#include "crypto_engine.h" + +#include + +#include "log.h" +#include "OEMCryptoCENC.h" +#include "wv_cdm_constants.h" + +namespace wvcdm { + +CryptoEngine* CryptoEngine::crypto_engine_ = NULL; +Lock CryptoEngine::crypto_engine_lock_; + +// wrapper classes for OEMCrypto interface +// CryptoEngine -- top-level interface +// CryptoSession -- session-specific interface +// CryptoKey -- key interface + +// CryptoEngine methods + +CryptoEngine::CryptoEngine() : initialized_(false) {} + +CryptoEngine::~CryptoEngine() { + if (initialized_) { + Terminate(); + } + + CryptoSessionMap::iterator i(sessions_.begin()); + for (; i != sessions_.end(); ++i) + delete i->second; + sessions_.clear(); +} + +// get the instance of OEMCrypto Client +CryptoEngine* CryptoEngine::GetInstance() { + if (NULL == crypto_engine_) { + crypto_engine_ = CreateSingleton(); + } + return crypto_engine_; +} + +CryptoEngine* CryptoEngine::CreateSingleton() { + AutoLock auto_lock(crypto_engine_lock_); + if (NULL == crypto_engine_) { + crypto_engine_ = new CryptoEngine; + } + return crypto_engine_; +} + +void CryptoEngine::DeleteInstance() { + if (NULL != crypto_engine_) { + delete crypto_engine_; + LOGV("CryptoEngine::DeleteInstance"); + crypto_engine_ = NULL; + } +} + +bool CryptoEngine::Init() { + LOGV("CryptoEngine::Init: Lock"); + AutoLock auto_lock(crypto_lock_); + if (!initialized_) { + OEMCryptoResult result = OEMCrypto_Initialize(); + initialized_ = (OEMCrypto_SUCCESS == result); + } + return initialized_; +} + +bool CryptoEngine::Terminate() { + DestroySessions(); + LOGV("CryptoEngine::Terminate: Lock"); + AutoLock auto_lock(crypto_lock_); + OEMCryptoResult result = OEMCrypto_Terminate(); + if (OEMCrypto_SUCCESS == result) { + initialized_ = false; + } + return !initialized_; +} + +bool CryptoEngine::ValidateKeybox() { + LOGV("CryptoEngine::ValidateKeybox: Lock"); + AutoLock auto_lock(crypto_lock_); + OEMCryptoResult result = OEMCrypto_IsKeyboxValid(); + return (OEMCrypto_SUCCESS == result); +} + +CryptoSession* CryptoEngine::CreateSession(const CdmSessionId& session_id) { + LOGV("CryptoEngine::CreateSession: SLock"); + AutoLock auto_lock(sessions_lock_); + if (0 == sessions_.size()) { + if (!Init()) { + return NULL; + } + } + + CryptoSessionMap::iterator it = sessions_.find(session_id); + if (it != sessions_.end()) { + LOGE("CryptoEngine::CreateSession : Duplicate session ID."); + return NULL; + } + + CryptoSession* new_session = new CryptoSession(session_id); + if (!new_session) { + return NULL; + } + + if (!new_session->Open()) { + delete new_session; + return NULL; + } + + sessions_[session_id] = new_session; + return new_session; +} + +CryptoSession* CryptoEngine::FindSessionInternal( + const CdmSessionId& session_id) { + // must hold sessions_lock_ + CryptoSessionMap::iterator it = sessions_.find(session_id); + if (it != sessions_.end()) { + return it->second; + } + return NULL; +} + +CryptoSession* CryptoEngine::FindSession(const CdmSessionId& session_id) { + LOGV("CryptoEngine::FindSession: SLock"); + AutoLock auto_lock(sessions_lock_); + return FindSessionInternal(session_id); +} + +bool CryptoEngine::DestroySession(const CdmSessionId& session_id) { + LOGV("CryptoEngine::DestroySession: SLock"); + AutoLock auto_lock(sessions_lock_); + if (0 == sessions_.size()) { + return false; + } + CryptoSession* session = FindSessionInternal(session_id); + if (session) { + delete session; + sessions_.erase(session_id); + return true; + } else { + return false; + } +} + +bool CryptoEngine::DestroySessions() { + for (CryptoSessionMap::iterator it = sessions_.begin(); + it != sessions_.end(); ++it) { + delete it->second; + } + sessions_.clear(); + return true; +} + +bool CryptoEngine::GetToken(std::string* token) { + LOGV("CryptoEngine::GetToken: Lock"); + AutoLock auto_lock(crypto_lock_); + if (!token) { + LOGE("CryptoEngine::GetToken : No token passed to method."); + return false; + } + uint8_t buf[72]; + size_t buflen = 72; + OEMCryptoResult sts = OEMCrypto_GetKeyData(buf, &buflen); + if (OEMCrypto_SUCCESS != sts) { + return false; + } + token->assign((const char*)buf, (size_t)buflen); + return true; +} + +}; // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/src/crypto_session.cpp b/libwvdrmengine/cdm/core/src/crypto_session.cpp new file mode 100644 index 00000000..9840f272 --- /dev/null +++ b/libwvdrmengine/cdm/core/src/crypto_session.cpp @@ -0,0 +1,338 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Crypto - wrapper classes for OEMCrypto interface +// + +#include "crypto_session.h" + +#include + +#include "crypto_engine.h" +#include "log.h" +// TODO(gmorgan,jtinker): decide if OEMCryptoCENC is needed here. +#include "OEMCryptoCENC.h" +#include "string_conversions.h" +#include "wv_cdm_constants.h" + +namespace { +// Encode unsigned integer into a big endian formatted string +std::string EncodeUint32(unsigned int u) { + std::string s; + s.append(1, (u >> 24) & 0xFF); + s.append(1, (u >> 16) & 0xFF); + s.append(1, (u >> 8) & 0xFF); + s.append(1, (u >> 0) & 0xFF); + return s; +} +} + +namespace wvcdm { + +// wrapper classes for OEMCrypto interface +// CryptoEngine -- top-level interface +// CryptoSession -- session-specific interface +// CryptoKey -- key interface + +// CryptoSession methods + +CryptoSession::CryptoSession() : valid_(false), open_(false) {} + +CryptoSession::CryptoSession(const std::string& sname) : valid_(true), + open_(false), cdm_session_id_(sname) {} + +CryptoSession::~CryptoSession() { + if (open_) { + Close(); + } + LOGV("CryptoSession::dtor: SLock"); + CryptoEngine* crypto_engine = CryptoEngine::GetInstance(); + crypto_engine->sessions_.erase(cdm_session_id_); + if (0 == crypto_engine->sessions_.size()) { + crypto_engine->DeleteInstance(); + } + + CryptoKeyMap::iterator i(keys_.begin()); + for (; i != keys_.end(); ++i) + delete i->second; + keys_.clear(); +} + +bool CryptoSession::Open() { + LOGV("CryptoSession::Open: Lock"); + CryptoEngine* crypto_engine = CryptoEngine::GetInstance(); + AutoLock auto_lock(crypto_engine->crypto_lock_); + OEMCrypto_SESSION sid; + OEMCryptoResult sts; + if (open_) + return false; + sts = OEMCrypto_OpenSession(&sid); + if (OEMCrypto_SUCCESS != sts) { + open_ = false; + } else { + oec_session_id_ = static_cast(sid); + LOGV("OpenSession: id= %ld", (uint32_t) oec_session_id_); + open_ = true; + } + return open_; +} + +void CryptoSession::Close() { + LOGV("CryptoSession::Close: Lock"); + CryptoEngine* crypto_engine = CryptoEngine::GetInstance(); + AutoLock auto_lock(crypto_engine->crypto_lock_); + LOGV("CloseSession: id=%ld open=%s", (uint32_t) oec_session_id_, open_? "true" : "false") ; + if (open_) { + OEMCryptoResult sts = OEMCrypto_CloseSession(oec_session_id_); + if (OEMCrypto_SUCCESS == sts) { + open_ = false; + } + } +} + +void CryptoSession::GenerateRequestId(std::string& req_id_str) { + LOGV("CryptoSession::GenerateRequestId: Lock"); + CryptoEngine* crypto_engine = CryptoEngine::GetInstance(); + AutoLock auto_lock(crypto_engine->crypto_lock_); + // TODO(gmorgan): Get unique ID from OEMCrypto + req_id_str.assign("987654321"); +} + +bool CryptoSession::PrepareRequest(const std::string& message, + std::string* signature) { + LOGV("CryptoSession::PrepareRequest: Lock"); + CryptoEngine* crypto_engine = CryptoEngine::GetInstance(); + AutoLock auto_lock(crypto_engine->crypto_lock_); + + if (!signature) { + LOGE("CryptoSession::PrepareRequest : No output destination provided."); + return false; + } + + uint8_t signature_buf[32]; + size_t length = 32; + OEMCryptoResult sts; + + std::string mac_deriv_message; + std::string enc_deriv_message; + GenerateMacContext(message, &mac_deriv_message); + GenerateEncryptContext(message, &enc_deriv_message); + + LOGV("GenerateDerivedKeys: id=%ld", (uint32_t) oec_session_id_); + sts = OEMCrypto_GenerateDerivedKeys( + oec_session_id_, + reinterpret_cast(mac_deriv_message.data()), + mac_deriv_message.size(), + reinterpret_cast(enc_deriv_message.data()), + enc_deriv_message.size()); + if (OEMCrypto_SUCCESS != sts) { + return false; + } + + LOGV("GenerateSignature: id=%ld", (uint32_t) oec_session_id_); + sts = OEMCrypto_GenerateSignature( + oec_session_id_, + reinterpret_cast(message.data()), + message.size(), + signature_buf, + &length); + if (OEMCrypto_SUCCESS != sts) { + return false; + } + + signature->assign(reinterpret_cast(signature_buf), length); + + return true; +} + +bool CryptoSession::PrepareRenewalRequest(const std::string& message, + std::string* signature) { + LOGV("CryptoSession::PrepareRenewalRequest: Lock"); + CryptoEngine* crypto_engine = CryptoEngine::GetInstance(); + AutoLock auto_lock(crypto_engine->crypto_lock_); + uint8_t signature_buf[32]; + size_t length = 32; + + OEMCryptoResult sts = OEMCrypto_GenerateSignature( + oec_session_id_, reinterpret_cast(message.data()), + message.size(), signature_buf, &length); + if (OEMCrypto_SUCCESS != sts) { + return false; + } + + signature->assign(reinterpret_cast(signature_buf), length); + + return true; +} + +void CryptoSession::GenerateMacContext(const std::string& input_context, + std::string* deriv_context) { + if (!deriv_context) { + LOGE("CryptoSession::GenerateMacContext : No output destination provided."); + return; + } + + const std::string kSigningKeyLabel = "AUTHENTICATION"; + const size_t kSigningKeySizeBits = MAC_KEY_SIZE * 8; + + deriv_context->assign(kSigningKeyLabel); + deriv_context->append(1, '\0'); + deriv_context->append(input_context); + deriv_context->append(EncodeUint32(kSigningKeySizeBits)); +} + +void CryptoSession::GenerateEncryptContext(const std::string& input_context, + std::string* deriv_context) { + if (!deriv_context) { + LOGE("CryptoSession::GenerateEncryptContext : No output destination provided."); + return; + } + + const std::string kEncryptionKeyLabel = "ENCRYPTION"; + const size_t kEncryptionKeySizeBits = KEY_SIZE * 8; + + deriv_context->assign(kEncryptionKeyLabel); + deriv_context->append(1, '\0'); + deriv_context->append(input_context); + deriv_context->append(EncodeUint32(kEncryptionKeySizeBits)); +} + +size_t CryptoSession::GetOffset(std::string message, std::string field) { + size_t pos = message.find(field); + if (pos == std::string::npos) { + LOGE("CryptoSession::GetOffset : Cannot find offset for %s", field.c_str()); + pos = 0; + } + return pos; +} + +bool CryptoSession::LoadKeys(const std::string& message, + const std::string& signature, + const std::string& mac_key_iv, + const std::string& mac_key, + int num_keys, + const CryptoKey* key_array) { + LOGV("CryptoSession::LoadKeys: Lock"); + CryptoEngine* crypto_engine = CryptoEngine::GetInstance(); + AutoLock auto_lock(crypto_engine->crypto_lock_); + + const uint8_t* msg = reinterpret_cast(message.data()); + const uint8_t* enc_mac_key = NULL; + const uint8_t* enc_mac_key_iv = NULL; + if (mac_key.size() >= MAC_KEY_SIZE && mac_key_iv.size() >= KEY_IV_SIZE) { + enc_mac_key = msg + GetOffset(message, mac_key); + enc_mac_key_iv = msg + GetOffset(message, mac_key_iv); + } + OEMCrypto_KeyObject load_key_array[num_keys]; + for (int i=0; ikey_id = msg + GetOffset(message, ki->key_id()); + ko->key_id_length = ki->key_id().length(); + ko->key_data_iv = msg + GetOffset(message, ki->key_data_iv()); + ko->key_data = msg + GetOffset(message, ki->key_data()); + if (ki->HasKeyControl()) { + ko->key_control_iv = msg + GetOffset(message, ki->key_control_iv()); + ko->key_control = msg + GetOffset(message, ki->key_control()); + } + else { + LOGE("For key %d: XXX key has no control block. size=%d", i, ki->key_control().size()); + ko->key_control_iv = NULL; + ko->key_control = NULL; + } + } + LOGV("LoadKeys: id=%ld", (uint32_t) oec_session_id_); + return (OEMCrypto_SUCCESS == OEMCrypto_LoadKeys( + oec_session_id_, msg, message.size(), + reinterpret_cast(signature.data()), + signature.size(), enc_mac_key_iv, enc_mac_key, + num_keys, load_key_array)); +} + +bool CryptoSession::RefreshKeys(const std::string& message, + const std::string& signature, + int num_keys, + const CryptoKey* key_array) { + LOGV("CryptoSession::RefreshKeys: Lock"); + CryptoEngine* crypto_engine = CryptoEngine::GetInstance(); + AutoLock auto_lock(crypto_engine->crypto_lock_); + + const uint8_t* msg = reinterpret_cast(message.data()); + OEMCrypto_KeyRefreshObject load_key_array[num_keys]; + for (int i=0; ikey_id().empty()) { + ko->key_id = NULL; + } else { + ko->key_id = msg + GetOffset(message, ki->key_id()); + } + if (ki->HasKeyControl()) { + if (ki->key_control_iv().empty()) { + ko->key_control_iv = NULL; + } else { + ko->key_control_iv = msg + GetOffset(message, ki->key_control_iv()); + } + ko->key_control = msg + GetOffset(message, ki->key_control()); + } + else { + ko->key_control_iv = NULL; + ko->key_control = NULL; + } + } + LOGV("RefreshKeys: id=%ld", static_cast(oec_session_id_)); + return (OEMCrypto_SUCCESS == OEMCrypto_RefreshKeys( + oec_session_id_, msg, message.size(), + reinterpret_cast(signature.data()), + signature.size(), + num_keys, load_key_array)); +} + +bool CryptoSession::SelectKey(const std::string& key_id) { + LOGV("CryptoSession::SelectKey: Lock"); + CryptoEngine* crypto_engine = CryptoEngine::GetInstance(); + AutoLock auto_lock(crypto_engine->crypto_lock_); + const uint8_t* key_id_string = + reinterpret_cast(key_id.data()); + + LOGV("SelectKey: id=%ld", static_cast(oec_session_id_)); + OEMCryptoResult sts = OEMCrypto_SelectKey(oec_session_id_, key_id_string, + key_id.size()); + if (OEMCrypto_SUCCESS != sts) { + return false; + } + return true; +} + +bool CryptoSession::Decrypt(const InputDescriptor input, + const OutputDescriptor output) { + LOGV("CryptoSession::Decrypt: Lock"); + CryptoEngine* crypto_engine = CryptoEngine::GetInstance(); + AutoLock auto_lock(crypto_engine->crypto_lock_); + // TODO(gmorgan): handle inputs and outputs to decrypt call + const uint8_t* data_addr = NULL; + uint32_t data_length = 0; + bool is_encrypted = false; + uint8_t* iv = NULL; + uint32_t offset = 0; + const OEMCrypto_DestBufferDesc* out_buffer = NULL; + OEMCryptoResult sts = OEMCrypto_DecryptCTR(oec_session_id_, data_addr, + data_length, is_encrypted, iv, + offset, out_buffer); + if (OEMCrypto_SUCCESS != sts) { + return false; + } + return true; +} + +bool CryptoSession::GenerateNonce(uint32_t* nonce) { + if (!nonce) { + LOGE("input parameter is null"); + return false; + } + + LOGV("CryptoSession::GenerateNonce: Lock"); + CryptoEngine* crypto_engine = CryptoEngine::GetInstance(); + AutoLock auto_lock(crypto_engine->crypto_lock_); + return(OEMCrypto_SUCCESS == OEMCrypto_GenerateNonce(oec_session_id_, nonce)); +} +}; // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/src/license.cpp b/libwvdrmengine/cdm/core/src/license.cpp new file mode 100644 index 00000000..c9e80968 --- /dev/null +++ b/libwvdrmengine/cdm/core/src/license.cpp @@ -0,0 +1,298 @@ +// Copyright 2012 Google Inc. All Rights Reserved. + +#include "license.h" + +#include "crypto_session.h" +#include "log.h" +#include "string_conversions.h" +#include "wv_cdm_constants.h" + +namespace wvcdm { + +// Protobuf generated classes. +using video_widevine_server::sdk::LicenseRequest; +using video_widevine_server::sdk::LicenseRequest_ClientIdentification; +using video_widevine_server::sdk::LicenseRequest_ClientIdentification_NameValue; +using video_widevine_server::sdk::LicenseRequest_ContentIdentification; +using video_widevine_server::sdk::LicenseRequest_ContentIdentification_CENC; +using video_widevine_server::sdk::License; +using video_widevine_server::sdk::License_KeyContainer; +using video_widevine_server::sdk::SignedMessage; +using video_widevine_server::sdk::STREAMING; +using video_widevine_server::sdk::LicenseRequest_ContentIdentification_ExistingLicense; + +CdmLicense::CdmLicense(): session_(NULL) {} + +CdmLicense::~CdmLicense() {} + +bool CdmLicense::Init(const std::string& token, CryptoSession* session) { + if (token.size() == 0) + return false; + if (session == NULL || !session->IsValid() || !session->IsOpen()) + return false; + token_ = token; + session_ = session; + return true; +} + +bool CdmLicense::PrepareKeyRequest(const CdmInitData& init_data, + CdmKeyMessage* signed_request) { + if (!session_ || + token_.empty()) { + return false; + } + if (init_data.empty()) { + LOGE("CdmLicense::PrepareKeyRequest : No init data provided;"); + return false; + } + if (!signed_request) { + LOGE("CdmLicense::PrepareKeyRequest : No signed request provided."); + return false; + } + + // TODO(gmorgan): Request ID owned by session? + std::string request_id; + session_->GenerateRequestId(request_id); + + LicenseRequest license_request; + LicenseRequest_ClientIdentification* client_id = + license_request.mutable_client_id(); + + client_id->set_type(LicenseRequest_ClientIdentification::KEYBOX); + client_id->set_token(token_); + + // Content Identification may be a cenc_id, a webm_id or a license_id + LicenseRequest_ContentIdentification* content_id = + license_request.mutable_content_id(); + + LicenseRequest_ContentIdentification_CENC* cenc_content_id = + content_id->mutable_cenc_id(); + cenc_content_id->add_pssh(init_data); + cenc_content_id->set_license_type(STREAMING); + cenc_content_id->set_request_id(request_id); + + // TODO(jfore): The time field will be updated once the cdm wrapper + // has been updated to pass us in the time. + license_request.set_request_time(0); + + license_request.set_type(LicenseRequest::NEW); + + // Get/set the nonce. This value will be reflected in the Key Control Block + // of the license response. + uint32_t nonce; + if (!session_->GenerateNonce(&nonce)) { + return false; + } + license_request.set_key_control_nonce(UintToString(nonce)); + LOGD("PrepareKeyRequest: nonce=%u", nonce); + + // License request is complete. Serialize it. + std::string serialized_license_req; + license_request.SerializeToString(&serialized_license_req); + + // Derive signing and encryption keys and construct signature. + std::string license_request_signature; + if (!session_->PrepareRequest(serialized_license_req, + &license_request_signature)) { + signed_request->clear(); + return false; + } + + if (license_request_signature.empty()) { + signed_request->clear(); + return false; + } + + // Put serialize license request and signature together + SignedMessage signed_message; + signed_message.set_type(SignedMessage::LICENSE_REQUEST); + signed_message.set_signature(license_request_signature); + signed_message.set_msg(serialized_license_req); + + signed_message.SerializeToString(signed_request); + + return true; +} + +bool CdmLicense::PrepareKeyRenewalRequest(CdmKeyMessage* signed_request) { + if (!session_) { + return false; + } + if (!signed_request) { + LOGE("CdmLicense::PrepareKeyRenewalRequest : No signed request provided."); + return false; + } + + LicenseRequest license_request; + license_request.set_type(LicenseRequest::RENEWAL); + + LicenseRequest_ContentIdentification_ExistingLicense* current_license = + license_request.mutable_content_id()->mutable_license(); + current_license->mutable_license_id()->CopyFrom(license_id_); + + // Get/set the nonce. This value will be reflected in the Key Control Block + // of the license response. + uint32_t nonce; + if (!session_->GenerateNonce(&nonce)) { + return false; + } + license_request.set_key_control_nonce(UintToString(nonce)); + LOGD("PrepareKeyRenewalRequest: nonce=%u", nonce); + + // License request is complete. Serialize it. + std::string serialized_license_req; + license_request.SerializeToString(&serialized_license_req); + + // Construct signature. + std::string license_request_signature; + if (!session_->PrepareRenewalRequest(serialized_license_req, + &license_request_signature)) + return false; + + if (license_request_signature.empty()) return false; + + // Put serialize license request and signature together + SignedMessage signed_message; + signed_message.set_type(SignedMessage::LICENSE_REQUEST); + signed_message.set_signature(license_request_signature); + signed_message.set_msg(serialized_license_req); + + signed_message.SerializeToString(signed_request); + + return true; +} + +bool CdmLicense::HandleKeyResponse(const CdmKeyResponse& license_response) { + if (!session_) { + return false; + } + if (license_response.empty()) { + LOGE("CdmLicense::HandleKeyResponse : Empty license response."); + return false; + } + + SignedMessage signed_response; + if (!signed_response.ParseFromString(license_response)) + return false; + + if (!signed_response.has_signature()) + return false; + + License license; + if (!license.ParseFromString(signed_response.msg())) + return false; + + // Extract mac key + std::string mac_key_iv; + std::string mac_key; + if (license.policy().can_renew()) + { + for (int i = 0; i < license.key_size(); ++i) { + if (license.key(i).type() == License_KeyContainer::SIGNING) { + mac_key_iv.assign(license.key(i).iv()); + + // Strip off PKCS#5 padding + mac_key.assign(license.key(i).key().data(), MAC_KEY_SIZE); + } + } + + if (mac_key_iv.size() != KEY_IV_SIZE || + mac_key.size() != MAC_KEY_SIZE) + { + return false; + } + + // License Id should not be empty for renewable license + if (!license.has_id()) return false; + + license_id_.CopyFrom(license.id()); + } + + CryptoKey* key_array = new CryptoKey[license.key_size()]; + if (key_array == NULL) return false; + + // Extract content key(s) + int num_keys = 0; + for (int i = 0; i < license.key_size(); ++i) { + // TODO(kqyang): Key ID size is not fixed in spec, but conventionally we + // always use 16 bytes key id. We'll need to update oemcrypto to support + // variable size key id. + if (license.key(i).id().size() == KEY_ID_SIZE && + license.key(i).key().size() == KEY_SIZE + KEY_PAD_SIZE && + license.key(i).type() == License_KeyContainer::CONTENT) { + + key_array[num_keys].set_key_id(license.key(i).id()); + + // Strip off PKCS#5 padding + key_array[num_keys].set_key_data( + license.key(i).key().substr(0, KEY_SIZE)); + key_array[num_keys].set_key_data_iv(license.key(i).iv()); + if (license.key(i).has_key_control()) { + key_array[num_keys].set_key_control( + license.key(i).key_control().struct_()); + key_array[num_keys].set_key_control_iv( + license.key(i).key_control().iv()); + } + num_keys++; + } + } + + if (num_keys == 0) return false; + + // TODO(kqyang): move protocol buffer related stuff in policy + // engine to this file. + // policy_engine_.SetLicense(license); + + bool status = session_->LoadKeys(signed_response.msg(), + signed_response.signature(), + mac_key_iv, + mac_key, + num_keys, + key_array); + delete[] key_array; + return status; +} + +bool CdmLicense::HandleKeyRenewalResponse( + const CdmKeyResponse& license_response) { + if (!session_) { + return false; + } + if (license_response.empty()) { + LOGE("CdmLicense::HandleKeyRenewalResponse : Empty license response."); + return false; + } + + SignedMessage signed_response; + if (!signed_response.ParseFromString(license_response)) + return false; + + if (!signed_response.has_signature()) + return false; + + // TODO(jfore): refresh the keys in oemcrypto + + License license; + if (!license.ParseFromString(signed_response.msg())) + return false; + + if (!license.has_id()) return false; + + if (license.id().version() > license_id_.version()) { + //This is the normal case. + license_id_.CopyFrom(license.id()); + + // TODO(kqyang): should we move protocol buffer related stuff in policy + // engine to this file instead? + // policy_engine_.UpdateLicense(license); + } else { + // This isn't supposed to happen. + // TODO(jfore): Handle wrap? We can miss responses and that should be + // considered normal until retries are exhausted. + // policy_.set_can_play(false); + } + + return true; +} + +} // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/src/license_protocol.proto b/libwvdrmengine/cdm/core/src/license_protocol.proto new file mode 100644 index 00000000..020d0207 --- /dev/null +++ b/libwvdrmengine/cdm/core/src/license_protocol.proto @@ -0,0 +1,224 @@ +syntax = "proto2"; + +package video_widevine_server.sdk; + +option optimize_for = LITE_RUNTIME; + +enum LicenseType { + STREAMING = 1; + OFFLINE = 2; +} + +// LicenseIdentification is propagated from LicenseRequest to License, +// incrementing version with each iteration. +message LicenseIdentification { + optional bytes request_id = 1; + optional bytes session_id = 2; + optional bytes purchase_id = 3; + optional LicenseType type = 4; + optional int32 version = 5; +} + +message License { + message Policy { + // Indicates that playback of the content is allowed. + optional bool can_play = 1 [default = false]; + + // Indicates that the license may be persisted to non-volatile + // storage for offline use. + optional bool can_persist = 2 [default = false]; + + // Indicates that renewal of this license is allowed. + optional bool can_renew = 3 [default = false]; + + // For the |*duration*| fields, playback must halt when + // license_start_time (seconds since the epoch (UTC)) + + // license_duration_seconds is exceeded. A value of 0 + // indicates that there is no limit to the duration. + + // Indicates the rental window. + optional int64 rental_duration_seconds = 4 [default = 0]; + + // Indicates the viewing window, once playback has begun. + optional int64 playback_duration_seconds = 5 [default = 0]; + + // Indicates the time window for this specific license. + optional int64 license_duration_seconds = 6 [default = 0]; + + // The |renewal*| fields only apply if |can_renew| is true. + + // The window of time, in which playback is allowed to continue while + // renewal is attempted, yet unsuccessful due to backend problems with + // the license server. + optional int64 renewal_recovery_duration_seconds = 7 [default = 0]; + + // All renewal requests for this license shall be directed to the + // specified URL. + optional string renewal_server_url = 8; + + // How many seconds after license_start_time, before renewal is first + // attempted. + optional int64 renewal_delay_seconds = 9 [default = 0]; + + // Specifies the delay in seconds between subsequent license + // renewal requests, in case of failure. + optional int64 renewal_retry_interval_seconds = 10 [default = 0]; + + // Indicates that the license shall be sent for renewal when usage is + // started. + optional bool renew_with_usage = 11 [default = false]; + } + + message KeyContainer { + enum KeyType { + // Exactly one key of this type must appear. + SIGNING = 1; + CONTENT = 2; + } + + // The SecurityLevel enumeration allows the server to communicate the level + // of robustness required by the client, in order to use the key. + enum SecurityLevel { + // Software-based whitebox crypto is required. + SW_SECURE_CRYPTO = 1; + + // Software crypto and an obfuscated decoder is required. + SW_SECURE_DECODE = 2; + + // The key material and crypto operations must be performed within a + // hardware backed trusted execution environment. + HW_SECURE_CRYPTO = 3; + + // The crypto and decoding of content must be performed within a hardware + // backed trusted execution environment. + HW_SECURE_DECODE = 4; + + // The crypto, decoding and all handling of the media (compressed and + // uncompressed) must be handled within a hardware backed trusted + // execution environment. + HW_SECURE_ALL = 5; + } + + message KeyControl { + // |key_control| is documented here: + // https://docs.google.com/a/google.com/document/d/17eDxzzGpPc2qSm7zW68_5ensuxbHErYCvD3IxSKETRo/edit# + // If present, the key control must be communicated to the secure + // environment prior to any usage. + optional bytes struct = 1; + optional bytes iv = 2; + } + + message OutputProtection { + // Indicates whether HDCP is required on digital outputs, and which + // version should be used. + enum HDCP { + HDCP_NONE = 0; + HDCP_V1 = 1; + HDCP_V2 = 2; + } + optional HDCP hdcp = 1 [default = HDCP_NONE]; + + // Indicate the CGMS setting to be inserted on analog output. + enum CGMS { + CGMS_NONE = 42; + COPY_FREE = 0; + COPY_ONCE = 2; + COPY_NEVER = 3; + } + optional CGMS cgms_flags = 2 [default = CGMS_NONE]; + } + optional bytes id = 1; + optional bytes iv = 2; + optional bytes key = 3; + optional KeyType type = 4; + optional SecurityLevel level = 5 [default = SW_SECURE_CRYPTO]; + optional OutputProtection required_protection = 6; + optional OutputProtection requested_protection = 7; + optional KeyControl key_control = 8; + } + + optional LicenseIdentification id = 1; + optional Policy policy = 2; + repeated KeyContainer key = 3; + optional int64 license_start_time = 4; +} + +message LicenseRequest { + message ClientIdentification { + enum TokenType { + KEYBOX = 0; + } + + message NameValue { + optional string name = 1; + optional string value = 2; + } + + optional TokenType type = 1; + optional bytes token = 2; + repeated NameValue client_info = 3; + } + + message ContentIdentification { + message CENC { + repeated bytes pssh = 1; + optional LicenseType license_type = 2; + optional bytes request_id = 3; // Opaque, client-specified. + } + + message WebM { + optional bytes header = 1; + optional LicenseType license_type = 2; + optional bytes request_id = 3; // Opaque, client-specified. + } + + message ExistingLicense { + optional LicenseIdentification license_id = 1; + optional int64 seconds_since_started = 2; + } + + // Exactly one of these must be present. + optional CENC cenc_id = 1; + optional WebM webm_id = 2; + optional ExistingLicense license = 3; + } + + enum RequestType { + NEW = 1; + RENEWAL = 2; + RELEASE = 3; + } + + optional ClientIdentification client_id = 1; + optional ContentIdentification content_id = 2; + optional RequestType type = 3; + optional int64 request_time = 4; + optional bytes key_control_nonce = 5; +} + +message SignedMessage { + enum MessageType { + LICENSE_REQUEST = 1; + LICENSE = 2; + } + + optional MessageType type = 1; + optional bytes msg = 2; + optional bytes signature = 3; +} + +// This message is used to pass optional data on initial license issuance. +message SessionInit { + optional string session_id = 1; + optional string purchase_id = 2; + optional string master_signing_key = 3; + optional string signing_key = 4; + optional int64 license_start_time = 5; +} + +// This message is used by the server to preserve and restore session state. +message SessionState { + optional LicenseIdentification license_id = 1; + optional bytes signing_key = 2; + optional uint32 keybox_system_id = 3; +} diff --git a/libwvdrmengine/cdm/core/src/policy_engine.cpp b/libwvdrmengine/cdm/core/src/policy_engine.cpp new file mode 100644 index 00000000..2e41b3ec --- /dev/null +++ b/libwvdrmengine/cdm/core/src/policy_engine.cpp @@ -0,0 +1,211 @@ +// Copyright 2013 Google Inc. All Rights Reserved. + +#include "policy_engine.h" + +#include +#include +#include +#include + +#include "log.h" +#include "string_conversions.h" + +namespace wvcdm { + +PolicyEngine::PolicyEngine() : + license_state_(kLicenseStateInitial), + license_start_time_(0), + next_renewal_time_(0), + policy_max_duration_seconds_(0) { +} + +PolicyEngine::~PolicyEngine() { +} + +void PolicyEngine::OnTimerEvent(int64_t current_time, bool event_occured, CdmEventType& event) { + event_occured = false; + + // License expiration trumps all. + if (IsLicenseDurationExpired(current_time) && + license_state_ != kLicenseStateExpired) { + license_state_ = kLicenseStateExpired; + event = LICENSE_EXPIRED_EVENT; + event_occured = true; + return; + } + + // Test to determine if renewal should be attempted. + switch (license_state_) { + case kLicenseStateCanPlay: { + if (IsRenewalDelayExpired(current_time)) { + license_state_ = kLicenseStateNeedRenewal; + UpdateRenewalRequest(current_time); + event = LICENSE_RENEWAL_NEEDED_EVENT; + event_occured = true; + } + return; + } + + case kLicenseStateNeedRenewal: { + UpdateRenewalRequest(current_time); + event = LICENSE_RENEWAL_NEEDED_EVENT; + event_occured = true; + return; + } + + case kLicenseStateWaitingLicenseUpdate: { + if (IsRenewalRetryIntervalExpired(current_time)) { + UpdateRenewalRequest(current_time); + event = LICENSE_RENEWAL_NEEDED_EVENT; + event_occured = true; + } + return; + } + + case kLicenseStateInitial: + case kLicenseStateExpired: { + return; + } + + default: { + license_state_ = kLicenseStateCannotPlay; + return; + } + } +} + +// TODO: Fix up differences between Eureka and other platforms' use cases. +// Eureka does not get calls to decrypt. license usage begins +// when we receive the initial license. renew_with_usage will cause renewal to +// occur on the first call to OnTimeEvent after PolicyEngine::SetLicense is +// called. +void PolicyEngine::SetLicense( + const video_widevine_server::sdk::License& license) { + license_id_.Clear(); + license_id_.CopyFrom(license.id()); + policy_.Clear(); + UpdateLicense(license); +} + +void PolicyEngine::UpdateLicense( + const video_widevine_server::sdk::License& license) { + if (!license.has_policy() || kLicenseStateExpired == license_state_) + return; + + policy_.MergeFrom(license.policy()); + policy_max_duration_seconds_ = 0; + + // Calculate policy_max_duration_seconds_. policy_max_duration_seconds_ + // will be set to the minimum of the following policies : + // rental_duration_seconds, playback_duration_seconds, and + // license_duration_seconds. The value is used to determine + // when the license expires. + if (policy_.has_rental_duration_seconds()) + policy_max_duration_seconds_ = policy_.rental_duration_seconds(); + + if ((policy_.has_playback_duration_seconds() && + (policy_.playback_duration_seconds() < policy_max_duration_seconds_)) || + !policy_max_duration_seconds_) { + policy_max_duration_seconds_ = policy_.playback_duration_seconds(); + } + + if ((policy_.has_license_duration_seconds() && + (policy_.license_duration_seconds() < policy_max_duration_seconds_)) || + !policy_max_duration_seconds_) { + policy_max_duration_seconds_ = policy_.license_duration_seconds(); + } + + switch (license_state_) { + case kLicenseStateInitial: { + // Process initial license. + license_start_time_ = license.license_start_time(); + if (policy_.can_play()) { + license_state_ = + policy_.renew_with_usage() ? + kLicenseStateNeedRenewal : kLicenseStateCanPlay; + } else { + license_state_ = kLicenseStateExpired; + } + next_renewal_time_ = license_start_time_ + + policy_.renewal_delay_seconds(); + } + break; + + case kLicenseStateExpired: + // Ignore policy updates. + return; + + default: { + // Process license renewal. + if (license.id().version() > license_id_.version()) { + // This is the normal case. + policy_.MergeFrom(license.policy()); + license_id_.CopyFrom(license.id()); + } else { + // This isn't supposed to happen. + // TODO(jfore): Handle wrap? We can miss responses and that should be + // considered normal until retries are exhausted. + policy_.set_can_play(false); + } + + if (license.has_license_start_time()) { + // license start has been updated. Transition back to the + // normal kLicenseStateCanPlay state if playback is allowed by + // the updated license. + license_start_time_ = license.license_start_time(); + next_renewal_time_ = license_start_time_ + + policy_.renewal_delay_seconds(); + license_state_ = + policy_.can_play() ? kLicenseStateCanPlay : kLicenseStateExpired; + } else { + // license start was not updated. Continue sending renewel requests + // at the specified retry rate. To perform this we transition directly + // to kLicenseStateWaitingLicenseUpdate. While in this state + // IsRenewalRetryIntervalExpired will always return false if retries are + // not allowed. Note that next_renewal_time_ was updated when this + // renewal was requested. + license_state_ = + policy_.can_play() ? + kLicenseStateWaitingLicenseUpdate : kLicenseStateExpired; + } + } + } +} + +void PolicyEngine::UpdateRenewalRequest(int64_t current_time) { + license_state_ = kLicenseStateWaitingLicenseUpdate; + next_renewal_time_ = current_time + policy_.renewal_retry_interval_seconds(); +} + +// For the policy time fields checked in the following methods, a value of 0 +// indicates that there is no limit to the duration. These methods +// will always return false if the value is 0. +bool PolicyEngine::IsLicenseDurationExpired(int64_t current_time) { + return policy_max_duration_seconds_ && + license_start_time_ + policy_max_duration_seconds_ <= + current_time; +} + +bool PolicyEngine::IsRenewalDelayExpired(int64_t current_time) { + return (policy_.renewal_delay_seconds() > 0) && + license_start_time_ + policy_.renewal_delay_seconds() <= + current_time; +} + +// TODO(jfore): there is some gray around how this should be +// implemented. It currently is not. +bool PolicyEngine::IsRenewalRecoveryDurationExpired( + int64_t current_time) { + return (policy_.renewal_recovery_duration_seconds() > 0) && + license_start_time_ + policy_.renewal_recovery_duration_seconds() <= + current_time; +} + +bool PolicyEngine::IsRenewalRetryIntervalExpired( + int64_t current_time) { + return (policy_.renewal_retry_interval_seconds() > 0) && + next_renewal_time_ <= current_time; +} + +} // wvcdm + diff --git a/libwvdrmengine/cdm/core/src/string_conversions.cpp b/libwvdrmengine/cdm/core/src/string_conversions.cpp new file mode 100644 index 00000000..638b4e6f --- /dev/null +++ b/libwvdrmengine/cdm/core/src/string_conversions.cpp @@ -0,0 +1,211 @@ +// Copyright 2012 Google Inc. All Rights Reserved. + +#include "string_conversions.h" + +#include +#include +#include +#include +#include +#include + +#include "log.h" + +namespace { +// Helper for Base64SafeDecode() +char B64ToBin(char inch) { + if (inch >= 'A' && inch <= 'Z') return inch - 'A'; + if (inch >= 'a' && inch <= 'z') return inch - 'a' + 26; + if (inch >= '0' && inch <= '9') return inch - '0' + 52; + if (inch == '-') return 62; + // if (inch == '_') + return 63; +} +} + +namespace wvcdm { + +static bool CharToDigit(char ch, unsigned char* digit) { + if (ch >= '0' && ch <= '9') { + *digit = ch - '0'; + } else { + ch = tolower(ch); + if ((ch >= 'a') && (ch <= 'f')) { + *digit = ch - 'a' + 10; + } else { + return false; + } + } + return true; +} + +// converts an ascii hex string(2 bytes per digit) into a decimal byte string +std::vector a2b_hex(const std::string& byte) { + std::vector array; + unsigned int count = byte.size(); + if (count == 0 || (count % 2) != 0) { + LOGE("Invalid input size %u for string %s", count, byte.c_str()); + return array; + } + + for (unsigned int i = 0; i < count / 2; ++i) { + unsigned char msb = 0; // most significant 4 bits + unsigned char lsb = 0; // least significant 4 bits + if (!CharToDigit(byte[i * 2], &msb) || + !CharToDigit(byte[i * 2 + 1], &lsb)) { + LOGE("Invalid hex value %c%c at index %d", byte[i*2], byte[i*2+1], i); + return array; + } + array.push_back((msb << 4) | lsb); + } + return array; +} + +std::string a2bs_hex(const std::string& byte) { + std::vector array = a2b_hex(byte); + return std::string(array.begin(), array.end()); +} + +std::string b2a_hex(const std::vector& byte) { + return HexEncode(&byte[0], byte.size()); +} + +std::string b2a_hex(const std::string& byte) { + return HexEncode(reinterpret_cast(byte.data()), + byte.length()); +} + +// Filename-friendly base64 encoding (RFC4648). +// This is the encoding required by GooglePlay for certain +// license server transactions. It is also used for logging +// certain strings. +std::string Base64SafeEncode(const std::vector& bin_input) { + static const char kBase64Chars[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789-_"; + if (bin_input.empty()) { + return std::string(); + } + int in_size = bin_input.size(); + int rup = ((in_size % 3) != 0) ? 1 : 0; + int out_size = ((in_size * 4) / 3) + rup; + std::string b64_output(out_size, '\0'); + int in_index = 0; + int out_index = 0; + unsigned long buffer; + unsigned char out_cc; + static const unsigned long kInMask = 0xff; + static const unsigned long kOutMask = 0x3f; + + while (in_index < in_size) { + // up to 3 bytes (0..255) in + buffer = (bin_input.at(in_index) & kInMask); + buffer <<= 8; + buffer |= (++in_index >= in_size) ? 0 : (bin_input.at(in_index) & kInMask); + buffer <<= 8; + buffer |= (++in_index >= in_size) ? 0 : (bin_input.at(in_index) & kInMask); + ++in_index; + // up to 4 bytes (0..63) out + out_cc = (buffer >> 18) & kOutMask; + b64_output.at(out_index) = kBase64Chars[out_cc]; + if (++out_index >= out_size) + break; + out_cc = (buffer >> 12) & kOutMask; + b64_output.at(out_index) = kBase64Chars[out_cc]; + if (++out_index >= out_size) + break; + out_cc = (buffer >> 6) & kOutMask; + b64_output.at(out_index) = kBase64Chars[out_cc]; + if (++out_index >= out_size) + break; + out_cc = buffer & kOutMask; + b64_output.at(out_index) = kBase64Chars[out_cc]; + ++out_index; + } + return b64_output; +} + +// Decode for Filename-friendly base64 encoding (RFC4648). +// This is the encoding required by GooglePlay for certain +// license server transactions. It is also used for logging +// certain strings. +std::vector Base64SafeDecode(const std::string& b64_input) { + if (b64_input.empty()) { + return std::vector(); + } + int in_size = b64_input.size(); + // out_size should be an integral number of bytes, assuming correct encode + int out_size = ((in_size * 3) / 4); + std::vector bin_output(out_size, '\0'); + int in_index = 0; + int out_index = 0; + unsigned long buffer; + unsigned char out_cc; + static const unsigned long kOutMask = 0xff; + + while (in_index < in_size) { + // up to 4 bytes (0..63) in + buffer = B64ToBin(b64_input.at(in_index)); + buffer <<= 6; + buffer |= (++in_index >= in_size) ? 0 : B64ToBin(b64_input.at(in_index)); + buffer <<= 6; + buffer |= (++in_index >= in_size) ? 0 : B64ToBin(b64_input.at(in_index)); + buffer <<= 6; + buffer |= (++in_index >= in_size) ? 0 : B64ToBin(b64_input.at(in_index)); + ++in_index; + // up to 3 bytes (0..255) out + out_cc = (buffer >> 16) & kOutMask; + bin_output.at(out_index) = out_cc; + if (++out_index >= out_size) + break; + out_cc = (buffer >> 8) & kOutMask; + bin_output.at(out_index) = out_cc; + if (++out_index >= out_size) + break; + out_cc = buffer & kOutMask; + bin_output.at(out_index) = out_cc; + ++out_index; + } + return bin_output; +} + +std::string HexEncode(const uint8_t* in_buffer, unsigned int size) { + static const char kHexChars[] = "0123456789ABCDEF"; + + // Each input byte creates two output hex characters. + std::string out_buffer(size * 2, '\0'); + + for (unsigned int i = 0; i < size; ++i) { + char byte = in_buffer[i]; + out_buffer[(i << 1)] = kHexChars[(byte >> 4) & 0xf]; + out_buffer[(i << 1) + 1] = kHexChars[byte & 0xf]; + } + return out_buffer; +} + +std::string IntToString(int value) { + // log10(2) ~= 0.3 bytes needed per bit or per byte log10(2**8) ~= 2.4. + // So round up to allocate 3 output characters per byte, plus 1 for '-'. + const int kOutputBufSize = 3 * sizeof(int) + 1; + char buffer[kOutputBufSize]; + memset(buffer, 0, kOutputBufSize); + snprintf(buffer, kOutputBufSize, "%d", value); + + std::string out_string(buffer, sizeof(buffer)); + return out_string; +} + +std::string UintToString(unsigned int value) { + // log10(2) ~= 0.3 bytes needed per bit or per byte log10(2**8) ~= 2.4. + // So round up to allocate 3 output characters per byte. + const int kOutputBufSize = 3 * sizeof(unsigned int); + char buffer[kOutputBufSize]; + memset(buffer, 0, kOutputBufSize); + snprintf(buffer, kOutputBufSize, "%u", value); + + std::string out_string(buffer, sizeof(buffer)); + return out_string; +} + +}; // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp b/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp new file mode 100644 index 00000000..e7715c1e --- /dev/null +++ b/libwvdrmengine/cdm/core/test/cdm_engine_test.cpp @@ -0,0 +1,245 @@ +// Copyright 2013 Google Inc. All Rights Reserved. + +#include +#include + +#include "cdm_engine.h" +#include "config_test_env.h" +#include "gtest/gtest.h" +#include "license_request.h" +#include "log.h" +#include "string_conversions.h" +#include "url_request.h" + +namespace { +// Default license server, can be configured using --server command line option +// Default key id (pssh), can be configured using --keyid command line option +std::string g_client_auth; +wvcdm::KeyId g_key_id; +wvcdm::CdmKeySystem g_key_system; +std::string g_license_server; +std::string g_port; +wvcdm::KeyId g_wrong_key_id; +int g_use_full_path = 0; // cannot use boolean in getopt_long +} // namespace + +namespace wvcdm { + +class WvCdmEngineTest : public testing::Test { + public: + WvCdmEngineTest() {} + ~WvCdmEngineTest() {} + + protected: + void GenerateKeyRequest(const std::string& key_system, + const std::string& init_data) { + wvcdm::CdmNameValueMap app_parameters; + EXPECT_EQ(cdm_engine_.GenerateKeyRequest(session_id_, + true, // is_key_system_present + key_system, + init_data, + kLicenseTypeStreaming, + app_parameters, + &key_msg_), wvcdm::KEY_MESSAGE); + } + + void GenerateRenewalRequest(const std::string& key_system, + const std::string& init_data) { + EXPECT_EQ(cdm_engine_.GenerateRenewalRequest(session_id_, + true, // is_key_system_init_data_present, + key_system, + init_data, + &key_msg_), + wvcdm::KEY_MESSAGE); + } + + // posts a request and extracts the drm message from the response + std::string GetKeyRequestResponse(const std::string& server_url, + const std::string& client_auth, + int expected_response) { + UrlRequest url_request(server_url + client_auth, g_port); + + if (!url_request.is_connected()) { + return ""; + } + + url_request.PostRequest(key_msg_); + std::string response; + int resp_bytes = url_request.GetResponse(response); + LOGD("response:\r\n%s", response.c_str()); + LOGD("end %d bytes response dump", resp_bytes); + + // Youtube server returns 400 for invalid message while play server returns + // 500, so just test inequity here for invalid message + int status_code = url_request.GetStatusCode(response); + if (expected_response == 200) { + EXPECT_EQ(200, status_code); + } else { + EXPECT_NE(200, status_code); + } + + std::string drm_msg; + if (200 == status_code) { + LicenseRequest lic_request; + lic_request.GetDrmMessage(response, drm_msg); + LOGV("drm msg: %u bytes\r\n%s", drm_msg.size(), + HexEncode(reinterpret_cast(drm_msg.data()), + drm_msg.size()).c_str()); + } + return drm_msg; + } + + void VerifyKeyRequestResponse(const std::string& server_url, + const std::string& client_auth, + std::string& init_data, + bool is_renewal) { + std::string resp = GetKeyRequestResponse(server_url, + client_auth, + 200); + if (is_renewal) { + EXPECT_EQ(cdm_engine_.RenewKey(session_id_, + true, // is_key_system_init_data_present + g_key_system, + init_data, + resp), wvcdm::KEY_ADDED); + } + else { + EXPECT_EQ(cdm_engine_.AddKey(session_id_, + true, // is_key_system_init_data_present + g_key_system, + init_data, + resp), wvcdm::KEY_ADDED); + } + } + + wvcdm::CdmEngine cdm_engine_; + std::string key_msg_; + std::string session_id_; +}; + +TEST_F(WvCdmEngineTest, BaseMessageTest) { + cdm_engine_.OpenSession(g_key_system, &session_id_); + GenerateKeyRequest(g_key_system, g_key_id); + GetKeyRequestResponse(g_license_server, g_client_auth, 200); + cdm_engine_.CloseSession(session_id_); +} + +TEST_F(WvCdmEngineTest, WrongMessageTest) { + cdm_engine_.OpenSession(g_key_system, &session_id_); + + std::string wrong_message = wvcdm::a2bs_hex(g_wrong_key_id); + GenerateKeyRequest(g_key_system, wrong_message); + GetKeyRequestResponse(g_license_server, g_client_auth, 500); + cdm_engine_.CloseSession(session_id_); +} + +TEST_F(WvCdmEngineTest, NormalDecryption) { + cdm_engine_.OpenSession(g_key_system, &session_id_); + GenerateKeyRequest(g_key_system, g_key_id); + VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); + cdm_engine_.CloseSession(session_id_); +} + +TEST_F(WvCdmEngineTest, LicenseRenewal) { + cdm_engine_.OpenSession(g_key_system, &session_id_); + GenerateKeyRequest(g_key_system, g_key_id); + VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); + + GenerateRenewalRequest(g_key_system, g_key_id); + VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, true); + cdm_engine_.CloseSession(session_id_); +} +} // namespace wvcdm + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + + wvcdm::ConfigTestEnv config; + g_client_auth.assign(config.client_auth()); + g_key_system.assign(config.key_system()); + g_wrong_key_id.assign(config.wrong_key_id()); + + // The following variables are configurable through command line options. + g_license_server.assign(config.license_server()); + g_key_id.assign(config.key_id()); + g_port.assign(config.port()); + std::string license_server(g_license_server); + + int show_usage = 0; + static const struct option long_options[] = { + { "use_full_path", no_argument, &g_use_full_path, 0 }, + { "keyid", required_argument, NULL, 'k' }, + { "port", required_argument, NULL, 'p' }, + { "server", required_argument, NULL, 's' }, + { NULL, 0, NULL, '\0' } + }; + + int option_index = 0; + int opt = 0; + while ((opt = getopt_long(argc, argv, "k:p:s:u", long_options, &option_index)) != -1) { + switch (opt) { + case 'k': { + g_key_id.clear(); + g_key_id.assign(optarg); + break; + } + case 'p': { + g_port.clear(); + g_port.assign(optarg); + break; + } + case 's': { + g_license_server.clear(); + g_license_server.assign(optarg); + break; + } + case 'u': { + g_use_full_path = 1; + break; + } + case '?': { + show_usage = 1; + break; + } + } + } + + if (show_usage) { + std::cout << std::endl; + std::cout << "usage: " << argv[0] << " [options]" << std::endl << std::endl; + std::cout << " enclose multiple arguments in '' when using adb shell" << std::endl; + std::cout << " e.g. adb shell '" << argv[0] << " --server=\"url\"'" << std::endl << std::endl; + + std::cout << std::setw(30) << std::left << " --port="; + std::cout << "specifies the port number, in decimal format" << std::endl; + std::cout << std::setw(30) << std::left << " "; + std::cout << "default: " << g_port << std::endl; + + std::cout << std::setw(30) << std::left << " --server="; + std::cout << "configure the license server url, please include http[s] in the url" << std::endl; + std::cout << std::setw(30) << std::left << " "; + std::cout << "default: " << license_server << std::endl; + + std::cout << std::setw(30) << std::left << " --keyid="; + std::cout << "configure the key id or pssh, in hex format" << std::endl; + std::cout << std::setw(30) << std::left << " default keyid:"; + std::cout << g_key_id << std::endl; + + std::cout << std::setw(30) << std::left << " --use_full_path"; + std::cout << "specify server url is not a proxy server" << std::endl; + std::cout << std::endl; + return 0; + } + + std::cout << std::endl; + std::cout << "Server: " << g_license_server << std::endl; + std::cout << "Port: " << g_port << std::endl; + std::cout << "KeyID: " << g_key_id << std::endl << std::endl; + + g_key_id = wvcdm::a2bs_hex(g_key_id); + config.set_license_server(g_license_server); + config.set_port(g_port); + config.set_key_id(g_key_id); + + return RUN_ALL_TESTS(); +} diff --git a/libwvdrmengine/cdm/core/test/config_test_env.cpp b/libwvdrmengine/cdm/core/test/config_test_env.cpp new file mode 100644 index 00000000..84559557 --- /dev/null +++ b/libwvdrmengine/cdm/core/test/config_test_env.cpp @@ -0,0 +1,77 @@ +// Copyright 2013 Google Inc. All Rights Reserved. + +#include "config_test_env.h" + +namespace { + +// choice of YT, GP, GP_Huahui, SDK_Hali +#define USE_SERVER_YT 1 +#define USE_SERVER_GP 2 +#define USE_SERVER_SDK_Hali 3 + +// select which server to use for testing +#define USE_SERVER USE_SERVER_GP + +#if (USE_SERVER == USE_SERVER_SDK_Hali) + +static const std::string kLicenseServer = + "http://hamid.kir.corp.google.com:8888/drm"; +static const std::string kClientAuth = ""; +static const std::string kKeyId = + "000000347073736800000000" // blob size and pssh + "EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id + "0801121030313233343536373839616263646566"; // key - for gHali + +#elif (USE_SERVER == USE_SERVER_YT) + +static const std::string kLicenseServer = + "https://www.youtube.com/api/drm/widevine?video_id=03681262dc412c06&source=YOUTUBE"; +static const std::string kClientAuth = ""; +static const std::string kKeyId = + "000000347073736800000000" // blob size and pssh + "EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id + "0801121093789920E8D6520098577DF8F2DD5546"; // pssh data + +#elif (USE_SERVER == USE_SERVER_GP) + +static const std::string kLicenseServer = + "https://jmt17.google.com/video-dev/license/GetCencLicense"; + +// NOTE: Append a userdata attribute to place a unique marker that the +// server team can use to track down specific requests during debugging +// e.g., "&userdata=." +// "&userdata=jbmr2.dev" +static const std::string kClientAuth = + "?source=YOUTUBE&video_id=EGHC6OHNbOo&oauth=ya.gtsqawidevine"; + +static const std::string kKeyId = + "000000347073736800000000" // blob size and pssh + "edef8ba979d64acea3c827dcd51d21ed00000014" // Widevine system id + "08011210e02562e04cd55351b14b3d748d36ed8e"; // pssh data + +#else +#error "Must define USE_SERVER" +#endif + +//static const char kWidevineKeySystem[] = "com.widevine.alpha"; + +// An invalid key id, expected to fail +static const std::string kWrongKeyId = + "000000347073736800000000" // blob size and pssh + "EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id + "0901121094889920E8D6520098577DF8F2DD5546"; // pssh data + +} // namespace + +namespace wvcdm { + +ConfigTestEnv::ConfigTestEnv() + : client_auth_(kClientAuth), + key_id_(kKeyId), + key_system_("com.widevine.alpha"), + license_server_(kLicenseServer), + port_("80"), + wrong_key_id_(kWrongKeyId) { +} + +} // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/test/config_test_env.h b/libwvdrmengine/cdm/core/test/config_test_env.h new file mode 100644 index 00000000..1c54f316 --- /dev/null +++ b/libwvdrmengine/cdm/core/test/config_test_env.h @@ -0,0 +1,46 @@ +// Copyright 2013 Google Inc. All Rights Reserved. + +#ifndef CDM_TEST_CONFIG_TEST_ENV_H_ +#define CDM_TEST_CONFIG_TEST_ENV_H_ + +#include +#include "wv_cdm_types.h" + +namespace wvcdm { + +// Configures default test environment. +class ConfigTestEnv { + public: + ConfigTestEnv(); + ~ConfigTestEnv() {}; + + const std::string& client_auth() const { return client_auth_; } + const KeyId& key_id() const { return key_id_; } + const CdmKeySystem& key_system() const { return key_system_; } + const std::string& license_server() const { return license_server_; } + const std::string& port() const { return port_; } + const KeyId& wrong_key_id() const { return wrong_key_id_; } + + void set_key_id(KeyId& key_id) { key_id_.assign(key_id); } + void set_key_system(CdmKeySystem& key_system) { + key_system_.assign(key_system); + } + void set_license_server(std::string& license_server) { + license_server_.assign(license_server); + } + void set_port(std::string& port) { port_.assign(port); } + + private: + std::string client_auth_; + KeyId key_id_; + CdmKeySystem key_system_; + std::string license_server_; + std::string port_; + KeyId wrong_key_id_; + + CORE_DISALLOW_COPY_AND_ASSIGN(ConfigTestEnv); +}; + +}; // namespace wvcdm + +#endif // CDM_TEST_CONFIG_TEST_ENV_H_ diff --git a/libwvdrmengine/cdm/core/test/http_socket.cpp b/libwvdrmengine/cdm/core/test/http_socket.cpp new file mode 100644 index 00000000..7f8f2699 --- /dev/null +++ b/libwvdrmengine/cdm/core/test/http_socket.cpp @@ -0,0 +1,192 @@ +// Copyright 2013 Google Inc. All Rights Reserved. + +#include "http_socket.h" + +#include +#include +#include +#include +#include + +#include "log.h" +namespace wvcdm { + +HttpSocket::HttpSocket() : socket_fd_(-1), timeout_enabled_(false) {} + +HttpSocket::~HttpSocket() +{ + CloseSocket(); +} + +void HttpSocket::CloseSocket() +{ + if (socket_fd_ != -1) { + close(socket_fd_); + socket_fd_ = -1; + } +} + +// Extracts the domain name and resource path from the input url parameter. +// The results are put in domain_name and resource_path respectively. +// The format of the url can begin with :://domain server/... +// or dowmain server/resource_path +void HttpSocket::GetDomainNameAndPathFromUrl(const std::string& url, + std::string& domain_name, + std::string& resource_path) { + domain_name.clear(); + resource_path.clear(); + + size_t start = url.find("//"); + size_t end = url.npos; + if (start != url.npos) { + end = url.find("/", start + 2); + if (end != url.npos) { + domain_name.assign(url, start + 2, end - start - 2); + resource_path.assign(url, end + 1, url.npos); + } else { + domain_name.assign(url, start + 2, url.npos); + } + } else { + // no scheme/protocol in url + end = url.find("/"); + if (end != url.npos) { + domain_name.assign(url, 0, end); + resource_path.assign(url, end + 1, url.npos); + } else { + domain_name.assign(url); + } + } + // strips port number if present, e.g. https://www.domain.com:8888/... + end = domain_name.find(":"); + if (end != domain_name.npos) { + domain_name.erase(end); + } +} + +bool HttpSocket::Connect(const char* url, const std::string& port, bool enable_timeout) +{ + GetDomainNameAndPathFromUrl(url, domain_name_, resource_path_); + + socket_fd_ = socket(AF_INET, SOCK_STREAM, 0); + if (socket_fd_ < 0) { + LOGE("cannot open socket %d", errno); + return false; + } + + int reuse = 1; + if (setsockopt(socket_fd_, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1) { + CloseSocket(); + LOGE("setsockopt error %d", errno); + return false; + } + + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + struct addrinfo* addr_info = NULL; + bool status = true; + int ret = getaddrinfo(domain_name_.c_str(), port.c_str(), &hints, &addr_info); + if (ret != 0) { + CloseSocket(); + LOGE("getaddrinfo failed with %d", ret); + status = false; + } else { + if (connect(socket_fd_, addr_info->ai_addr, addr_info->ai_addrlen) == -1) { + CloseSocket(); + LOGE("cannot connect socket to %s, error=%d", domain_name_.c_str(), errno); + status = false; + } + } + timeout_enabled_ = enable_timeout; + if (addr_info != NULL) { + freeaddrinfo(addr_info); + } + return status; +} + +int HttpSocket::Read(char* data, int len) { + return(Read(data, len, 0)); +} + +// makes non-blocking mode only during read, it supports timeout for read +// returns -1 for error, number of bytes read for success +int HttpSocket::Read(char* data, int len, int timeout_in_ms) +{ + bool use_timeout = (timeout_enabled_ && (timeout_in_ms > 0)); + int original_flags = 0; + if (use_timeout) { + original_flags = fcntl(socket_fd_, F_GETFL, 0); + if (original_flags == -1) { + LOGE("fcntl error %d", errno); + return -1; + } + if (fcntl(socket_fd_, F_SETFL, original_flags | O_NONBLOCK) == -1) { + LOGE("fcntl error %d", errno); + return -1; + } + } + + int total_read = 0; + int read = 0; + int to_read = len; + while (to_read > 0) { + if (use_timeout) { + fd_set read_fds; + struct timeval tv; + tv.tv_sec = timeout_in_ms / 1000; + tv.tv_usec = (timeout_in_ms % 1000) * 1000; + FD_ZERO(&read_fds); + FD_SET(socket_fd_, &read_fds); + if (select(socket_fd_ + 1, &read_fds, NULL, NULL, &tv) == -1) { + LOGE("select failed"); + break; + } + if (!FD_ISSET(socket_fd_, &read_fds)) { + LOGE("socket read timeout"); + break; + } + } + + read = recv(socket_fd_, data, to_read, 0); + if (read > 0) { + to_read -= read; + data += read; + total_read += read; + } else if (read == 0) { + // in blocking mode, zero read mean's peer closed. + // in non-blocking mode, select said that there is data. so it should not happen + break; + } else { + LOGE("recv returned %d, error = %d", read, errno); + break; + } + } + + if (use_timeout) { + fcntl(socket_fd_, F_SETFL, original_flags); // now blocking again + } + return total_read; +} + +int HttpSocket::Write(const char* data, int len) +{ + int total_sent = 0; + int sent = 0; + int to_send = len; + while (to_send > 0) { + sent = send(socket_fd_, data, to_send, 0); + if (sent > 0) { + to_send -= sent; + data += sent; + total_sent += sent; + } else if (sent == 0) { + usleep(10); // retry later + } else { + LOGE("send returned error %d", errno); + } + } + return total_sent; +} + +} // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/test/http_socket.h b/libwvdrmengine/cdm/core/test/http_socket.h new file mode 100644 index 00000000..8cc09531 --- /dev/null +++ b/libwvdrmengine/cdm/core/test/http_socket.h @@ -0,0 +1,39 @@ +// Copyright 2013 Google Inc. All Rights Reserved. + +#ifndef CDM_TEST_HTTP_SOCKET_H_ +#define CDM_TEST_HTTP_SOCKET_H_ + +#include +#include "wv_cdm_types.h" + +namespace wvcdm { + +// Provides basic Linux based TCP socket interface. +class HttpSocket { + public: + HttpSocket(); + ~HttpSocket(); + + void CloseSocket(); + bool Connect(const char* url, const std::string& port, bool enable_timeout); + void GetDomainNameAndPathFromUrl(const std::string& url, + std::string& domain_name, + std::string& resource_path); + const std::string& domain_name() const { return domain_name_; }; + const std::string& resource_path() const { return resource_path_; }; + int Read(char* data, int len); + int Read(char* data, int len, int timeout_in_ms); + int Write(const char* data, int len); + + private: + std::string domain_name_; + std::string resource_path_; + int socket_fd_; + bool timeout_enabled_; + + CORE_DISALLOW_COPY_AND_ASSIGN(HttpSocket); +}; + +}; // namespace wvcdm + +#endif // CDM_TEST_HTTP_SOCKET_H_ diff --git a/libwvdrmengine/cdm/core/test/http_socket_test.cpp b/libwvdrmengine/cdm/core/test/http_socket_test.cpp new file mode 100644 index 00000000..cf7b1d8e --- /dev/null +++ b/libwvdrmengine/cdm/core/test/http_socket_test.cpp @@ -0,0 +1,194 @@ +// Copyright 2013 Google Inc. All Rights Reserved. + +#include +#include "gtest/gtest.h" +#include "http_socket.h" +#include "log.h" +#include "string_conversions.h" + +namespace { + std::string gTestServer("https://www.google.com"); + std::string gTestData("Hello"); + const int kHttpBufferSize = 4096; + char gBuffer[kHttpBufferSize]; +} + +namespace wvcdm { + +class HttpSocketTest : public testing::Test { + public: + HttpSocketTest() {} + ~HttpSocketTest() { socket_.CloseSocket(); } + + protected: + bool Connect(const std::string& server_url) { + + if (socket_.Connect(server_url.c_str(), "80", true)) { + LOGD("connected to %s", socket_.domain_name().c_str()); + } else { + LOGE("failed to connect to %s", socket_.domain_name().c_str()); + return false; + } + return true; + } + + bool PostRequest(const std::string& data) { + std::string request("POST "); + if (socket_.resource_path().empty()) + request.append(socket_.domain_name()); + else + request.append(socket_.resource_path()); + request.append(" HTTP/1.1\r\n"); + request.append("Host: "); + request.append(socket_.domain_name()); + request.append("\r\nUser-Agent: httpSocketTest/1.0\r\n"); + request.append("Content-Length: "); + memset(gBuffer, 0, kHttpBufferSize); + snprintf(gBuffer, kHttpBufferSize, "%d\r\n", static_cast(data.size())); + request.append(gBuffer); + request.append("Content-Type: multipart/form-data\r\n"); + + // newline terminates header + request.append("\r\n"); + + // append data + request.append(data); + socket_.Write(request.c_str(), request.size()); + LOGD("request: %s", request.c_str()); + return true; + } + + bool GetResponse() { + int bytes = socket_.Read(gBuffer, kHttpBufferSize, 1000); + if (bytes < 0) { + LOGE("read error = ", errno); + return false; + } else { + LOGD("read %d bytes", bytes); + std::string response(gBuffer, bytes); + LOGD("response: %s", response.c_str()); + LOGD("end response dump"); + return true; + } + } + + HttpSocket socket_; + std::string domain_name_; + std::string resource_path_; +}; + +TEST_F(HttpSocketTest, GetDomainNameAndPathFromUrlTest) +{ + socket_.GetDomainNameAndPathFromUrl("http://code.google.com/p/googletest/wiki/Primer", + domain_name_, + resource_path_); + EXPECT_STREQ("code.google.com", domain_name_.c_str()); + EXPECT_STREQ("p/googletest/wiki/Primer", resource_path_.c_str()); + + socket_.GetDomainNameAndPathFromUrl("http://code.google.com/p/googletest/wiki/Primer/", + domain_name_, + resource_path_); + EXPECT_STREQ("code.google.com", domain_name_.c_str()); + EXPECT_STREQ("p/googletest/wiki/Primer/", resource_path_.c_str()); + + socket_.GetDomainNameAndPathFromUrl("http://code.google.com/", + domain_name_, + resource_path_); + EXPECT_STREQ("code.google.com", domain_name_.c_str()); + EXPECT_STREQ("", resource_path_.c_str()); + + socket_.GetDomainNameAndPathFromUrl("http://code.google.com", + domain_name_, + resource_path_); + EXPECT_STREQ("code.google.com", domain_name_.c_str()); + EXPECT_STREQ("", resource_path_.c_str()); + + socket_.GetDomainNameAndPathFromUrl("code.google.com/p/googletest/wiki/Primer", + domain_name_, + resource_path_); + EXPECT_STREQ("code.google.com", domain_name_.c_str()); + EXPECT_STREQ("p/googletest/wiki/Primer", resource_path_.c_str()); + + socket_.GetDomainNameAndPathFromUrl("code.google.com", + domain_name_, + resource_path_); + EXPECT_STREQ("code.google.com", domain_name_.c_str()); + EXPECT_STREQ("", resource_path_.c_str()); + + socket_.GetDomainNameAndPathFromUrl("code.google.com/", + domain_name_, + resource_path_); + EXPECT_STREQ("code.google.com", domain_name_.c_str()); + EXPECT_STREQ("", resource_path_.c_str()); + + socket_.GetDomainNameAndPathFromUrl("", + domain_name_, + resource_path_); + EXPECT_TRUE(domain_name_.empty()); + EXPECT_TRUE(resource_path_.empty()); + + socket_.GetDomainNameAndPathFromUrl("http://10.21.200.68:8888/drm", + domain_name_, + resource_path_); + EXPECT_STREQ("10.21.200.68", domain_name_.c_str()); + EXPECT_STREQ("drm", resource_path_.c_str()); + + socket_.GetDomainNameAndPathFromUrl("http://10.21.200.68:8888", + domain_name_, + resource_path_); + EXPECT_STREQ("10.21.200.68", domain_name_.c_str()); + EXPECT_TRUE(resource_path_.empty()); +} + +TEST_F(HttpSocketTest, ConnectTest) +{ + EXPECT_TRUE(Connect(gTestServer)); + socket_.CloseSocket(); + EXPECT_FALSE(Connect("ww.g.c")); + socket_.CloseSocket(); +} + +TEST_F(HttpSocketTest, RoundTripTest) +{ + ASSERT_TRUE(Connect(gTestServer)); + EXPECT_TRUE(PostRequest(gTestData)); + GetResponse(); + socket_.CloseSocket(); +} + +} // namespace wvcdm + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + + std::string temp; + std::string test_server(gTestServer); + std::string test_data(gTestData); + for (int i=1; i"; + std::cout << "configure the test server url, please include http[s] in the url" << std::endl; + std::cout << std::setw(30) << std::left << " "; + std::cout << "default: " << test_server << std::endl; + std::cout << std::setw(30) << std::left << " --data="; + std::cout << "configure data to send, in ascii string format" << std::endl; + std::cout << std::setw(30) << std::left << " "; + std::cout << "default: " << test_data << std::endl << std::endl; + return 0; + } + } + + std::cout << std::endl; + std::cout << "Server: " << gTestServer << std::endl; + std::cout << "Data: " << gTestData << std::endl; + + return RUN_ALL_TESTS(); +} diff --git a/libwvdrmengine/cdm/core/test/license_request.cpp b/libwvdrmengine/cdm/core/test/license_request.cpp new file mode 100644 index 00000000..45322c29 --- /dev/null +++ b/libwvdrmengine/cdm/core/test/license_request.cpp @@ -0,0 +1,82 @@ +// Copyright 2013 Google Inc. All Rights Reserved. + +#include "license_request.h" +#include "log.h" + +namespace wvcdm { + +static const std::string kTwoBlankLines("\r\n\r\n"); + +size_t LicenseRequest::FindHeaderEndPosition( + const std::string& response) const { + return(response.find(kTwoBlankLines)); +} + +// Returns drm message in drm_msg. +// The drm message is at the end of the response message. +void LicenseRequest::GetDrmMessage(const std::string& response, + std::string& drm_msg) { + if (response.empty()) { + drm_msg.clear(); + return; + } + + // Extracts DRM message. + // Content-Length = GLS line + Header(s) + empty line + drm message; + // we use the empty line to locate the drm message, and compute + // the drm message length as below instead of using Content-Length + size_t header_end_pos = FindHeaderEndPosition(response); + if (header_end_pos != std::string::npos) { + header_end_pos += kTwoBlankLines.size(); // points to response body + + drm_msg.clear(); + size_t drm_msg_pos = response.find(kTwoBlankLines, header_end_pos); + if (drm_msg_pos != std::string::npos) { + drm_msg_pos += kTwoBlankLines.size(); // points to drm message + } else { + // For backward compatibility, no blank line after error code + drm_msg_pos = response.find("\r\n", header_end_pos); + if (drm_msg_pos != std::string::npos) { + drm_msg_pos += 2; // points to drm message + } + } + + if (drm_msg_pos != std::string::npos) { + drm_msg = response.substr(drm_msg_pos); + } else { + LOGE("drm msg not found"); + } + } else { + LOGE("response body not found"); + } +} + +// Returns heartbeat url in heartbeat_url. +// The heartbeat url is stored as meta data in the response message. +void LicenseRequest::GetHeartbeatUrl(const std::string& response, + std::string& heartbeat_url) { + if (response.empty()) { + heartbeat_url.clear(); // TODO: assign default heartbeat url + return; + } + + size_t header_end_pos = FindHeaderEndPosition(response); + if (header_end_pos != std::string::npos) { + header_end_pos += kTwoBlankLines.size(); // points to response body + + heartbeat_url.clear(); + size_t heartbeat_url_pos = response.find("Heartbeat-Url: ", + header_end_pos); + if (heartbeat_url_pos != std::string::npos) { + heartbeat_url_pos += sizeof("Heartbeat-Url: "); + heartbeat_url.assign(response.substr(heartbeat_url_pos)); + } else { + LOGE("heartbeat url not found"); + } + } else { + LOGE("response body not found"); + } +} + + +} // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/test/license_request.h b/libwvdrmengine/cdm/core/test/license_request.h new file mode 100644 index 00000000..11576c55 --- /dev/null +++ b/libwvdrmengine/cdm/core/test/license_request.h @@ -0,0 +1,30 @@ +// Copyright 2013 Google Inc. All Rights Reserved. + +#ifndef CDM_TEST_LICENSE_REQUEST_H_ +#define CDM_TEST_LICENSE_REQUEST_H_ + +#include +#include "wv_cdm_types.h" + +namespace wvcdm { + +// Parses response from a license request. +// This class assumes a particular response format defined by +// Google license servers. +class LicenseRequest { + public: + LicenseRequest() {}; + ~LicenseRequest() {}; + + void GetDrmMessage(const std::string& response, std::string& drm_msg); + void GetHeartbeatUrl(const std::string& response, std::string& heartbeat_url); + + private: + size_t FindHeaderEndPosition(const std::string& response) const; + + CORE_DISALLOW_COPY_AND_ASSIGN(LicenseRequest); +}; + +}; // namespace wvcdm + +#endif // CDM_TEST_LICENSE_REQUEST_H_ diff --git a/libwvdrmengine/cdm/core/test/license_unittest.cpp b/libwvdrmengine/cdm/core/test/license_unittest.cpp new file mode 100644 index 00000000..794115b9 --- /dev/null +++ b/libwvdrmengine/cdm/core/test/license_unittest.cpp @@ -0,0 +1,100 @@ +// Copyright 2012 Google Inc. All Rights Reserved. + +#include "crypto_engine.h" +#include "crypto_session.h" +#include "license.h" +#include "gtest/gtest.h" +#include "string_conversions.h" + +namespace { + +// The test data is based on key box Eureka-Dev-G1-0001520 +// This unit test should run on oemcrypto mock with the same key box +static const char* kInitData = "0801121093789920E8D6520098577DF8F2DD5546"; +static const char* kSignedRequest = + "080112790A4C0800124800000002000001241F344DB9DFF087F01D917910F39B" + "60DC7797CD97789EE82516DC07A478CB70B8C08C299293150AA8E01D2DC9808C" + "98DAA16E40A0E55DFE3618C7584DD3C7BE4212250A230A140801121093789920" + "E8D6520098577DF8F2DD554610011A09393837363534333231180120001A20FA" + "E2DDCD7F1ACA4B728EC957FEE802F8A5541557ACA784EE0D05BFCC0E65FEA1"; +static const char* kValidResponse = + "080212D9020A190A093938373635343332311208C434AB9240A9EF2420012800" + "120E0801180120809A9E0128809A9E011A461210B72EEBF582B04BDB15C2E0E3" + "20B21C351A30E51FC1D27F70DB8E0DDF8C051BD6E251A44599DBCE4E1BE663FD" + "3AFAB191A7DD5736841FB04CE558E7F17BD9812A2DBA20011A6E0A1093789920" + "E8D6520098577DF8F2DD55461210367E8714B6F10087AFDE542EDC5C91541A20" + "ED51D4E84D81C8CBD8E2046EE079F8A2016268A2F192B902FDA241FEEB10C014" + "200242240A109209D46191B8752147C9F6A1CE2BEE6E12107910F39B60DC7797" + "CD97789EE82516DC1A6E0A107B1328EB61B554E293F75B1E3E94CC3B1210676F" + "69BBDA35EE972B77BC1328A087391A20D2B9FA92B164F5F6362CAD9200A11661" + "B8F71E9CE671A3A252D34586526B68FA200242240A109D7B13420FD6217666CC" + "CD43860FAA3A1210DBCE4E1BE663FD3AFAB191A7DD57368420E9FDCE86051A20" + "C6279E32FD2CB9067229E87AFF4B2DE14A077CDF8F061DAEE2CC2D1BCDEF62D0"; +static const char* kInvalidResponse = + "0802128D020A190A093938373635343332311208BA68C949396C438C20012800" + "120E0801180120809A9E0128809A9E011A4612105021EB9AEDC1F73E96DE7DCC" + "6D7D72401A300A82E118C0BF0DB230FCADE3F49A9777DDD392322240FEF32C97" + "F85428E2F6CCFA638B5481464ADBCF199CEC2FCF3AFB20011A480A1093789920" + "E8D6520098577DF8F2DD55461210EE52C59B99050A36E10569AFB34D1DA41A20" + "C61FCB8019AC9ADE99FF8FCA99ED35E2331B6488A35102F9379AA42C87A22DC7" + "20021A480A107B1328EB61B554E293F75B1E3E94CC3B12101BBF5286B859E349" + "2E4A47A24C06AC1B1A2061F21836A04E558BEE0244EF41C165F60CF23C580275" + "3175D48BAF1C6CA5759F200220A2BCCA86051A203FD4671075D9DEC6486A9317" + "70669993306831EDD57D77F34EFEB467470BA364"; +} + +namespace wvcdm { + +class LicenseTest : public ::testing::Test { + protected: + virtual void SetUp() { + CryptoEngine* crypto_engine = CryptoEngine::GetInstance(); + EXPECT_TRUE(crypto_engine != NULL); + session_ = crypto_engine->CreateSession("Dummy"); + EXPECT_TRUE(session_ != NULL); + + std::string token; + EXPECT_TRUE(crypto_engine->GetToken(&token)); + + EXPECT_TRUE(session_->IsOpen()); + EXPECT_TRUE(license_.Init(token, session_)); + } + + virtual void TearDown() { + session_->Close(); + delete session_; + } + + CryptoSession* session_; + CdmLicense license_; +}; + +TEST(LicenseTestSession, InitNullSession) { + CdmLicense license; + EXPECT_FALSE(license.Init("Dummy", NULL)); +} + +TEST_F(LicenseTest, PrepareKeyRequest) { + std::string signed_request; + license_.PrepareKeyRequest(a2bs_hex(kInitData), &signed_request); + EXPECT_EQ(signed_request, a2bs_hex(kSignedRequest)); +} + +TEST_F(LicenseTest, HandleKeyResponseValid) { + std::string signed_request; + license_.PrepareKeyRequest(a2bs_hex(kInitData), &signed_request); + EXPECT_EQ(signed_request, a2bs_hex(kSignedRequest)); + EXPECT_TRUE(license_.HandleKeyResponse(a2bs_hex(kValidResponse))); +} + +TEST_F(LicenseTest, HandleKeyResponseInvalid) { + std::string signed_request; + license_.PrepareKeyRequest(a2bs_hex(kInitData), &signed_request); + EXPECT_EQ(signed_request, a2bs_hex(kSignedRequest)); + EXPECT_FALSE(license_.HandleKeyResponse(a2bs_hex(kInvalidResponse))); +} + +// TODO(kqyang): add unit test cases for PrepareKeyRenewalRequest +// and HandleRenewalKeyResponse + +} diff --git a/libwvdrmengine/cdm/core/test/url_request.cpp b/libwvdrmengine/cdm/core/test/url_request.cpp new file mode 100644 index 00000000..c1bd83ff --- /dev/null +++ b/libwvdrmengine/cdm/core/test/url_request.cpp @@ -0,0 +1,106 @@ +// Copyright 2013 Google Inc. All Rights Reserved. + +#include "url_request.h" + +#include "http_socket.h" +#include "log.h" + +namespace wvcdm { + +UrlRequest::UrlRequest(const std::string& url, const std::string& port) + : is_connected_(false), + port_("80"), + request_(""), + server_url_(url) +{ + if (!port.empty()) { + port_.assign(port); + } + if (socket_.Connect((server_url_).c_str(), port_, true)) { + LOGD("connected to %s", socket_.domain_name().c_str()); + is_connected_ = true; + } else { + LOGE("failed to connect to %s, port=%s", + socket_.domain_name().c_str(), port.c_str()); + } +} + +UrlRequest::~UrlRequest() +{ + socket_.CloseSocket(); +} + +void UrlRequest::AppendChunkToUpload(const std::string& data) { + // format of chunk: + // size of chunk in hex\r\n + // data\r\n + // . . . + // 0\r\n + + // buffer to store length of chunk + memset(buffer_, 0, kHttpBufferSize); + snprintf(buffer_, kHttpBufferSize, "%x\r\n", data.size()); + request_.append(buffer_); // appends size of chunk + LOGD("...\r\n%s", request_.c_str()); + request_.append(data); + request_.append("\r\n"); // marks end of data +} + +int UrlRequest::GetResponse(std::string& response) { + response.clear(); + + const int kTimeoutInMs = 1000; + int bytes = 0; + int total_bytes = 0; + do { + memset(buffer_, 0, kHttpBufferSize); + bytes = socket_.Read(buffer_, kHttpBufferSize, kTimeoutInMs); + if (bytes > 0) { + response.append(buffer_, bytes); + total_bytes += bytes; + } else { + if (bytes < 0) LOGE("read error = ", errno); + // bytes == 0 indicates nothing to read + } + } while (bytes > 0); + return total_bytes; +} + +int UrlRequest::GetStatusCode(const std::string& response) { + const std::string kHttpVersion("HTTP/1.1"); + + int status_code = -1; + size_t pos = response.find(kHttpVersion); + if (pos != std::string::npos) { + pos += kHttpVersion.size(); + sscanf(response.substr(pos).c_str(), "%d", &status_code); + } + return status_code; +} + +bool UrlRequest::PostRequest(const std::string& data) { + request_.assign("POST /"); + request_.append(socket_.resource_path()); + request_.append(" HTTP/1.1\r\n"); + request_.append("Host: "); + request_.append(socket_.domain_name()); + request_.append("\r\nConnection: Keep-Alive\r\n"); + request_.append("Transfer-Encoding: chunked\r\n"); + request_.append("User-Agent: Widevine CDM v1.0\r\n"); + request_.append("Accept-Encoding: gzip,deflate\r\n"); + request_.append("Accept-Language: en-us,fr\r\n"); + request_.append("Accept-Charset: iso-8859-1,*,utf-8\r\n"); + request_.append("\r\n"); // empty line to terminate header + + // calls AppendChunkToUpload repeatedly for multiple chunks + AppendChunkToUpload(data); + + // terminates last chunk with 0\r\n, then ends header with an empty line + request_.append("0\r\n\r\n"); + + socket_.Write(request_.c_str(), request_.size()); + return true; +} + + +} // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/test/url_request.h b/libwvdrmengine/cdm/core/test/url_request.h new file mode 100644 index 00000000..22a58880 --- /dev/null +++ b/libwvdrmengine/cdm/core/test/url_request.h @@ -0,0 +1,39 @@ +// Copyright 2013 Google Inc. All Rights Reserved. + +#ifndef CDM_TEST_URL_REQUEST_H_ +#define CDM_TEST_URL_REQUEST_H_ + +#include +#include "http_socket.h" +#include "wv_cdm_types.h" + +namespace wvcdm { + +// Provides simple HTTP request and response service. +// Only POST request method is implemented. +class UrlRequest { + public: + UrlRequest(const std::string& url, const std::string& port); + ~UrlRequest(); + + void AppendChunkToUpload(const std::string& data); + int GetResponse(std::string& response); + int GetStatusCode(const std::string& response); + bool is_connected() const { return is_connected_; } + bool PostRequest(const std::string& data); + + private: + static const unsigned int kHttpBufferSize = 4096; + char buffer_[kHttpBufferSize]; + bool is_connected_; + std::string port_; + std::string request_; + HttpSocket socket_; + std::string server_url_; + + CORE_DISALLOW_COPY_AND_ASSIGN(UrlRequest); +}; + +}; // namespace wvcdm + +#endif // CDM_TEST_URL_REQUEST_H_ diff --git a/libwvdrmengine/cdm/core/test/wvcdm_decryptor_unittest.cpp b/libwvdrmengine/cdm/core/test/wvcdm_decryptor_unittest.cpp new file mode 100644 index 00000000..b7cdcaaa --- /dev/null +++ b/libwvdrmengine/cdm/core/test/wvcdm_decryptor_unittest.cpp @@ -0,0 +1,861 @@ +// 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 +#include +#include +#include + +#include "base/at_exit.h" +#include "base/basictypes.h" +#include "base/bind.h" +#include "base/message_loop.h" +#include "base/sys_byteorder.h" +#include "crypto/encryptor.h" +#include "crypto/hmac.h" +#include "crypto/symmetric_key.h" +#include "license_protocol.pb.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "eureka/widevine_cdm/oemcrypto/client/oemcrypto_client.h" +#include "eureka/widevine_cdm/oemcrypto/mock/src/cmac.h" +#include "eureka/widevine_cdm/oemcrypto/mock/src/oemcrypto_keybox_mock.h" +#include "wv_cdm_types.h" +#include "wv_content_decryption_module.h" + +namespace { + +namespace wv_license_protocol = video_widevine_server::sdk; + +using wv_license_protocol::License; +using wv_license_protocol::LicenseIdentification; +using wv_license_protocol::LicenseRequest; +using wv_license_protocol::SessionState; +using wv_license_protocol::SignedMessage; + +enum PolicyType { + kDefault = 0, + kNoPlay, + kShortDuration +}; + +struct PolicyItem { + PolicyType type; + bool can_play; + bool can_renew; + int duration_seconds; + int renewal_delay_seconds; + int renewal_retry_interval_seconds; +}; + +struct PolicyItem PolicyItems[] = { + { + kDefault, + true, + true, + 1000, + 100, + 0 + }, + { + kShortDuration, + true, + true, + 12, + 2, + 2 + }, + { + kNoPlay, + false, + false, + 0, + 0, + 0 + } +}; + +// TODO(jfore): Move this into the test class. +/*const*/ char kTestSigningKey[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 +}; +const int kTestSigningKeySize = arraysize(kTestSigningKey); + +const char* kEncryptionKeyLabel = "ENCRYPTION"; +const uint32_t kEncryptionKeySizeBits = 128; +const char* kSigningKeyLabel = "AUTHENTICATION"; +const uint32_t kSigningKeySizeBits = 256; + +// This is a container to hold the info for an encrypted frame. +struct WvCdmEncryptedFrameInfo { + char plain_text[32]; + int plain_text_size; + uint8_t key_id[32]; + int key_id_size; + uint8_t content_key[32]; + int content_key_size; + uint8_t encrypted_data[64]; + int encrypted_data_size; + PolicyType policy_type; +}; + +const WvCdmEncryptedFrameInfo kWvCdmEncryptedFrames[] = { + { + "Original data.", 14, + { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35 + }, 16, + { 0xeb, 0xdd, 0x62, 0xf1, 0x68, 0x14, 0xd2, 0x7b, + 0x68, 0xef, 0x12, 0x2a, 0xfc, 0xe4, 0xae, 0x3c + }, 16, + { 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x82, 0x54, 0xab, 0x89, 0xa2, 0x75, 0xcd, + 0x85, 0x83, 0x61, 0x3f, 0xfe, 0x13, 0x58 + }, 23, + kShortDuration + }, + { + "Original data.", 14, + { 0x08, 0x01, 0x12, 0x10, 0x6f, 0x13, 0x33, 0xe7, + 0x6e, 0x59, 0x5e, 0xb5, 0x8c, 0x04, 0x30, 0x72, + 0xcb, 0xb2, 0x50, 0x68 + }, 20, + { 0xeb, 0xdd, 0x62, 0xf1, 0x68, 0x14, 0xd2, 0x7b, + 0x68, 0xef, 0x12, 0x2a, 0xfc, 0xe4, 0xae, 0x3c + }, 16, + { 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x82, 0x54, 0xab, 0x89, 0xa2, 0x75, 0xcd, + 0x85, 0x83, 0x61, 0x3f, 0xfe, 0x13, 0x58 + }, 23, + kShortDuration + }, + { + "Changed Original data.", 22, + { 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, + 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02 + }, 16, + { 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, + 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23 + }, 16, + { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x57, 0x66, 0xf4, 0x12, 0x1a, 0xed, 0xb5, + 0x79, 0x1c, 0x8e, 0x25, 0xd7, 0x17, 0xe7, 0x5e, + 0x16, 0xe3, 0x40, 0x08, 0x27, 0x11, 0xe9 + }, 31, + kShortDuration + }, + { + "Original data.", 14, + { 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f + }, 16, + { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40 + }, 16, + { 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9c, 0x71, 0x26, 0x57, 0x3e, 0x25, 0x37, + 0xf7, 0x31, 0x81, 0x19, 0x64, 0xce, 0xbc + }, 23, + kShortDuration + }, + // For license renewal test. This has kNoPlay. + { + // Differnent key and key id. + "Original data.", 14, + { 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, + 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33 + }, 16, + { 0xeb, 0xdd, 0x62, 0xf1, 0x68, 0x14, 0xd2, 0x7b, + 0x68, 0xef, 0x12, 0x2a, 0xfc, 0xe4, 0xae, 0x3c + }, 16, + { 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x82, 0x54, 0xab, 0x89, 0xa2, 0x75, 0xcd, + 0x85, 0x83, 0x61, 0x3f, 0xfe, 0x13, 0x58 + }, 23, + kNoPlay + } +}; + +bool GetPolicy(PolicyType policy_type, License::Policy* policy) { + DCHECK(policy); + PolicyItem policy_item; + switch (policy_type) { + case kDefault: + policy_item = PolicyItems[0]; + DCHECK_EQ(policy_item.type, kDefault); + break; + case kShortDuration: + policy_item = PolicyItems[1]; + DCHECK_EQ(policy_item.type, kShortDuration); + break; + case kNoPlay: + policy_item = PolicyItems[2]; + DCHECK_EQ(policy_item.type, kNoPlay); + break; + default: + NOTREACHED(); + return false; + } + policy->set_can_play(policy_item.can_play); + policy->set_can_renew(policy_item.can_renew); + policy->set_license_duration_seconds(policy_item.duration_seconds); + policy->set_renewal_delay_seconds(policy_item.renewal_delay_seconds); + policy->set_renewal_retry_interval_seconds( + policy_item.renewal_retry_interval_seconds); + return true; +} + +SessionState GetTestSessionState() { + static const std::string kTestSessionId = "SomeSessionId"; + SessionState session_cache; + session_cache.mutable_license_id()->set_session_id(kTestSessionId); + session_cache.mutable_license_id()->set_version(0); + session_cache.set_signing_key(kTestSigningKey, kTestSigningKeySize); + return session_cache; +} + +// Since "GetTime" is used in this test where some functions cannot reach +// Host instance, this will be used as a universal clock for this test. +double GetCurrentTestTime() { + return base::Time::Now().ToDoubleT(); +} + +// This encrypts the content key using the device key in cdm/base/device_key.h. +std::string EncryptUsingKey(const std::string& input, + const std::string& iv, + const std::string& encryption_key) { + //static const int kAesBlockSize = 16; + crypto::Encryptor aes_ecryptor; + scoped_ptr device_key( + crypto::SymmetricKey::Import(crypto::SymmetricKey::AES, + encryption_key)); + if (!aes_ecryptor.Init(device_key.get(), crypto::Encryptor::CBC, iv)) + return ""; + + std::string encrypted_data; + if (!aes_ecryptor.Encrypt(input, &encrypted_data)) + return ""; + + return encrypted_data; +} + +// Takes the license and the session state and generates a SignedMessage. The +// return value is the serialized version of the SignedMessage object. +std::string GenerateSignedLicenseResponse(const License& license, + const std::string& signing_key) { + SignedMessage signed_message; + bool success = license.SerializeToString( + signed_message.mutable_msg()); + DCHECK(success); + + crypto::HMAC hmacer(crypto::HMAC::SHA256); + if (!hmacer.Init(signing_key)) + return ""; + + static const int kDigestSize = 32; + uint8_t digest[kDigestSize] = { 0 }; + if (!hmacer.Sign(signed_message.msg(), digest, kDigestSize)) + return ""; + + signed_message.set_signature(digest, kDigestSize); + + std::string signed_message_bytes; + success = signed_message.SerializeToString(&signed_message_bytes); + DCHECK(success); + + return signed_message_bytes; +} + +// Note: We only use one session. So there aren't any list of sessions stored +// anywhere. +std::string GenerateNewSignedLicense( + const LicenseRequest& license_request, + const License::Policy& policies, + const License::KeyContainer& content_key, + const std::string& encryption_key, + const std::string& signing_key) { + DCHECK(license_request.content_id().has_cenc_id()); + SessionState session_cache = GetTestSessionState(); + DCHECK(session_cache.has_signing_key()); + + session_cache.mutable_license_id()->set_request_id( + license_request.content_id().webm_id().request_id()); + session_cache.mutable_license_id()->set_type( + license_request.content_id().webm_id().license_type()); + + License license; + license.mutable_id()->CopyFrom(session_cache.license_id()); + license.mutable_policy()->CopyFrom(policies); + license.set_license_start_time(GetCurrentTestTime()); + + License::KeyContainer* renewal_signing_key = license.add_key(); + renewal_signing_key->set_key(session_cache.signing_key()); + renewal_signing_key->set_type(License::KeyContainer::SIGNING); + + license.add_key()->CopyFrom(content_key); + for (int i = 0; i < license.key_size(); ++i) { + license.mutable_key(i)->set_iv("0123456789012345"); + license.mutable_key(i)->set_key( + EncryptUsingKey(license.key(i).key(), + license.key(i).iv(), + encryption_key)); + if (license.key(i).key().empty()) + return ""; + } + + return GenerateSignedLicenseResponse(license, signing_key); +} + +bool GetContentKeyFromKeyId(const std::string& key_id, + std::string* content_key) { + DCHECK(content_key); + for (unsigned int i = 0; i < arraysize(kWvCdmEncryptedFrames); ++i) { + const WvCdmEncryptedFrameInfo& frame = kWvCdmEncryptedFrames[i]; + if (frame.key_id_size != static_cast(key_id.size())) + continue; + if (!memcmp(frame.key_id, key_id.data(), frame.key_id_size)) { + content_key->assign(frame.content_key, + frame.content_key + frame.content_key_size); + return true; + } + } + return false; +} + +std::string DeriveKey(const std::string& key, + const std::string& purpose, + const std::string& context, + const uint32_t size_bits) { + if (key.size() != 16) + return ""; + + // We only handle even multiples of 16 bytes (128 bits) right now. + if ((size_bits % 128) || (size_bits > (128 * 255))) { + return ""; + } + + std::string result; + + const EVP_CIPHER *cipher = EVP_aes_128_cbc(); + CMAC_CTX* cmac_ctx = CMAC_CTX_new(); + + size_t reslen; + unsigned char res[128]; + unsigned char counter; + for (counter = 1; counter <= (size_bits / 128); counter++) { + if (!CMAC_Init(cmac_ctx, key.data(), key.size(), cipher, 0)) + break; + + std::string message; + message.append(1, counter); + message.append(purpose); + message.append(1, '\0'); + message.append(context); + uint32_t size_l = htonl(size_bits); + message.append(reinterpret_cast(&size_l), sizeof(size_l)); + if (!CMAC_Update(cmac_ctx, message.data(), message.size())) + break; + + if (!CMAC_Final(cmac_ctx, res, &reslen)) + break; + + result.append((const char*)res, reslen); + } + + CMAC_CTX_free(cmac_ctx); + + if (counter <= (size_bits / 128)) + return ""; + + return result; +} + +bool VerifyTestSignature(const std::string& message, + const std::string& signature, + const std::string& key) { + crypto::HMAC hmacer(crypto::HMAC::SHA256); + if (!hmacer.Init(key)) + return false; + + if (!hmacer.Verify(message, signature)) + return false; + + return true; +} + +std::string GenerateNewLicenseResponse(const LicenseRequest& license_request, + const PolicyType& policy_type) { + if (!license_request.content_id().has_cenc_id()) + return ""; + + std::string content_key_id = license_request.content_id().cenc_id().pssh(0); + + std::string content_key; + if (!GetContentKeyFromKeyId(content_key_id, + &content_key)) { + return ""; + } + + if (content_key_id.size() > 16) + content_key_id.resize(16); + + License::Policy policies; + if (!GetPolicy(policy_type, &policies)) + return ""; + + std::string context; + if (!license_request.SerializeToString(&context)) + return ""; + + wvoec_mock::WvKeybox keybox; + + // TODO(): Fix this to use a constant for key length. + std::string widevine_device_key(keybox.device_key().value()); + std::string encryption_key = DeriveKey(widevine_device_key, + std::string(kEncryptionKeyLabel), + context, + kEncryptionKeySizeBits); + std::string signing_key = DeriveKey(widevine_device_key, + std::string(kSigningKeyLabel), + context, + kSigningKeySizeBits); + + memcpy(kTestSigningKey, &signing_key[0], 32); + + License::KeyContainer key_container; + key_container.set_id(content_key_id); + key_container.set_key(content_key); + key_container.set_type(License::KeyContainer::CONTENT); + return GenerateNewSignedLicense(license_request, + policies, + key_container, + encryption_key, + signing_key); +} + +std::string GenerateLicenseRenewalResponse( + const SignedMessage& signed_message, + const PolicyType& policy_type) { + SessionState session_cache = GetTestSessionState(); + + LicenseRequest license_request; + if (!license_request.ParseFromString(signed_message.msg())) + return ""; + + std::string session_id = license_request.content_id().license(). + license_id().session_id(); + if (session_id.compare(session_cache.license_id().session_id())) + return ""; + + if (!VerifyTestSignature(signed_message.msg(), + signed_message.signature(), + session_cache.signing_key())) { + return ""; + } + session_cache.mutable_license_id()->set_version( + session_cache.license_id().version() + 1); + + License license; + license.mutable_id()->CopyFrom(session_cache.license_id()); + + // Always get Policy object with kDefault for renewal. + License::Policy policy; + GetPolicy(policy_type, &policy); + license.mutable_policy()->Swap(&policy); + license.set_license_start_time(GetCurrentTestTime()); + + return GenerateSignedLicenseResponse(license, session_cache.signing_key()); +} + +std::string GenerateLicenseResponse(const std::string& signed_request, + const PolicyType& policy) { + SignedMessage signed_message; + if (!signed_message.ParseFromString(signed_request)) + return ""; + + LicenseRequest license_request; + if (!license_request.ParseFromString(signed_message.msg())) + return ""; + + if (license_request.type() == LicenseRequest::NEW) { + return GenerateNewLicenseResponse(license_request, policy); + } else if (license_request.type() == LicenseRequest::RENEWAL) { + return GenerateLicenseRenewalResponse(signed_message, policy); + } + + return ""; +} + +struct WvCdmEncryptedData { + char plain_text[32]; + int plain_text_size; + uint8_t key_id[32]; + int key_id_size; + uint8_t content_key[32]; + int content_key_size; + uint8_t encrypted_data[64]; + int encrypted_data_size; + const char* license_response; + int license_response_size; +}; + +// Container used to pass data from GenerateLicenseRequest to Decrypt. +// TODO(rkuroiwa): This class was made before KeyMessage existed; this +// should be removed. +class LicenseRequestParameter { + public: + explicit LicenseRequestParameter(const WvCdmEncryptedData& frame) + : init_data(new uint8_t[frame.key_id_size]), + init_data_size(frame.key_id_size), + session_id(NULL), + session_id_size(0), + key_request(NULL), + key_request_size(0), + default_url(NULL), + default_url_size(0) { + memcpy(init_data.get(), frame.key_id, frame.key_id_size); + } + + ~LicenseRequestParameter() { + } + + scoped_array init_data; + int init_data_size; + scoped_array session_id; + int session_id_size; + scoped_array key_request; + int key_request_size; + scoped_array default_url; + int default_url_size; + + private: + DISALLOW_COPY_AND_ASSIGN(LicenseRequestParameter); +}; + +// |encrypted_data| is encrypted from |plain_text| using |key|. |key_id| is +// used to distinguish |key|. +struct WebmEncryptedData { + uint8 plain_text[32]; + int plain_text_size; + uint8 key_id[32]; + int key_id_size; + uint8 key[32]; + int key_size; + uint8 encrypted_data[64]; + int encrypted_data_size; +}; + +// Frames 0 & 1 are encrypted with the same key. Frame 2 is encrypted with a +// different key. Frame 3 is unencrypted. +const WebmEncryptedData kWebmEncryptedFrames[] = { + { + // plaintext + "Original data.", 14, + // key_id + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13 + }, 20, + // key + { 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, + 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23 + }, 16, + // encrypted_data + { 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xf0, 0xd1, 0x12, 0xd5, 0x24, 0x81, 0x96, + 0x55, 0x1b, 0x68, 0x9f, 0x38, 0x91, 0x85 + }, 23 + }, + { + // plaintext + "Changed Original data.", 22, + // key_id + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13 + }, 20, + // key + { 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, + 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23 + }, 16, + // encrypted_data + { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x57, 0x66, 0xf4, 0x12, 0x1a, 0xed, 0xb5, + 0x79, 0x1c, 0x8e, 0x25, 0xd7, 0x17, 0xe7, 0x5e, + 0x16, 0xe3, 0x40, 0x08, 0x27, 0x11, 0xe9 + }, 31 + }, + { + // plaintext + "Original data.", 14, + // key_id + { 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, + 0x2c, 0x2d, 0x2e, 0x2f, 0x30 + }, 13, + // key + { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40 + }, 16, + // encrypted_data + { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x9c, 0x71, 0x26, 0x57, 0x3e, 0x25, 0x37, + 0xf7, 0x31, 0x81, 0x19, 0x64, 0xce, 0xbc + }, 23 + }, + { + // plaintext + "Changed Original data.", 22, + // key_id + { 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, + 0x2c, 0x2d, 0x2e, 0x2f, 0x30 + }, 13, + // key + { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40 + }, 16, + // encrypted_data + { 0x00, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, + 0x20, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, + 0x6c, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2e + }, 23 + } +}; + +static const uint8 kWebmWrongSizedKey[] = { 0x20, 0x20 }; + +static const char kClearKeySystem[] = "org.w3.clearkey"; +static const char *kWidevineKeySystem = "com.widevine.alpha"; +static const char kKeyType[] = "any"; + +// All three ContentIdentification are supported but cenc_id only supports +// one key for now. +// Using key_id kCencTestRequest will return the content_key kCencTestRequest +// +static const char kCencTestRequest[] = "0123456789ABCDEF"; +static const char kCencTestContentKey[] = { + 0x16, 0x23, 0xa3, 0x16, 0x67, 0x6d, 0xb7, 0x70, + 0xfe, 0x78, 0xf6, 0x58, 0x42, 0xb8, 0x16, 0x3c +}; + +// System Id of the Widevine DRM system for identification in pssh +static const uint8 kWidevineSystemId[] = { + 0xED, 0xEF, 0x8B, 0xA9, 0x79, 0xD6, 0x4A, 0xCE, + 0xA3, 0xC8, 0x27, 0xDC, 0xD5, 0x1D, 0x21, 0xED, +}; + +static std::string EncodeInt32(int i) { + std::string s; + s.resize(sizeof(int)); + memcpy(&*s.begin(), &i, sizeof(int)); + return s; +} + +// Generate PSSH blob from init data +static std::string GeneratePSSHBlob(const uint8* init_data, + int init_data_length) { + std::string output; + + // 4 byte size of the PSSH atom, inclusive + int size = 4 + 4 + 4 + sizeof(kWidevineSystemId) + 4 + init_data_length; + output.append(EncodeInt32(base::HostToNet32(size))); + + // "pssh" + output.append("pssh"); + + // 4 byte flags, value 0 + int flag = 0; + output.append(EncodeInt32(base::HostToNet32(flag))); + + // 16 byte system id + output.append(reinterpret_cast(kWidevineSystemId), + sizeof(kWidevineSystemId)); + + // 4 byte size of PSSH data, exclusive + output.append(EncodeInt32(base::HostToNet32(init_data_length))); + + // pssh data + output.append(reinterpret_cast(init_data), + init_data_length); + + return output; +} + +}; // anonymous + +namespace wvcdm { + +class WvCdmDecryptorTest : public testing::Test { + + public: + WvCdmDecryptorTest() {} + + ~WvCdmDecryptorTest() {} + + protected: + void GenerateKeyRequest(const uint8* key_id, int key_id_size, + std::string& message_buffer) { + std::string init_data = GeneratePSSHBlob(key_id, key_id_size); + wvcdm::CdmResponseType res = decryptor_.GenerateKeyRequest( + kWidevineKeySystem, init_data, &message_buffer, &session_id_string_); + EXPECT_TRUE(res == wvcdm::KEY_MESSAGE); + } + + void PrepareForRenewalRequest(int i) { + } + + void GetRenewalMessage(std::string& message_buffer) { + } + + void GetFailedRenewalMessage(std::string& message_buffer) { + } + + public: + void AddKeyAndExpectToSucceed(const uint8* key_id, int key_id_size, + const uint8* key, int key_size) { + std::string cma_key((const char*)key, key_size); + std::string init_data = GeneratePSSHBlob(key_id, key_id_size); + wvcdm::CdmResponseType res = decryptor_.AddKey( + kWidevineKeySystem, init_data, cma_key, session_id_string_); + EXPECT_TRUE(res == wvcdm::KEY_ADDED); + } + + void AddKeyAndExpectToFail(const uint8* key_id, int key_id_size, + const uint8* key, int key_size) { + std::string cma_key((const char*)key, key_size); + std::string init_data = GeneratePSSHBlob(key_id, key_id_size); + wvcdm::CdmResponseType res = decryptor_.AddKey( + kWidevineKeySystem, init_data, cma_key, session_id_string_); + EXPECT_TRUE(res == wvcdm::KEY_ADDED); + } + protected: + + wvcdm::WvContentDecryptionModule decryptor_; + std::string session_id_string_; +}; + +TEST_F(WvCdmDecryptorTest, RenewalTest) { + std::string response; + std::string message; + const WvCdmEncryptedFrameInfo& frame = kWvCdmEncryptedFrames[3]; + License::Policy policies; + DCHECK(GetPolicy(frame.policy_type, &policies)); + + GenerateKeyRequest(frame.key_id, frame.key_id_size, message); + response = GenerateLicenseResponse(message, kShortDuration); + AddKeyAndExpectToSucceed(frame.key_id, frame.key_id_size, + reinterpret_cast(response.data()), + response.size()); + MessageLoop::current()->RunUntilIdle(); + PrepareForRenewalRequest(1); + sleep(policies.renewal_delay_seconds()); + MessageLoop::current()->RunUntilIdle(); + GetRenewalMessage(message); + response = GenerateLicenseResponse(message, frame.policy_type); + AddKeyAndExpectToSucceed(frame.key_id, frame.key_id_size, + reinterpret_cast(response.data()), + response.size()); +} + +TEST_F(WvCdmDecryptorTest, MultiRenewalTest) { + std::string response; + std::string message; + const WvCdmEncryptedFrameInfo& frame = kWvCdmEncryptedFrames[3]; + License::Policy policies; + DCHECK(GetPolicy(frame.policy_type, &policies)); + + GenerateKeyRequest(frame.key_id, frame.key_id_size, message); + response = GenerateLicenseResponse(message, kShortDuration); + AddKeyAndExpectToSucceed(frame.key_id, frame.key_id_size, + reinterpret_cast(response.data()), + response.size()); + MessageLoop::current()->RunUntilIdle(); + PrepareForRenewalRequest(1); + sleep(policies.renewal_delay_seconds()); + MessageLoop::current()->RunUntilIdle(); + GetRenewalMessage(message); + response = GenerateLicenseResponse(message, frame.policy_type); + AddKeyAndExpectToSucceed(frame.key_id, frame.key_id_size, + reinterpret_cast(response.data()), + response.size()); + + PrepareForRenewalRequest(2); + sleep(policies.renewal_delay_seconds()); + MessageLoop::current()->RunUntilIdle(); + GetRenewalMessage(message); +} + +TEST_F(WvCdmDecryptorTest, RenewalRetryTest_ExpectSuccess) { + std::string response; + std::string message; + const WvCdmEncryptedFrameInfo& frame = kWvCdmEncryptedFrames[3]; + License::Policy policies; + DCHECK(GetPolicy(frame.policy_type, &policies)); + + GenerateKeyRequest(frame.key_id, frame.key_id_size, message); + response = GenerateLicenseResponse(message, kShortDuration); + AddKeyAndExpectToSucceed(frame.key_id, frame.key_id_size, + reinterpret_cast(response.data()), + response.size()); + MessageLoop::current()->RunUntilIdle(); + + int loop_seconds = + policies.license_duration_seconds() - policies.renewal_delay_seconds(); + int loop_count = loop_seconds / policies.renewal_retry_interval_seconds(); + if (loop_seconds % policies.renewal_retry_interval_seconds()) + ++loop_count; + for (int i = 1; i <= loop_count; ++i) { + PrepareForRenewalRequest(i); + sleep(policies.renewal_delay_seconds()); + MessageLoop::current()->RunUntilIdle(); + GetRenewalMessage(message); + } + + response = GenerateLicenseResponse(message, frame.policy_type); + AddKeyAndExpectToSucceed(frame.key_id, frame.key_id_size, + reinterpret_cast(response.data()), + response.size()); +} + +TEST_F(WvCdmDecryptorTest, RenewalRetryTest_ExpectLicenseExpiration) { + std::string response; + std::string message; + const WvCdmEncryptedFrameInfo& frame = kWvCdmEncryptedFrames[3]; + License::Policy policies; + DCHECK(GetPolicy(frame.policy_type, &policies)); + + GenerateKeyRequest(frame.key_id, frame.key_id_size, message); + response = GenerateLicenseResponse(message, kShortDuration); + AddKeyAndExpectToSucceed(frame.key_id, frame.key_id_size, + reinterpret_cast(response.data()), + response.size()); + MessageLoop::current()->RunUntilIdle(); + + int loop_seconds = + policies.license_duration_seconds() - policies.renewal_delay_seconds(); + int loop_count = loop_seconds / policies.renewal_retry_interval_seconds() + 1; + if (loop_seconds % policies.renewal_retry_interval_seconds()) + ++loop_count; + + for (int i = 1; i <= loop_count; ++i) { + PrepareForRenewalRequest(i); + sleep(i > 1 ? + policies.renewal_retry_interval_seconds() : + policies.renewal_delay_seconds()); + MessageLoop::current()->RunUntilIdle(); + if (i < loop_count) + GetRenewalMessage(message); + } + GetFailedRenewalMessage(message); +} + +} // namespace wvcdm + +// TODO(rkuroiwa): Find where to put this main function just for Widevine CDM +// unit tests. +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + base::AtExitManager exit; + MessageLoop ttr(MessageLoop::TYPE_IO); + return RUN_ALL_TESTS(); +} diff --git a/libwvdrmengine/cdm/core/test/wvcdm_test.cpp b/libwvdrmengine/cdm/core/test/wvcdm_test.cpp new file mode 100644 index 00000000..55076ba7 --- /dev/null +++ b/libwvdrmengine/cdm/core/test/wvcdm_test.cpp @@ -0,0 +1,201 @@ +// 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 + +#include "base/at_exit.h" +#include "net/url_request/url_request.h" +#include "net/url_request/url_request_test_util.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "string_conversions.h" +#include "wv_content_decryption_module.h" + +namespace { + +// Default license server to play server, can be configured using --server command line option +std::string gGpLicenseServer = + "https://jmt17.google.com/video-dev/license/GetCencLicense"; +std::string gYtLicenseServer = + "https://www.youtube.com/api/drm/widevine?video_id=03681262dc412c06&source=YOUTUBE"; + +std::string gLicenseServer(gYtLicenseServer); + +// Default key id (pssh), can be configured using --keyid command line option +std::string gKeyID = "000000347073736800000000" // blob size and pssh + "EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id + "08011210e02562e04cd55351b14b3d748d36ed8e"; // pssh data + +// An invalid key id, expected to fail +std::string kWrongKeyID = "000000347073736800000000" // blob size and psshb + "EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id + "0901121094889920E8D6520098577DF8F2DD5546"; // pssh data + +static const char kWidevineKeySystem[] = "com.widevine.alpha"; + +} // namespace + +namespace wvcdm_test { + +class WvCdmDecryptorTest : public testing::Test { + public: + WvCdmDecryptorTest() {} + ~WvCdmDecryptorTest() {} + + protected: + void GenerateKeyRequest(const std::string& key_system, + const std::string& init_data) { + EXPECT_EQ(decryptor_.GenerateKeyRequest(key_system, + init_data, + &key_msg_, + &session_id_), wvcdm::KEY_MESSAGE); + } + + void GenerateRenewalRequest(const std::string& key_system, + const std::string& init_data) { + EXPECT_EQ(decryptor_.GenerateRenewalRequest(key_system, + init_data, + session_id_, &key_msg_), + wvcdm::KEY_MESSAGE); + } + + std::string GetKeyRequestResponse(const std::string& server_url, + int expected_response) { + net::TestDelegate d; + net::TestNetworkDelegate network_delegate; + net::TestURLRequestContext context(true); + context.set_network_delegate(&network_delegate); + scoped_ptr resolver( + net::HostResolver::CreateDefaultResolver(NULL)); + context.set_host_resolver(resolver.get()); + context.Init(); + net::URLRequest r(GURL(server_url), &d, &context); + r.EnableChunkedUpload(); + r.set_method("POST"); + r.AppendChunkToUpload(key_msg_.data(), key_msg_.size(), true); + r.Start(); + EXPECT_TRUE(r.is_pending()); + + MessageLoop::current()->Run(); + + std::string data = d.data_received(); + // Youtube server returns 400 for invalid message while play server returns + // 500, so just test inequity here for invalid message + if (expected_response == 200) { + EXPECT_EQ(200, r.GetResponseCode()) << data; + } else { + EXPECT_NE(200, r.GetResponseCode()) << data; + } + EXPECT_EQ(net::URLRequestStatus::SUCCESS, r.status().status()); + EXPECT_TRUE(d.bytes_received() > 0); + // Extract DRM message: + // https://docs.google.com/a/google.com/document/d/1Xue3bgwv2qIAnuFIZ-HCcix43dvH2UxsOEA_8FCBO3I/edit# + if (r.status().status() == net::URLRequestStatus::SUCCESS) { + size_t pos = data.find("\r\n"); + if (pos != data.npos) data = data.substr(pos+2); + } + return data; + } + + void VerifyKeyRequestResponse(const std::string& server_url, + std::string& init_data, + bool is_renewal) { + std::string resp = GetKeyRequestResponse(server_url, 200); + + std::cout << "Message: " << wvcdm::b2a_hex(key_msg_) << std::endl; + std::cout << "Response: " << wvcdm::b2a_hex(resp) << std::endl; + + if (is_renewal) { + EXPECT_EQ(decryptor_.RenewKey(kWidevineKeySystem, + init_data, resp, + session_id_), wvcdm::KEY_ADDED); + } + else { + EXPECT_EQ(decryptor_.AddKey(kWidevineKeySystem, + init_data, resp, + session_id_), wvcdm::KEY_ADDED); + } + + std::cout << "back from AddKey" << std::endl; + + } + + wvcdm::WvContentDecryptionModule decryptor_; + + std::string key_msg_; + std::string session_id_; +}; + +TEST_F(WvCdmDecryptorTest, BaseMessageTest) +{ + GenerateKeyRequest(kWidevineKeySystem, gKeyID); + + GetKeyRequestResponse(gLicenseServer, 200); +} + +TEST_F(WvCdmDecryptorTest, WrongMessageTest) +{ + std::string wrong_message = wvcdm::a2b_hex(kWrongKeyID); + + GenerateKeyRequest(kWidevineKeySystem, wrong_message); + + GetKeyRequestResponse(gLicenseServer, 500); +} + +TEST_F(WvCdmDecryptorTest, NormalWebMDecryption) { + GenerateKeyRequest(kWidevineKeySystem, gKeyID); + + VerifyKeyRequestResponse(gLicenseServer, gKeyID, false); +} + +TEST_F(WvCdmDecryptorTest, LicenseRenewal) { + GenerateKeyRequest(kWidevineKeySystem, gKeyID); + + VerifyKeyRequestResponse(gLicenseServer, gKeyID, false); + + GenerateRenewalRequest(kWidevineKeySystem, gKeyID); + + VerifyKeyRequestResponse(gLicenseServer, gKeyID, true); +} + +} // namespace wvcdm_test + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + + std::string temp; + std::string license_server(gLicenseServer); + std::string key_id(gKeyID); + for (int i=1; i"; + std::cout << "configure the license server url, please include http[s] in the url" << std::endl; + std::cout << std::setw(30) << std::left << " "; + std::cout << "default: " << license_server << std::endl; + std::cout << std::setw(30) << std::left << " --keyid="; + std::cout << "configure the key id or pssh, in hex format" << std::endl; + std::cout << std::setw(30) << std::left << " "; + std::cout << "default: " << key_id << std::endl << std::endl; + return 0; + } + } + + std::cout << std::endl; + std::cout << "Server: " << gLicenseServer << std::endl; + std::cout << "KeyID: " << gKeyID << std::endl << std::endl; + + gKeyID = wvcdm::a2b_hex(gKeyID); + + base::AtExitManager exit; + MessageLoop ttr(MessageLoop::TYPE_IO); + return RUN_ALL_TESTS(); +} diff --git a/libwvdrmengine/cdm/include/ContentDecryptionModule.h b/libwvdrmengine/cdm/include/ContentDecryptionModule.h deleted file mode 100644 index aa7754f2..00000000 --- a/libwvdrmengine/cdm/include/ContentDecryptionModule.h +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2012 Google Inc. All Rights Reserved. - */ - -#ifndef WV_CONTENT_DECRYPTION_MODULE_H_ -#define WV_CONTENT_DECRYPTION_MODULE_H_ - -#include -#include -#include -#include "sys/types.h" - -#include - -namespace wvcdm { - -class CdmDecryptor; - -/** - * This class allows the invoker of the ContentDecryptionModule to receive - * license requests and error information. The invoker should provide a - * concrete implementation for this class. - */ -class CdmClient -{ -public: - typedef enum { - kErrorInvalidInitData = 0, - kErrorInvalidKeyResponse = 1, - kErrorInvalidSessionId = 2, - kErrorCdmDecryptorInitializationFailed = 3, - kErrorKeyAddFailed = 4 - } Error; - - CdmClient(); - virtual ~CdmClient(); - - virtual void keyMessage(const android::String8& sessionId, - const android::Vector& message, - const android::String8& defaultUrl) = 0; - - virtual void keyAdded(const android::String8& sessionId) = 0; - virtual void keyError(const android::String8& sessionId, Error error) = 0; -}; - -/** - * The ContentDecryptionModule provides a mechanism to create a license - * request, handle the license response and handle decryption of content. - */ -class ContentDecryptionModule -{ - -public: - explicit ContentDecryptionModule(CdmClient* client); - virtual ~ContentDecryptionModule(); - - /** - * This generates a license request message and session ID for given - * initialization data. - * - * @param[in] initData contains Content Protection system specific data - * (initialization or PSSH data). - * @return false on error. - * @note CdmClient::keyMessage and CdmClient::keyError may be called - * on successful license generation or error, respectively. - */ - bool generateKeyRequest(const android::Vector& initData); - - /** - * This takes in a license response and sets up the crypto content - * in preparation for decryption for a given session. - * - * @param[in] sessionId identifies the license request message created by - * generateKeyRequest though the CdmClient::KeyMessage method. - * @param[in] keyResponse is the license response from the license server. - * @note CdmClient::keyError may be called on error. - */ - void addKey(const android::String8& sessionId, - const android::Vector& keyResponse); - - /** - * TODO:rfrias,edwinwong: These interfaces are pending finalization - * of OEMCrypto APIs - */ - void decryptVideo(const uint8_t* keyId, const uint8_t* iv, - size_t ivLength, const void* input, - const android::CryptoPlugin::SubSample* subSamples, - size_t numSubSamples, void* outputHandle, size_t* outputOffset, - size_t* outputLength); - - /** - * TODO:rfrias,edwinwong: These interfaces are pending finalization - * of OEMCrypto APIs - */ - void decryptAudio(const uint8_t* keyId, const uint8_t* iv, - size_t ivLength, const void* input, - const android::CryptoPlugin::SubSample* subSamples, - size_t numSubSamples, void* output, size_t* outputLength); - - /** - * This releases resources and crypto contexts associated with a - * given session. - * - * @param[in] sessionId identifies a license and associated crypto - * contexts - */ - virtual void closeSession(const android::String8& sessionId); - -private: - CdmClient* const mClient; - android::KeyedVector mSessionIdToDecryptorMap; - - DISALLOW_EVIL_CONSTRUCTORS(ContentDecryptionModule); -}; - -} // namespace wvcdm - -#endif // WV_CONTENT_DECRYPTION_MODULE_H_ diff --git a/libwvdrmengine/cdm/include/wv_content_decryption_module.h b/libwvdrmengine/cdm/include/wv_content_decryption_module.h new file mode 100644 index 00000000..a3781df3 --- /dev/null +++ b/libwvdrmengine/cdm/include/wv_content_decryption_module.h @@ -0,0 +1,81 @@ +// Copyright 2013 Google Inc. All Rights Reserved. + +#ifndef CDM_BASE_WV_CONTENT_DECRYPTION_MODULE_H_ +#define CDM_BASE_WV_CONTENT_DECRYPTION_MODULE_H_ + +#include "wv_cdm_types.h" + +#include "utils/UniquePtr.h" + +namespace wvcdm { + +class CdmEngine; +class WvCdmEventListener; + +class WvContentDecryptionModule { + public: + WvContentDecryptionModule(); + virtual ~WvContentDecryptionModule(); + + // Session related methods + virtual CdmResponseType OpenSession(const CdmKeySystem& key_system, + CdmSessionId* session_id); + virtual CdmResponseType CloseSession(CdmSessionId& session_id); + + // Construct a valid license request. + virtual CdmResponseType GenerateKeyRequest(const CdmSessionId& session_id, + const CdmInitData& init_data, + const CdmLicenseType license_type, + CdmNameValueMap& app_parameters, + CdmKeyMessage* key_request); + + // Accept license response and extract key info. + virtual CdmResponseType AddKey(const CdmSessionId& session_id, + const CdmKeyResponse& key_data); + + // Cancel session + virtual CdmResponseType CancelKeyRequest(const CdmSessionId& session_id); + + // Query license information + virtual CdmResponseType QueryKeyStatus(const CdmSessionId& session_id, + CdmNameValueMap* key_info); + + // Provisioning related methods + virtual CdmResponseType GetProvisioningRequest( + CdmProvisioningRequest* request, + std::string* default_url); + + virtual CdmResponseType HandleProvisioningResponse( + CdmProvisioningResponse& response); + + // Secure stop related methods + virtual CdmResponseType GetSecureStops(CdmSecureStops* secure_stops); + virtual CdmResponseType ReleaseSecureStops( + const CdmSecureStopReleaseMessage& message); + + // Accept encrypted buffer and return decrypted data. + virtual CdmResponseType Decrypt(const CdmSessionId& session_id, + bool is_encrypted, + const KeyId& key_id, + const uint8_t* encrypted_buffer, + size_t encrypted_size, + const std::vector& iv, + size_t block_offset, + void* decrypted_buffer); + + // Event listener related methods + virtual bool AttachEventListener(CdmSessionId& session_id, + WvCdmEventListener* listener); + virtual bool DetachEventListener(CdmSessionId& session_id, + WvCdmEventListener* listener); + private: + + // instance variables + UniquePtr cdm_engine_; + + CORE_DISALLOW_COPY_AND_ASSIGN(WvContentDecryptionModule); +}; + +} // namespace wvcdm + +#endif // CDM_BASE_WV_CONTENT_DECRYPTION_MODULE_H_ diff --git a/libwvdrmengine/cdm/src/TODO b/libwvdrmengine/cdm/src/TODO deleted file mode 100644 index d4292a81..00000000 --- a/libwvdrmengine/cdm/src/TODO +++ /dev/null @@ -1,2 +0,0 @@ -Rahul and Edwin fill this out -Rahul and Edwin write tests diff --git a/libwvdrmengine/cdm/src/clock.cpp b/libwvdrmengine/cdm/src/clock.cpp new file mode 100644 index 00000000..a1fc398b --- /dev/null +++ b/libwvdrmengine/cdm/src/clock.cpp @@ -0,0 +1,19 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Clock - implemented using the standard linux time library + +#include "clock.h" + +#include + +namespace wvcdm { + +int64_t GetCurrentTime() { + struct timeval tv; + tv.tv_sec = tv.tv_usec = 0; + gettimeofday(&tv, NULL); + return tv.tv_sec; +} + + +}; // namespace wvcdm diff --git a/libwvdrmengine/cdm/src/lock.cpp b/libwvdrmengine/cdm/src/lock.cpp new file mode 100644 index 00000000..49404597 --- /dev/null +++ b/libwvdrmengine/cdm/src/lock.cpp @@ -0,0 +1,56 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Lock class - provides a simple android specific mutex implementation + +#include "lock.h" +#include "utils/Mutex.h" + +namespace wvcdm { + +class Lock::Impl { + public: + android::Mutex lock_; +}; + +Lock::Lock() : impl_(new Lock::Impl()) { +} + +Lock::~Lock() { + delete impl_; + impl_ = NULL; +} + +void Lock::Acquire() { + impl_->lock_.lock(); +} + +void Lock::Release() { + impl_->lock_.unlock(); +} + +bool Lock::Try() { + return (impl_->lock_.tryLock() == 0); +} + +class AutoLock::Impl { + public: + android::Mutex::Autolock *autolock_; +}; + +AutoLock::AutoLock(Lock& lock) : impl_(new AutoLock::Impl()) { + impl_->autolock_ = new android::Mutex::Autolock(lock.impl_->lock_); +} + +AutoLock::AutoLock(Lock* lock) : impl_(new AutoLock::Impl()) { + impl_->autolock_ = new android::Mutex::Autolock(lock->impl_->lock_); +} + +AutoLock::~AutoLock() { + delete impl_->autolock_; + delete impl_; + impl_ = NULL; +} + +}; // namespace wvcdm + + diff --git a/libwvdrmengine/cdm/src/log.cpp b/libwvdrmengine/cdm/src/log.cpp new file mode 100644 index 00000000..56618032 --- /dev/null +++ b/libwvdrmengine/cdm/src/log.cpp @@ -0,0 +1,33 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Log - implemented using the standard Android logging mechanism + +#define LOG_TAG "WVCdm" +#define LOG_BUF_SIZE 1024 + +#include "log.h" +#include "utils/Log.h" + +namespace wvcdm { + +void log_write(LogPriority level, const char *fmt, ...) { + va_list ap; + char buf[LOG_BUF_SIZE]; + va_start(ap, fmt); + vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); + va_end(ap); + + android_LogPriority prio = ANDROID_LOG_VERBOSE; + + switch(level) { + case LOG_ERROR: prio = ANDROID_LOG_ERROR; break; + case LOG_WARN: prio = ANDROID_LOG_WARN; break; + case LOG_INFO: prio = ANDROID_LOG_INFO; break; + case LOG_DEBUG: prio = ANDROID_LOG_DEBUG; break; + case LOG_VERBOSE: prio = ANDROID_LOG_VERBOSE; break; + } + + __android_log_write(prio, LOG_TAG, buf); +} + +}; // namespace wvcdm diff --git a/libwvdrmengine/cdm/src/timer.cpp b/libwvdrmengine/cdm/src/timer.cpp new file mode 100644 index 00000000..8e5028dd --- /dev/null +++ b/libwvdrmengine/cdm/src/timer.cpp @@ -0,0 +1,57 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Timer class - provides a simple Android specific timer implementation + +#include + +#include "timer.h" +#include "utils/Thread.h" + +namespace wvcdm { + +class Timer::Impl : public android::Thread { + public: + Impl() : Thread(false), handler_(NULL), period_(0) {} + virtual ~Impl() {}; + + void Start(TimerHandler *handler, uint32_t time_in_secs) { + handler_ = handler; + period_ = time_in_secs; + run(); + } + + private: + virtual bool threadLoop() { + sleep(period_); + handler_->OnTimerEvent(); + return true; + } + + TimerHandler *handler_; + uint32_t period_; +}; + +Timer::Timer() : impl_(new Timer::Impl()) { +} + +Timer::~Timer() { + if (IsRunning()) + Stop(); + + delete impl_; + impl_ = NULL; +} + +void Timer::Start(TimerHandler *handler, uint32_t time_in_secs) { + impl_->Start(handler, time_in_secs); +} + +void Timer::Stop() { + impl_->requestExitAndWait(); +} + +bool Timer::IsRunning() { + return impl_->getTid() < 0; +} + +} // namespace wvcdm diff --git a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp new file mode 100644 index 00000000..206981d2 --- /dev/null +++ b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp @@ -0,0 +1,112 @@ +// Copyright 2013 Google Inc. All Rights Reserved. + +#include "wv_content_decryption_module.h" + +#include + +#include "cdm_engine.h" +#include "log.h" +#include "wv_cdm_constants.h" +#include "wv_cdm_event_listener.h" + +namespace wvcdm { + +WvContentDecryptionModule::WvContentDecryptionModule() : + cdm_engine_(new CdmEngine()) { +} + +WvContentDecryptionModule::~WvContentDecryptionModule() { +} + +CdmResponseType WvContentDecryptionModule::OpenSession( + const CdmKeySystem& key_system, + CdmSessionId* session_id) { + return cdm_engine_->OpenSession(key_system, session_id); +} + +CdmResponseType WvContentDecryptionModule::CloseSession( + CdmSessionId& session_id) { + return cdm_engine_->CloseSession(session_id); +} + +CdmResponseType WvContentDecryptionModule::GenerateKeyRequest( + const CdmSessionId& session_id, + const CdmInitData& init_data, + const CdmLicenseType license_type, + CdmNameValueMap& app_parameters, + CdmKeyMessage* key_request) { + CdmKeySystem key_system; + return cdm_engine_->GenerateKeyRequest(session_id, false, key_system, + init_data, license_type, + app_parameters, key_request); +} + +CdmResponseType WvContentDecryptionModule::AddKey( + const CdmSessionId& session_id, + const CdmKeyResponse& key_data) { + CdmKeySystem key_system; + CdmInitData init_data; + return cdm_engine_->AddKey(session_id, false, key_system, + init_data, key_data); +} + +CdmResponseType WvContentDecryptionModule::CancelKeyRequest( + const CdmSessionId& session_id) { + CdmKeySystem key_system; + return cdm_engine_->CancelKeyRequest(session_id, false, key_system); +} + +CdmResponseType WvContentDecryptionModule::QueryKeyStatus( + const CdmSessionId& session_id, + CdmNameValueMap* key_info) { + return cdm_engine_->QueryKeyStatus(session_id, key_info); +} + +CdmResponseType WvContentDecryptionModule::GetProvisioningRequest( + CdmProvisioningRequest* request, + std::string* default_url) { + return cdm_engine_->GetProvisioningRequest(request, default_url); +} + +CdmResponseType WvContentDecryptionModule::HandleProvisioningResponse( + CdmProvisioningResponse& response) { + return cdm_engine_->HandleProvisioningResponse(response); +} + +CdmResponseType WvContentDecryptionModule::GetSecureStops( + CdmSecureStops* secure_stops) { + return cdm_engine_->GetSecureStops(secure_stops); +} + +CdmResponseType WvContentDecryptionModule::ReleaseSecureStops( + const CdmSecureStopReleaseMessage& message) { + return cdm_engine_->ReleaseSecureStops(message); +} + +CdmResponseType WvContentDecryptionModule::Decrypt( + const CdmSessionId& session_id, + bool is_encrypted, + const KeyId& key_id, + const uint8_t* encrypted_buffer, + size_t encrypted_size, + const std::vector& iv, + size_t block_offset, + void* decrypted_buffer) { + return cdm_engine_->Decrypt(session_id, is_encrypted, key_id, + encrypted_buffer, encrypted_size, iv, + block_offset, decrypted_buffer); +} + +bool WvContentDecryptionModule::AttachEventListener( + CdmSessionId& session_id, + WvCdmEventListener* listener) { + return cdm_engine_->AttachEventListener(session_id, listener); +} + +bool WvContentDecryptionModule::DetachEventListener( + CdmSessionId& session_id, + WvCdmEventListener* listener) { + return cdm_engine_->DetachEventListener(session_id, listener); +} + +} // namespace wvcdm diff --git a/libwvdrmengine/cdm/test/TODO b/libwvdrmengine/cdm/test/TODO deleted file mode 100644 index d4292a81..00000000 --- a/libwvdrmengine/cdm/test/TODO +++ /dev/null @@ -1,2 +0,0 @@ -Rahul and Edwin fill this out -Rahul and Edwin write tests diff --git a/libwvdrmengine/cdm/test/request_license_test.cpp b/libwvdrmengine/cdm/test/request_license_test.cpp new file mode 100644 index 00000000..4772a753 --- /dev/null +++ b/libwvdrmengine/cdm/test/request_license_test.cpp @@ -0,0 +1,249 @@ +// Copyright 2013 Google Inc. All Rights Reserved. + +#include +#include + +#include "config_test_env.h" +#include "gtest/gtest.h" +#include "license_request.h" +#include "log.h" +#include "string_conversions.h" +#include "url_request.h" +#include "wv_content_decryption_module.h" + +namespace { +// Default license server, can be configured using --server command line option +// Default key id (pssh), can be configured using --keyid command line option +std::string g_client_auth; +wvcdm::KeyId g_key_id; +wvcdm::CdmKeySystem g_key_system; +std::string g_license_server; +std::string g_port; +wvcdm::KeyId g_wrong_key_id; +int g_use_full_path = 0; // cannot use boolean in getopt_long +} // namespace + +namespace wvcdm { + +class WvCdmRequestLicenseTest : public testing::Test { + public: + WvCdmRequestLicenseTest() {} + ~WvCdmRequestLicenseTest() {} + + protected: + void GenerateKeyRequest(const std::string& key_system, + const std::string& init_data) { + wvcdm::CdmNameValueMap app_parameters; + EXPECT_EQ(decryptor_.GenerateKeyRequest(session_id_, + init_data, + kLicenseTypeStreaming, + app_parameters, + &key_msg_), wvcdm::KEY_MESSAGE); + } + + void GenerateRenewalRequest(const std::string& key_system, + const std::string& init_data) { + // TODO application makes a license request, CDM will renew the license + // when appropriate. + wvcdm::CdmNameValueMap app_parameters; + EXPECT_EQ(decryptor_.GenerateKeyRequest(session_id_, + init_data, + kLicenseTypeStreaming, + app_parameters, + &key_msg_), wvcdm::KEY_MESSAGE); + } + + // posts a request and extracts the drm message from the response + std::string GetKeyRequestResponse(const std::string& server_url, + const std::string& client_auth, + int expected_response) { + UrlRequest url_request(server_url + client_auth, g_port); + + if (!url_request.is_connected()) { + return ""; + } + + url_request.PostRequest(key_msg_); + std::string response; + int resp_bytes = url_request.GetResponse(response); + LOGD("response:\r\n%s", response.c_str()); + LOGD("end %d bytes response dump", resp_bytes); + + // Youtube server returns 400 for invalid message while play server returns + // 500, so just test inequity here for invalid message + int status_code = url_request.GetStatusCode(response); + if (expected_response == 200) { + EXPECT_EQ(200, status_code); + } else { + EXPECT_NE(200, status_code); + } + + std::string drm_msg; + if (200 == status_code) { + LicenseRequest lic_request; + lic_request.GetDrmMessage(response, drm_msg); + LOGV("drm msg: %u bytes\r\n%s", drm_msg.size(), + HexEncode(reinterpret_cast(drm_msg.data()), + drm_msg.size()).c_str()); + } + return drm_msg; + } + + void VerifyKeyRequestResponse(const std::string& server_url, + const std::string& client_auth, + std::string& init_data, + bool is_renewal) { + std::string resp = GetKeyRequestResponse(server_url, + client_auth, + 200); + + if (is_renewal) { + // TODO application makes a license request, CDM will renew the license + // when appropriate + wvcdm::CdmNameValueMap app_parameters; + EXPECT_EQ(decryptor_.GenerateKeyRequest(session_id_, + init_data, + kLicenseTypeStreaming, + app_parameters, + &key_msg_), wvcdm::KEY_ADDED); + } + else { + EXPECT_EQ(decryptor_.AddKey(session_id_, resp), wvcdm::KEY_ADDED); + } + } + + wvcdm::WvContentDecryptionModule decryptor_; + std::string key_msg_; + std::string session_id_; +}; + +TEST_F(WvCdmRequestLicenseTest, BaseMessageTest) { + decryptor_.OpenSession(g_key_system, &session_id_); + GenerateKeyRequest(g_key_system, g_key_id); + GetKeyRequestResponse(g_license_server, g_client_auth, 200); + decryptor_.CloseSession(session_id_); +} + +TEST_F(WvCdmRequestLicenseTest, WrongMessageTest) { + decryptor_.OpenSession(g_key_system, &session_id_); + + std::string wrong_message = wvcdm::a2bs_hex(g_wrong_key_id); + GenerateKeyRequest(g_key_system, wrong_message); + GetKeyRequestResponse(g_license_server, g_client_auth, 500); + decryptor_.CloseSession(session_id_); +} + +TEST_F(WvCdmRequestLicenseTest, NormalDecryption) { + decryptor_.OpenSession(g_key_system, &session_id_); + GenerateKeyRequest(g_key_system, g_key_id); + VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); + decryptor_.CloseSession(session_id_); +} + +#if 0 +// TODO License renewal is not yet implented +TEST_F(WvCdmRequestLicenseTest, LicenseRenewal) { + decryptor_.OpenSession(g_key_system, &session_id_); + GenerateKeyRequest(g_key_system, g_key_id); + VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); + + GenerateRenewalRequest(g_key_system, g_key_id); + VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, true); + decryptor_.CloseSession(session_id_); +} +#endif + +} // namespace wvcdm + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + + wvcdm::ConfigTestEnv config; + g_client_auth.assign(config.client_auth()); + g_key_system.assign(config.key_system()); + g_wrong_key_id.assign(config.wrong_key_id()); + + // The following variables are configurable through command line options. + g_license_server.assign(config.license_server()); + g_key_id.assign(config.key_id()); + g_port.assign(config.port()); + std::string license_server(g_license_server); + + int show_usage = 0; + static const struct option long_options[] = { + { "use_full_path", no_argument, &g_use_full_path, 0 }, + { "keyid", required_argument, NULL, 'k' }, + { "port", required_argument, NULL, 'p' }, + { "server", required_argument, NULL, 's' }, + { NULL, 0, NULL, '\0' } + }; + + int option_index = 0; + int opt = 0; + while ((opt = getopt_long(argc, argv, "k:p:s:u", long_options, &option_index)) != -1) { + switch (opt) { + case 'k': { + g_key_id.clear(); + g_key_id.assign(optarg); + break; + } + case 'p': { + g_port.clear(); + g_port.assign(optarg); + break; + } + case 's': { + g_license_server.clear(); + g_license_server.assign(optarg); + break; + } + case 'u': { + g_use_full_path = 1; + break; + } + case '?': { + show_usage = 1; + break; + } + } + } + + if (show_usage) { + std::cout << std::endl; + std::cout << "usage: " << argv[0] << " [options]" << std::endl << std::endl; + std::cout << " enclose multiple arguments in '' when using adb shell" << std::endl; + std::cout << " e.g. adb shell '" << argv[0] << " --server=\"url\"'" << std::endl << std::endl; + + std::cout << std::setw(30) << std::left << " --port="; + std::cout << "specifies the port number, in decimal format" << std::endl; + std::cout << std::setw(30) << std::left << " "; + std::cout << "default: " << g_port << std::endl; + + std::cout << std::setw(30) << std::left << " --server="; + std::cout << "configure the license server url, please include http[s] in the url" << std::endl; + std::cout << std::setw(30) << std::left << " "; + std::cout << "default: " << license_server << std::endl; + + std::cout << std::setw(30) << std::left << " --keyid="; + std::cout << "configure the key id or pssh, in hex format" << std::endl; + std::cout << std::setw(30) << std::left << " default keyid:"; + std::cout << g_key_id << std::endl; + + std::cout << std::setw(30) << std::left << " --use_full_path"; + std::cout << "specify server url is not a proxy server" << std::endl; + std::cout << std::endl; + return 0; + } + + std::cout << std::endl; + std::cout << "Server: " << g_license_server << std::endl; + std::cout << "Port: " << g_port << std::endl; + std::cout << "KeyID: " << g_key_id << std::endl << std::endl; + + g_key_id = wvcdm::a2bs_hex(g_key_id); + config.set_license_server(g_license_server); + config.set_port(g_port); + config.set_key_id(g_key_id); + + return RUN_ALL_TESTS(); +} diff --git a/libwvdrmengine/cdm/unit-test.mk b/libwvdrmengine/cdm/unit-test.mk new file mode 100644 index 00000000..b539e286 --- /dev/null +++ b/libwvdrmengine/cdm/unit-test.mk @@ -0,0 +1,51 @@ +# Makes a unit test. test_name must be +# passed in as the base filename (without the .cpp). + +$(call assert-not-null,test_name) + +LOCAL_MODULE := $(test_name) +LOCAL_MODULE_TAGS := tests +LOCAL_MODULE_CLASS := EXECUTABLES + +PROTO_SRC_DIR := $(proto_generated_cc_sources_dir)/$(LOCAL_PATH)/core/src +LOCAL_SRC_FILES := \ + $(test_name).cpp \ + core/test/config_test_env.cpp \ + core/test/http_socket.cpp \ + core/test/license_request.cpp \ + core/test/url_request.cpp \ + $(PROTO_SRC_DIR)/license_protocol.pb.cc + +LOCAL_C_INCLUDES += \ + bionic \ + external/gtest/include \ + external/stlport/stlport \ + vendor/widevine/libwvdrmengine/cdm/core/include \ + vendor/widevine/libwvdrmengine/cdm/core/test \ + vendor/widevine/libwvdrmengine/cdm/include + +# Add protocol buffer generated headers +# +LOCAL_C_INCLUDES += \ + $(proto_generated_cc_sources_dir)/$(LOCAL_PATH)/core/src \ + external/protobuf/src + +LOCAL_ADDITIONAL_DEPENDENCIES += $(proto_generated_headers) + +LOCAL_STATIC_LIBRARIES := \ + libcdm \ + libgtest \ + libgtest_main \ + libprotobuf-cpp-2.3.0-lite + +LOCAL_WHOLE_STATIC_LIBRARIES := \ + license_protocol_protos + +LOCAL_SHARED_LIBRARIES := \ + libstlport \ + libchromium_net \ + libcrypto \ + liboemcrypto \ + libutils + +include $(BUILD_EXECUTABLE) diff --git a/libwvdrmengine/crypto/TODO b/libwvdrmengine/crypto/TODO deleted file mode 100644 index 26a7769a..00000000 --- a/libwvdrmengine/crypto/TODO +++ /dev/null @@ -1,2 +0,0 @@ -Jeff and Juce. -Maybe we should add a README that says this is the implementation if ICrypto. diff --git a/libwvdrmengine/crypto/include/TODO b/libwvdrmengine/crypto/include/TODO deleted file mode 100644 index cf2b2899..00000000 --- a/libwvdrmengine/crypto/include/TODO +++ /dev/null @@ -1 +0,0 @@ -Jeff and Juce. diff --git a/libwvdrmengine/crypto/src/TODO b/libwvdrmengine/crypto/src/TODO deleted file mode 100644 index cf2b2899..00000000 --- a/libwvdrmengine/crypto/src/TODO +++ /dev/null @@ -1 +0,0 @@ -Jeff and Juce. diff --git a/libwvdrmengine/crypto/test/TODO b/libwvdrmengine/crypto/test/TODO deleted file mode 100644 index cf2b2899..00000000 --- a/libwvdrmengine/crypto/test/TODO +++ /dev/null @@ -1 +0,0 @@ -Jeff and Juce. diff --git a/libwvdrmengine/drmclient/TODO b/libwvdrmengine/drmclient/TODO deleted file mode 100644 index 1e59b009..00000000 --- a/libwvdrmengine/drmclient/TODO +++ /dev/null @@ -1 +0,0 @@ -Jeff and Jerry. diff --git a/libwvdrmengine/drmclient/include/TODO b/libwvdrmengine/drmclient/include/TODO deleted file mode 100644 index 1e59b009..00000000 --- a/libwvdrmengine/drmclient/include/TODO +++ /dev/null @@ -1 +0,0 @@ -Jeff and Jerry. diff --git a/libwvdrmengine/drmclient/src/TODO b/libwvdrmengine/drmclient/src/TODO deleted file mode 100644 index 1e59b009..00000000 --- a/libwvdrmengine/drmclient/src/TODO +++ /dev/null @@ -1 +0,0 @@ -Jeff and Jerry. diff --git a/libwvdrmengine/drmclient/test/TODO b/libwvdrmengine/drmclient/test/TODO deleted file mode 100644 index 1e59b009..00000000 --- a/libwvdrmengine/drmclient/test/TODO +++ /dev/null @@ -1 +0,0 @@ -Jeff and Jerry. diff --git a/libwvdrmengine/include/TODO b/libwvdrmengine/include/TODO deleted file mode 100644 index 93253ffb..00000000 --- a/libwvdrmengine/include/TODO +++ /dev/null @@ -1 +0,0 @@ -Juce and Jerry to fill in. diff --git a/libwvdrmengine/include/WVCDMSingleton.h b/libwvdrmengine/include/WVCDMSingleton.h new file mode 100644 index 00000000..e82bfc59 --- /dev/null +++ b/libwvdrmengine/include/WVCDMSingleton.h @@ -0,0 +1,16 @@ +// +// Copyright 2013 Google Inc. All Rights Reserved. +// + +#ifndef WV_CDM_SINGLETON_H_ +#define WV_CDM_SINGLETON_H_ + +#include "wv_content_decryption_module.h" + +namespace wvdrm { + +wvcdm::WvContentDecryptionModule* getCDM(); + +} // namespace wvdrm + +#endif // WV_CDM_SINGLETON_H_ diff --git a/libwvdrmengine/include/WVCreatePluginFactories.h b/libwvdrmengine/include/WVCreatePluginFactories.h new file mode 100644 index 00000000..2d363366 --- /dev/null +++ b/libwvdrmengine/include/WVCreatePluginFactories.h @@ -0,0 +1,16 @@ +// +// Copyright 2013 Google Inc. All Rights Reserved. +// + +#ifndef WV_CREATE_PLUGIN_FACTORIES_H_ +#define WV_CREATE_PLUGIN_FACTORIES_H_ + +#include "media/drm/DrmAPI.h" +#include "media/hardware/CryptoAPI.h" + +extern "C" { + android::DrmFactory* createDrmFactory(); + android::CryptoFactory* createCryptoFactory(); +} + +#endif // WV_CREATE_PLUGIN_FACTORIES_H_ diff --git a/libwvdrmengine/include/WVCryptoFactory.h b/libwvdrmengine/include/WVCryptoFactory.h new file mode 100644 index 00000000..aaba5516 --- /dev/null +++ b/libwvdrmengine/include/WVCryptoFactory.h @@ -0,0 +1,34 @@ +// +// Copyright 2013 Google Inc. All Rights Reserved. +// + +#ifndef WV_CRYPTO_FACTORY_H_ +#define WV_CRYPTO_FACTORY_H_ + +#include "media/hardware/CryptoAPI.h" +#include "media/stagefright/foundation/ABase.h" +#include "utils/Errors.h" + +namespace wvdrm { + +class WVCryptoFactory : public android::CryptoFactory { + public: + WVCryptoFactory(); + virtual ~WVCryptoFactory(); + + virtual bool isCryptoSchemeSupported(const uint8_t uuid[16]) const; + + virtual android::status_t createPlugin(const uint8_t uuid[16], + const void* data, size_t size, + android::CryptoPlugin** plugin); + + private: + DISALLOW_EVIL_CONSTRUCTORS(WVCryptoFactory); + + void* mLegacyLibraryHandle; + android::CryptoFactory* mLegacyFactory; +}; + +} // namespace wvdrm + +#endif // WV_CRYPTO_FACTORY_H_ diff --git a/libwvdrmengine/include/WVDrmFactory.h b/libwvdrmengine/include/WVDrmFactory.h new file mode 100644 index 00000000..b2106ffd --- /dev/null +++ b/libwvdrmengine/include/WVDrmFactory.h @@ -0,0 +1,30 @@ +// +// Copyright 2013 Google Inc. All Rights Reserved. +// + +#ifndef WV_DRM_FACTORY_H_ +#define WV_DRM_FACTORY_H_ + +#include "media/drm/DrmAPI.h" +#include "media/stagefright/foundation/ABase.h" +#include "utils/Errors.h" + +namespace wvdrm { + +class WVDrmFactory : public android::DrmFactory { + public: + WVDrmFactory() {}; + virtual ~WVDrmFactory() {}; + + virtual bool isCryptoSchemeSupported(const uint8_t uuid[16]); + + virtual android::status_t createDrmPlugin(const uint8_t uuid[16], + android::DrmPlugin** plugin); + + private: + DISALLOW_EVIL_CONSTRUCTORS(WVDrmFactory); +}; + +} // namespace wvdrm + +#endif // WV_DRM_FACTORY_H_ diff --git a/libwvdrmengine/include/WVUUID.h b/libwvdrmengine/include/WVUUID.h new file mode 100644 index 00000000..320101cf --- /dev/null +++ b/libwvdrmengine/include/WVUUID.h @@ -0,0 +1,16 @@ +// +// Copyright 2013 Google Inc. All Rights Reserved. +// + +#ifndef WV_UUID_H_ +#define WV_UUID_H_ + +#include + +namespace wvdrm { + +bool isWidevineUUID(const uint8_t uuid[16]); + +} // namespace wvdrm + +#endif // WV_UUID_H_ diff --git a/libwvdrmengine/level3/Android.mk b/libwvdrmengine/level3/Android.mk new file mode 100644 index 00000000..b648db5f --- /dev/null +++ b/libwvdrmengine/level3/Android.mk @@ -0,0 +1,37 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + + +LOCAL_SRC_FILES := \ + $(TARGET_ARCH)/l3crypto_engine_mock.cpp \ + $(TARGET_ARCH)/l3crypto_key_mock.cpp \ + $(TARGET_ARCH)/l3crypto_keybox_mock.cpp \ + $(TARGET_ARCH)/l3crypto_mock.cpp \ + $(TARGET_ARCH)/lock.cpp \ + $(TARGET_ARCH)/log.cpp \ + $(TARGET_ARCH)/string_conversions.cpp \ + $(TARGET_ARCH)/wvcrc.cpp \ + +LOCAL_C_INCLUDES += \ + bionic \ + external/openssh \ + external/openssl/include \ + external/stlport/stlport \ + vendor/widevine/libwvdrmengine/oemcrypto/include \ + $(LOCAL_PATH)/include \ + $(LOCAL_PATH)/$(TARGET_ARCH) + +LOCAL_MODULE := libl3crypto + +LOCAL_SHARED_LIBRARIES := \ + libcrypto \ + libcutils \ + libdl \ + liblog \ + liboemcrypto \ + libstlport \ + libutils \ + libz \ + +include $(BUILD_STATIC_LIBRARY) diff --git a/libwvdrmengine/level3/arm/l3crypto_engine_mock.cpp b/libwvdrmengine/level3/arm/l3crypto_engine_mock.cpp new file mode 100644 index 00000000..0a4256ce --- /dev/null +++ b/libwvdrmengine/level3/arm/l3crypto_engine_mock.cpp @@ -0,0 +1,524 @@ +/******************************************************************************* + * + * Copyright 2013 Google Inc. All Rights Reserved. + * + * mock implementation of OEMCrypto APIs + * + ******************************************************************************/ + +#include "l3crypto_engine_mock.h" + +#include +#include +#include + +#include "log.h" +#include "l3crypto_key_mock.h" +#include "openssl/aes.h" +#include "openssl/cmac.h" +#include "openssl/hmac.h" +#include "openssl/rand.h" +#include "openssl/sha.h" +#include "wv_cdm_constants.h" + +namespace { +// Increment counter for AES-CTR +void ctr128_inc(uint8_t* counter) { + uint32_t n = 16; + do { + if (++counter[--n] != 0) return; + } while (n); +} +} + +namespace wvoec_obfs { + +SessionKeyTable::~SessionKeyTable() { + for (KeyMap::iterator i = keys_.begin(); i != keys_.end(); ++i) { + if (NULL != i->second) { + delete i->second; + } + } +} + +bool SessionKeyTable::Insert(const KeyId key_id, const Key& key_data) { + if (keys_.find(key_id) != keys_.end()) return false; + keys_[key_id] = new Key(key_data); + return true; +} + +Key* SessionKeyTable::Find(const KeyId key_id) { + if (keys_.find(key_id) == keys_.end()) { + return NULL; + } + return keys_[key_id]; +} + +void SessionKeyTable::Remove(const KeyId key_id) { + if (keys_.find(key_id) != keys_.end()) { + delete keys_[key_id]; + keys_.erase(key_id); + } +} + +void SessionContext::Open() { +} + +void SessionContext::Close() { +} + +// Internal utility function to derive key using CMAC-128 +bool SessionContext::DeriveKey(const std::vector& key, + const std::vector& context, + int counter, + std::vector* out) { + if (key.empty() || counter > 2 || context.empty() || out == NULL) { + LOGE("[DeriveKey(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return false; + } + + const EVP_CIPHER* cipher = EVP_aes_128_cbc(); + CMAC_CTX* cmac_ctx = CMAC_CTX_new(); + + if (!CMAC_Init(cmac_ctx, &key[0], key.size(), cipher, 0)) { + LOGE("[DeriveKey(): OEMCrypto_ERROR_CMAC_FAILURE]"); + return false; + } + + std::vector message; + message.push_back(counter); + message.insert(message.end(), context.begin(), context.end()); + + if (!CMAC_Update(cmac_ctx, &message[0], message.size())) { + LOGE("[DeriveKey(): OEMCrypto_ERROR_CMAC_FAILURE]"); + return false; + } + + size_t reslen; + uint8_t res[128]; + if (!CMAC_Final(cmac_ctx, res, &reslen)) { + LOGE("[DeriveKey(): OEMCrypto_ERROR_CMAC_FAILURE]"); + return false; + } + + out->assign(res, res+reslen); + + CMAC_CTX_free(cmac_ctx); + + return true; +} + +bool SessionContext::DeriveKeys(const std::vector& mac_key_context, + const std::vector& enc_key_context) { + // Generate derived key for mac key + std::vector device_key = ce_->keybox().device_key().value(); + std::vector mac_key; + std::vector result; + if (!DeriveKey(device_key, mac_key_context, 1, &mac_key)) { + return false; + } + if (!DeriveKey(device_key, mac_key_context, 2, &result)) { + return false; + } + mac_key.insert( mac_key.end(), result.begin(), result.end()); + + // Generate derived key for encryption key + std::vector enc_key; + if (!DeriveKey(device_key, enc_key_context, 1, &enc_key)) { + return false; + } + + set_mac_key(mac_key); + set_encryption_key(enc_key); + return true; +} + +// Utility function to generate a message signature +bool SessionContext::GenerateSignature(const uint8_t* message, + size_t message_length, + uint8_t* signature, + size_t* signature_length) { + + if (message == NULL || message_length == 0 || + signature == NULL || signature_length == 0) { + LOGE("[OEMCrypto_GenerateSignature(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return false; + } + + if (mac_key_.empty() || mac_key_.size() != wvcdm::MAC_KEY_SIZE) { + LOGE("[GenerateSignature(): No MAC Key]"); + return false; + } + + if (*signature_length < SHA256_DIGEST_LENGTH) { + *signature_length = SHA256_DIGEST_LENGTH; + return false; + } + + unsigned int md_len = *signature_length; + if (HMAC(EVP_sha256(), &mac_key_[0], SHA256_DIGEST_LENGTH, + message, message_length, signature, &md_len)) { + *signature_length = md_len; + return true; + } + return false; +} + +// Validate message signature +bool SessionContext::ValidateMessage(const uint8_t* given_message, + size_t message_length, + const uint8_t* given_signature, + size_t signature_length) { + + if (signature_length != SHA256_DIGEST_LENGTH) { + return false; + } + uint8_t computed_signature[signature_length]; + if (! GenerateSignature(given_message, message_length, + computed_signature, &signature_length)) { + return false; + } + if (memcmp(given_signature, computed_signature, signature_length)) { + return false; + } + return true; +} + +bool SessionContext::ParseKeyControl( + const std::vector& key_control_string, + KeyControlBlock& key_control_block) { + + key_control_block.Invalidate(); + + if (key_control_string.size() < wvcdm::KEY_CONTROL_SIZE) { + return false; + } + if (!key_control_block.SetFromString(key_control_string)) { + LOGE("KCB: BAD Size or Structure"); + return false; + } + if (!key_control_block.Validate()) { + LOGE("KCB: BAD Signature"); + return false; + } + // TODO(fredgc): This checks each key against a nonce and then throws it out. + // Instead, it should use the same nonce for the whole message. + // if (!CheckNonce(key_control_block.nonce())) { + // LOGE("KCB: BAD Nonce"); + // return false; + // } + + LOGD("KCB:"); + LOGD(" valid: %d", key_control_block.valid()); + LOGD(" duration: %d", key_control_block.duration()); + LOGD(" nonce: %08X", key_control_block.nonce()); + LOGD(" bits: %08X", key_control_block.control_bits()); + + return true; +} + +bool SessionContext::InstallKey(const KeyId& key_id, + const std::vector& key_data, + const std::vector& key_data_iv, + const std::vector& key_control, + const std::vector& key_control_iv) { + + // Decrypt encrypted key_data using derived encryption key and offered iv + std::vector content_key; + std::vector key_control_str; + KeyControlBlock key_control_block; + + if (!ce_->DecryptMessage(this, encryption_key_, key_data_iv, + key_data, &content_key)) { + return false; + } + + // Key control must be supplied by license server + if (key_control.empty()) { + LOGE("[Installkey(): WARNING: No Key Control]"); + key_control_block.Invalidate(); + return false; + } else { + if (key_control_iv.empty()) { + LOGE("[Installkey(): ERROR: No Key Control IV]"); + return false; + } + if (!ce_->DecryptMessage(this, content_key, key_control_iv, + key_control, &key_control_str)) { + return false; + } + + if (!ParseKeyControl(key_control_str, key_control_block)) { + return false; + } + } + + Key key(KEYTYPE_CONTENT, content_key, key_control_block); + session_keys_.Insert(key_id, key); + return true; +} + +bool SessionContext::RefreshKey(const KeyId& key_id, + const std::vector& key_control, + const std::vector& key_control_iv) { + if (key_id.empty()) { + return false; + } + + Key* content_key = session_keys_.Find(key_id); + + if (NULL == content_key) { + return false; + } + + if (!key_control.empty()) { + const std::vector content_key_value = content_key->value(); + + // Decrypt encrypted key control block + // We don't actually make use of it in Oemcrypto mock, just to verify its + // validity + std::vector control; + if (key_control_iv.empty()) { + control = key_control; + } else if (!ce_->DecryptMessage(this, content_key_value, key_control_iv, + key_control, &control)) { + return false; + } + + KeyControlBlock key_control_block; + if (!ParseKeyControl(control, key_control_block)) { + return false; + } + + if (!content_key->UpdateControl(key_control_block)) { + return false; + } + } + + return true; +} + +bool SessionContext::UpdateMacKey(const std::vector& enc_mac_key, + const std::vector& iv) { + + // Decrypt mac key from enc_mac_key using device_key + std::vector mac_key; + if (!ce_->DecryptMessage(this, encryption_key_, iv, + enc_mac_key, &mac_key)) { + return false; + } + mac_key_ = mac_key; + return true; +} + +bool SessionContext::SelectContentKey(const KeyId& key_id) { + const Key* content_key = session_keys_.Find(key_id); + if (NULL == content_key) { + LOGE("[SelectContentKey(): No key matches key id]"); + return false; + } + current_content_key_ = content_key; + return true; +} + +void SessionContext::AddNonce(uint32_t nonce) { + nonce_table_.AddNonce(nonce); +} + +bool SessionContext::CheckNonce(uint32_t nonce) { + return nonce_table_.CheckNonce(nonce); +} + + +CryptoEngine::CryptoEngine() : + ce_state_(CE_INITIALIZED), current_session_(NULL) { + valid_ = true; +} + +CryptoEngine::~CryptoEngine() { + current_session_ = NULL; + sessions_.clear(); +} + +void CryptoEngine::Terminate() { +} + +KeyboxError CryptoEngine::ValidateKeybox() { return keybox_.Validate(); } + +SessionId CryptoEngine::CreateSession() { + wvcdm::AutoLock lock(session_table_lock_); + static int unique_id = 1; + SessionId sid = (SessionId)++unique_id; + SessionContext* sctx = new SessionContext(this, sid); + sessions_[sid] = sctx; + return sid; +} + +bool CryptoEngine::DestroySession(SessionId sid) { + SessionContext* sctx = FindSession(sid); + wvcdm::AutoLock lock(session_table_lock_); + if (sctx) { + sessions_.erase(sid); + delete sctx; + return true; + } else { + return false; + } +} + +SessionContext* CryptoEngine::FindSession(SessionId sid) { + wvcdm::AutoLock lock(session_table_lock_); + ActiveSessions::iterator it = sessions_.find(sid); + if (it != sessions_.end()) { + return it->second; + } + return NULL; +} + +// Internal utility function to decrypt the message +bool CryptoEngine::DecryptMessage(SessionContext* session, + const std::vector& key, + const std::vector& iv, + const std::vector& message, + std::vector* decrypted) { + if (key.empty() || iv.empty() || message.empty() || !decrypted) { + LOGE("[DecryptMessage(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return false; + } + + decrypted->resize(message.size()); + uint8_t iv_buffer[16]; + memcpy(iv_buffer, &iv[0], 16); + AES_KEY aes_key; + AES_set_decrypt_key(&key[0], 128, &aes_key); + AES_cbc_encrypt(&message[0], &(decrypted->front()), message.size(), + &aes_key, iv_buffer, AES_DECRYPT); + return true; +} + +bool CryptoEngine::DecryptCTR(SessionContext* session, + const std::vector& iv, + size_t byte_offset, + const std::vector& cipher_data, + bool is_encrypted, + void* clear_data, + BufferType buffer_type) { + + // Check there is a content key + if (session->current_content_key() == NULL) { + LOGE("[DecryptCTR(): OEMCrypto_ERROR_NO_CONTENT_KEY]"); + return false; + } + const KeyControlBlock& control = session->current_content_key()->control(); + if (control.control_bits() & kControlDataPathSecure) { + if (buffer_type == BUFFER_TYPE_CLEAR) { + LOGE("[DecryptCTR(): Secure key with insecure buffer]"); + return false; + } + } + // TODO(fredgc): Check duration of key. + + const std::vector& content_key = session->current_content_key()->value(); + + // Set the AES key. + if (static_cast(content_key.size()) != AES_BLOCK_SIZE) { + LOGE("[DecryptCTR(): CONTENT_KEY has wrong size."); + return false; + } + const uint8_t* key_u8 = &content_key[0]; + AES_KEY aes_key; + if (AES_set_encrypt_key(key_u8, AES_BLOCK_SIZE * 8, &aes_key) != 0) { + LOGE("[DecryptCTR(): FAILURE]"); + return false; + } + + if (buffer_type == BUFFER_TYPE_DIRECT) { + // For reference implementation, we quietly drop direct video. + return true; + } + + if (buffer_type == BUFFER_TYPE_SECURE) { + // For reference implementation, we also quietly drop secure data. + return true; + } + + if (! is_encrypted) { + memcpy(reinterpret_cast(clear_data), + &cipher_data[0], cipher_data.size()); + return true; + } + + // Local copy (will be modified). + uint8_t aes_iv[AES_BLOCK_SIZE]; + if (static_cast(iv.size()) != AES_BLOCK_SIZE) { + LOGE("[DecryptCTR(): FAILURE: iv has wrong length]"); + return false; + } + memcpy(aes_iv, &iv[0], AES_BLOCK_SIZE); + + // Encrypt the IV. + uint8_t ecount_buf[AES_BLOCK_SIZE]; + if (byte_offset != 0) { + // The context is needed only when not starting a new block. + AES_encrypt(aes_iv, ecount_buf, &aes_key); + ctr128_inc(aes_iv); + } + + // Decryption. + unsigned int byte_offset_cur = byte_offset; + AES_ctr128_encrypt( + &cipher_data[0], reinterpret_cast(clear_data), cipher_data.size(), + &aes_key, aes_iv, ecount_buf, &byte_offset_cur); + if (byte_offset_cur != ((byte_offset + cipher_data.size()) % AES_BLOCK_SIZE)) { + LOGE("[DecryptCTR(): FAILURE: byte offset wrong.]"); + return false; + } + return true; +} + +void NonceTable::AddNonce(uint32_t nonce) { + int new_slot = -1; + int oldest_slot = -1; + + for (int i = 0; i < kTableSize; ++i) { + if (valid_[i]) { + ++age_[i]; + if (-1 == oldest_slot) { + oldest_slot = i; + } else { + if (age_[i] > age_[oldest_slot]) { + oldest_slot = i; + } + } + } else { + if (-1 == new_slot) { + age_[i] = 0; + nonces_[i] = nonce; + valid_[i] = true; + new_slot = i; + } + } + } + if (-1 == new_slot) { + // reuse oldest + // assert (oldest_slot != -1) + int i = oldest_slot; + age_[i] = 0; + nonces_[i] = nonce; + valid_[i] = true; + } +} + +bool NonceTable::CheckNonce(uint32_t nonce) { + for (int i = 0; i < kTableSize; ++i) { + if (valid_[i]) { + if (nonce == nonces_[i]) { + valid_[i] = false; + return true; + } + } + } + return false; +} + +}; // namespace wvoec_obfs diff --git a/libwvdrmengine/level3/arm/l3crypto_engine_mock.h b/libwvdrmengine/level3/arm/l3crypto_engine_mock.h new file mode 100644 index 00000000..86ffce87 --- /dev/null +++ b/libwvdrmengine/level3/arm/l3crypto_engine_mock.h @@ -0,0 +1,208 @@ +/******************************************************************************* + * + * Copyright 2013 Google Inc. All Rights Reserved. + * + * mock implementation of OEMCrypto APIs + * + ******************************************************************************/ + +#ifndef L3CRYPTO_ENGINE_MOCK_H_ +#define L3CRYPTO_ENGINE_MOCK_H_ + +#include +#include +#include +#include "lock.h" +#include "l3crypto_key_mock.h" +#include "l3crypto_keybox_mock.h" +#include "wv_cdm_types.h" + +namespace wvoec_obfs { + +enum SessionState { + SESSION_STATE_ILLEGAL, + SESSION_STATE_INITIALIZED, + SESSION_STATE_HAS_DERIVED_KEYS, + SESSION_STATE_ACTIVE, + SESSION_STATE_ERROR +}; + +enum BufferType { + BUFFER_TYPE_CLEAR, + BUFFER_TYPE_SECURE, + BUFFER_TYPE_DIRECT +}; + +class SessionContext; +class CryptoEngine; + +typedef uint32_t SessionId; +typedef std::map ActiveSessions; + +typedef std::vector KeyId; +typedef std::map KeyMap; + +// SessionKeyTable holds the keys for the current session +class SessionKeyTable { + public: + SessionKeyTable() {} + ~SessionKeyTable(); + + bool Insert(const KeyId key_id, const Key& key_data); + Key* Find(const KeyId key_id); + void Remove(const KeyId key_id); + + private: + KeyMap keys_; + + CORE_DISALLOW_COPY_AND_ASSIGN(SessionKeyTable); +}; + +class NonceTable { + public: + static const int kTableSize = 16; + NonceTable() { + for (int i = 0; i < kTableSize; ++i) { + valid_[i] = false; + } + } + ~NonceTable() {}; + void AddNonce(uint32_t nonce); + bool CheckNonce(uint32_t nonce); + private: + bool valid_[kTableSize]; + uint32_t age_[kTableSize]; + uint32_t nonces_[kTableSize]; +}; + +class SessionContext { + private: + SessionContext() {} + + public: + explicit SessionContext(CryptoEngine* ce, SessionId sid) + : valid_(true), ce_(ce), id_(sid), current_content_key_(NULL) {} + ~SessionContext() {} + + void Open(); + void Close(); + + bool isValid() { return valid_; } + + bool DeriveKeys(const std::vector& mac_context, + const std::vector& enc_context); + bool GenerateSignature(const uint8_t* message, + size_t message_length, + uint8_t* signature, + size_t* signature_length); + + bool ValidateMessage(const uint8_t* message, + size_t message_length, + const uint8_t* signature, + size_t signature_length); + bool InstallKey(const KeyId& key_id, + const std::vector& key_data, + const std::vector& key_data_iv, + const std::vector& key_control, + const std::vector& key_control_iv); + bool ParseKeyControl(const std::vector& key_control_string, + KeyControlBlock& key_control_block); + bool RefreshKey(const KeyId& key_id, + const std::vector& key_control, + const std::vector& key_control_iv); + bool UpdateMacKey(const std::vector& mac_key, const std::vector& iv); + bool SelectContentKey(const KeyId& key_id); + const Key* current_content_key(void) {return current_content_key_;} + void set_mac_key(const std::vector& mac_key) { mac_key_ = mac_key; } + const std::vector& mac_key() { return mac_key_; } + + void set_encryption_key(const std::vector& enc_key) { + encryption_key_ = enc_key; + } + const std::vector& encryption_key() { return encryption_key_; } + + void AddNonce(uint32_t nonce); + bool CheckNonce(uint32_t nonce); + + private: + + bool DeriveKey(const std::vector& key, const std::vector& context, + int counter, std::vector* out); + + bool valid_; + CryptoEngine* ce_; + SessionId id_; + SessionState state_; + std::vector mac_key_; + std::vector encryption_key_; + const Key* current_content_key_; + SessionKeyTable session_keys_; + NonceTable nonce_table_; + + CORE_DISALLOW_COPY_AND_ASSIGN(SessionContext); +}; + +class CryptoEngine { + + private: + + enum CryptoEngineState { + CE_ILLEGAL, + CE_INITIALIZED, + CE_HAS_KEYBOX, + CE_HAS_SESSIONS, + CE_ERROR + }; + + public: + + CryptoEngine(); + ~CryptoEngine(); + + bool Initialized() { return (ce_state_ != CE_ILLEGAL); } + + void Terminate(); + + bool isValid() { return valid_; } + + KeyboxError ValidateKeybox(); + WvKeybox& keybox() { return keybox_; } + + SessionId CreateSession(); + + bool DestroySession(SessionId sid); + + SessionContext* FindSession(SessionId sid); + + void set_current_session_(SessionContext* current) { + current_session_ = current; + } + + bool DecryptMessage(SessionContext* session, + const std::vector& key, + const std::vector& iv, + const std::vector& message, + std::vector* decrypted); + + bool DecryptCTR(SessionContext* session, + const std::vector& iv, + size_t byte_offset, + const std::vector& cipher_data, + bool is_encrypted, + void* clear_data, + BufferType buffer_type); + + private: + + bool valid_; + CryptoEngineState ce_state_; + SessionContext* current_session_; + ActiveSessions sessions_; + WvKeybox keybox_; + wvcdm::Lock session_table_lock_; + + CORE_DISALLOW_COPY_AND_ASSIGN(CryptoEngine); +}; + +}; // namespace wvoec_eng +#endif diff --git a/libwvdrmengine/level3/arm/l3crypto_key_mock.cpp b/libwvdrmengine/level3/arm/l3crypto_key_mock.cpp new file mode 100644 index 00000000..5228873d --- /dev/null +++ b/libwvdrmengine/level3/arm/l3crypto_key_mock.cpp @@ -0,0 +1,97 @@ +/******************************************************************************* + * + * Copyright 2013 Google Inc. All Rights Reserved. + * + * mock implementation of OEMCrypto APIs + * + ******************************************************************************/ + +#include +#include "l3crypto_key_mock.h" +#include "wv_cdm_constants.h" + +namespace wvoec_obfs { + +bool KeyControlBlock::Validate() { + + valid_ = false; + if (0x6b63746c != verification_) { + return false; + } + // TODO(gmorgan): validate control bits + valid_ = true; + return valid_; +} + +uint32_t KeyControlBlock::ExtractField(const std::vector& str, int idx) { + int bidx = idx * 4; + uint32_t t = static_cast(str[bidx]) << 24; + t |= static_cast(str[bidx+1]) << 16; + t |= static_cast(str[bidx+2]) << 8; + t |= static_cast(str[bidx+3]); + return t; +} + +bool KeyControlBlock::SetFromString(const std::vector& key_control_string) { + if (key_control_string.size() < wvcdm::KEY_CONTROL_SIZE) { + return false; + } + + verification_ = ExtractField(key_control_string, 0); + duration_ = ExtractField(key_control_string, 1); + nonce_ = ExtractField(key_control_string, 2); + control_bits_ = ExtractField(key_control_string, 0); + + return Validate(); +} + +Key::Key(KeyType ktype, const std::vector& key_string, + const KeyControlBlock& control) : + valid_(true), type_(ktype), + value_(key_string), has_control_(true), + control_(control) { +} + +bool Key::setValue(const char* key_string, size_t key_string_length) { + valid_ = false; + if (!key_string || key_string_length == 0) { + return false; + } + + value_.assign(key_string, key_string + key_string_length); + + if (isValidType() && has_control_) { + valid_ = true; + } + return valid_; +} + +bool Key::setType(KeyType ktype) { + valid_ = false; + type_ = ktype; + if (value_.empty()) { + return false; + } + + if (isValidType() && has_control_) { + valid_ = true; + } + return valid_; +} + +bool Key::setControl(const KeyControlBlock& control) { + valid_ = false; + if (!control.valid()) { + return false; + } + + control_ = control; + has_control_ = true; + + if (isValidType() && !value_.empty()) { + valid_ = true; + } + return valid_; +} + +}; // namespace wvoec_eng diff --git a/libwvdrmengine/level3/arm/l3crypto_key_mock.h b/libwvdrmengine/level3/arm/l3crypto_key_mock.h new file mode 100644 index 00000000..3b4e7032 --- /dev/null +++ b/libwvdrmengine/level3/arm/l3crypto_key_mock.h @@ -0,0 +1,117 @@ +/******************************************************************************* + * + * Copyright 2013 Google Inc. All Rights Reserved. + * + * mock implementation of OEMCrypto APIs + * + ******************************************************************************/ + +#ifndef L3CRYPTO_KEY_MOCK_H_ +#define L3CRYPTO_KEY_MOCK_H_ + +#include +#include +#include + +namespace wvoec_obfs { + +enum KeyType { + KEYTYPE_UNKNOWN, + KEYTYPE_PREPROV, + KEYTYPE_ROOT, + KEYTYPE_DEVICE, + KEYTYPE_CONTENT, + KEYTYPE_CONTENT_AUDIO, + KEYTYPE_CONTENT_VIDEO, + KEYTYPE_MAX +}; + +const uint32_t kControlObserveDataPath = (1<<31); +const uint32_t kControlObserveHDCP = (1<<30); +const uint32_t kControlObserveCGMS = (1<<29); +const uint32_t kControlDataPathSecure = (1<<4); +const uint32_t kControlNonceEnabled = (1<<3); +const uint32_t kControlHDCPRequired = (1<<2); +const uint32_t kControlCGMSMask = (0x03); +const uint32_t kControlCGMSCopyFreely = (0x00); +const uint32_t kControlCGMSCopyOnce = (0x02); +const uint32_t kControlCGMSCopyNever = (0x03); + +class KeyControlBlock { + public: + KeyControlBlock() {} + KeyControlBlock(const std::vector& key_control_string) { + valid_ = SetFromString(key_control_string); + } + ~KeyControlBlock() {} + + bool SetFromString(const std::vector& key_control_string); + bool Validate(); + void Invalidate() { valid_ = false; } + + bool valid() const { return valid_; } + uint32_t duration() const { return duration_; } + uint32_t nonce() const { return nonce_; } + uint32_t control_bits() const { return control_bits_; } + + private: + + uint32_t ExtractField(const std::vector& str, int idx); + + bool valid_; + uint32_t verification_; + uint32_t duration_; + uint32_t nonce_; + uint32_t control_bits_; +}; + +// AES-128 crypto key +class Key { + public: + Key() : valid_(false), type_(KEYTYPE_UNKNOWN), has_control_(false) {} + Key(const Key& key) : valid_(key.valid_), type_(key.type_), + value_(key.value_), + has_control_(key.has_control_), + control_(key.control_) {} + Key(KeyType type, const std::vector& key_string, + const KeyControlBlock& control); + + virtual ~Key() {}; + + // Key is valid iff setValue(), setType(), and setControl() have been called + bool setValue(const char* key_string, size_t key_string_length); + bool setType(KeyType ktype); + bool setControl(const KeyControlBlock& control); + bool UpdateControl(const KeyControlBlock& control) { return true; } + + KeyType keyType() { return type_; } + const std::vector& value() const { return value_; } + const KeyControlBlock& control() const { return control_; } + + bool isDeviceKey() { return (KEYTYPE_DEVICE == type_); } + bool isRootKey() { return (KEYTYPE_ROOT == type_); } + bool isPreprovKey() { return (KEYTYPE_PREPROV == type_); } + bool isContentKey() { + bool ctypes = (KEYTYPE_CONTENT == type_) || + (KEYTYPE_CONTENT_AUDIO == type_) || + (KEYTYPE_CONTENT_VIDEO == type_); + return ctypes; + } + bool isValidType() { + return ((KEYTYPE_UNKNOWN < type_) && (KEYTYPE_MAX > type_)); + } + bool isValid() { return valid_; } + + void clear() { value_.clear(); valid_ = false; } + + private: + bool valid_; + KeyType type_; + std::vector value_; + bool has_control_; + KeyControlBlock control_; +}; + +}; // namespace wvoec_eng + +#endif diff --git a/libwvdrmengine/level3/arm/l3crypto_keybox_mock.cpp b/libwvdrmengine/level3/arm/l3crypto_keybox_mock.cpp new file mode 100644 index 00000000..cde685fe --- /dev/null +++ b/libwvdrmengine/level3/arm/l3crypto_keybox_mock.cpp @@ -0,0 +1,109 @@ +/******************************************************************************* + * + * Copyright 2013 Google Inc. All Rights Reserved. + * + * mock implementation of OEMCrypto APIs + * + ******************************************************************************/ + +#include "l3crypto_keybox_mock.h" +#include // TODO(fredgc): Add ntoh to wv_cdm_utilities.h +#include +#include +#include +#include "log.h" +#include "wvcrc32.h" +#include "wv_keybox.h" + +namespace wvoec_obfs { + +const WidevineKeybox kDefaultKeybox = { + // Sample keybox used for test vectors + { + // deviceID + 0x54, 0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x30, // TestKey01 + 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + }, { + // key + 0xfb, 0xda, 0x04, 0x89, 0xa1, 0x58, 0x16, 0x0e, + 0xa4, 0x02, 0xe9, 0x29, 0xe3, 0xb6, 0x8f, 0x04, + }, { + // data + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x10, 0x19, + 0x07, 0xd9, 0xff, 0xde, 0x13, 0xaa, 0x95, 0xc1, + 0x22, 0x67, 0x80, 0x53, 0x36, 0x21, 0x36, 0xbd, + 0xf8, 0x40, 0x8f, 0x82, 0x76, 0xe4, 0xc2, 0xd8, + 0x7e, 0xc5, 0x2b, 0x61, 0xaa, 0x1b, 0x9f, 0x64, + 0x6e, 0x58, 0x73, 0x49, 0x30, 0xac, 0xeb, 0xe8, + 0x99, 0xb3, 0xe4, 0x64, 0x18, 0x9a, 0x14, 0xa8, + 0x72, 0x02, 0xfb, 0x02, 0x57, 0x4e, 0x70, 0x64, + 0x0b, 0xd2, 0x2e, 0xf4, 0x4b, 0x2d, 0x7e, 0x39, + }, { + // magic + 0x6b, 0x62, 0x6f, 0x78, + }, { + // Crc + 0x0a, 0x7a, 0x2c, 0x35, + } +}; + +WvKeybox::WvKeybox() : valid_(false) { + Prepare(); +} + +bool WvKeybox::Prepare() { + InstallKeybox(reinterpret_cast(&kDefaultKeybox), + sizeof(kDefaultKeybox)); + valid_ = true; + return valid_; +} + +KeyboxError WvKeybox::Validate() { + if (!valid_) { + LOGE("[KEYBOX NOT LOADED]"); + return OTHER_ERROR; + } + if (strncmp(reinterpret_cast(magic_), "kbox", 4) != 0) { + LOGE("[KEYBOX HAS BAD MAGIC]"); + return BAD_MAGIC; + } + uint32_t crc_computed; + uint32_t* crc_stored = (uint32_t*)crc_; + WidevineKeybox keybox; + memset(&keybox, 0, sizeof(keybox)); + memcpy(keybox.device_id_, &device_id_[0], device_id_.size()); + memcpy(keybox.device_key_, &device_key_.value()[0], sizeof(keybox.device_key_)); + memcpy(keybox.data_, key_data_, sizeof(keybox.data_)); + memcpy(keybox.magic_, magic_, sizeof(keybox.magic_)); + + crc_computed = ntohl(wvcrc32(reinterpret_cast(&keybox), + sizeof(keybox) - 4)); // Don't include last 4 bytes. + if (crc_computed != *crc_stored) { + LOGE("[KEYBOX CRC problem: computed = %08x, stored = %08x]\n", + crc_computed, *crc_stored); + return BAD_CRC; + } + return NO_ERROR; +} + +bool WvKeybox::InstallKeybox(const uint8_t* buffer, size_t keyBoxLength) { + if (keyBoxLength != 128) { + return false; + } + + const WidevineKeybox* keybox + = reinterpret_cast(buffer); + device_id_.assign(keybox->device_id_, + keybox->device_id_ + sizeof(keybox->device_id_)); + device_key_.setValue(reinterpret_cast(keybox->device_key_), + sizeof(keybox->device_key_)); + device_key_.setType(KEYTYPE_DEVICE); + memcpy(key_data_, keybox->data_, sizeof(keybox->data_)); + memcpy(magic_, keybox->magic_, sizeof(keybox->magic_)); + memcpy(crc_, keybox->crc_, sizeof(keybox->crc_)); + return true; +} + +}; // namespace wvoec_eng diff --git a/libwvdrmengine/level3/arm/l3crypto_keybox_mock.h b/libwvdrmengine/level3/arm/l3crypto_keybox_mock.h new file mode 100644 index 00000000..14442788 --- /dev/null +++ b/libwvdrmengine/level3/arm/l3crypto_keybox_mock.h @@ -0,0 +1,50 @@ +/******************************************************************************* + * + * Copyright 2013 Google Inc. All Rights Reserved. + * + * mock implementation of OEMCrypto APIs + * + ******************************************************************************/ + +#ifndef L3CRYPTO_KEYBOX_MOCK_H_ +#define L3CRYPTO_KEYBOX_MOCK_H_ + +#include "l3crypto_key_mock.h" + +namespace wvoec_obfs { + +const int DEVICE_KEY_LENGTH = 16; +typedef uint8_t WvKeyboxKey[DEVICE_KEY_LENGTH]; + +const int KEY_DATA_LENGTH = 72; +typedef uint8_t WvKeyboxKeyData[KEY_DATA_LENGTH]; + +enum KeyboxError { NO_ERROR, BAD_CRC, BAD_MAGIC, OTHER_ERROR }; + +// Widevine keybox +class WvKeybox { + public: + WvKeybox(); + ~WvKeybox() {} + + KeyboxError Validate(); + const std::vector& device_id() { return device_id_; } + Key& device_key() { return device_key_; } + const WvKeyboxKeyData& key_data() { return key_data_; } + size_t key_data_length() { return KEY_DATA_LENGTH; } + bool InstallKeybox(const uint8_t* keybox, size_t keyBoxLength); + + private: + + bool Prepare(); + + bool valid_; + std::vector device_id_; + Key device_key_; + WvKeyboxKeyData key_data_; + uint8_t magic_[4]; + uint8_t crc_[4]; +}; + +}; // namespace wvoec_eng +#endif diff --git a/libwvdrmengine/level3/arm/l3crypto_mock.cpp b/libwvdrmengine/level3/arm/l3crypto_mock.cpp new file mode 100644 index 00000000..898b885b --- /dev/null +++ b/libwvdrmengine/level3/arm/l3crypto_mock.cpp @@ -0,0 +1,633 @@ +/******************************************************************************* + * + * Copyright 2013 Google Inc. All Rights Reserved. + * + * mock implementation of OEMCrypto APIs + * + ******************************************************************************/ + +#include "L3CryptoCENC.h" + +#include +#include +#include +#include +#include "log.h" +#include "l3crypto_engine_mock.h" +#include "openssl/rand.h" +#include "wv_cdm_constants.h" + +namespace wvoec_obfs { + +static CryptoEngine* crypto_engine = NULL; + +// Set this to true when you are generating test vectors. +const bool trace_all_calls = false; + +static void dump_hex(std::string name, const uint8_t* vector, size_t length) { + printf("%s = ", name.c_str()); + if (vector == NULL) { + printf("NULL;\n"); + return; + } + // TODO(fredgc): replace with HEXEncode. + for (size_t i = 0; i < length; i++) { + if (i == 0) { + printf("\n wvcdm::a2b_hex(\""); + } else if (i % 32 == 0) { + printf("\"\n \""); + } + printf("%02X", vector[i]); + } + printf("\");\n"); +} + +void dump_array_part(std::string array, size_t index, + std::string name, const uint8_t* vector, size_t length) { + if (vector == NULL) { + printf("%s[%d].%s = NULL;\n", array.c_str(), index, name.c_str()); + return; + } + printf("std::string s%d_", index); + dump_hex(name, vector, length); + printf("%s[%d].%s = message_ptr + message.find(s%d_%s.data());\n", + array.c_str(), index, name.c_str(), index, name.c_str()); +} + +extern "C" +OEMCryptoResult L3Crypto_Initialize(void) { + if (trace_all_calls) { + printf("------------------------- L3Crypto_Initialize(void)\n"); + } + + crypto_engine = new CryptoEngine; + + if (!crypto_engine || !crypto_engine->Initialized()) { + LOGE("[L3Crypto_Initialize(): failed]"); + return OEMCrypto_ERROR_INIT_FAILED; + } + LOGD("[L3Crypto_Initialize(): success]"); + return OEMCrypto_SUCCESS; +} + +extern "C" +OEMCryptoResult L3Crypto_Terminate(void) { + if (trace_all_calls) { + printf("----------------- OEMCryptoResult L3Crypto_Terminate(void)\n"); + } + + if (!crypto_engine) { + LOGE("[L3Crypto_Terminate(): failed]"); + return OEMCrypto_ERROR_TERMINATE_FAILED; + } + + if (crypto_engine->Initialized()) { + crypto_engine->Terminate(); + } + + delete crypto_engine; + crypto_engine = NULL; + LOGD("[L3Crypto_Terminate(): success]"); + return OEMCrypto_SUCCESS; +} + +extern "C" +OEMCryptoResult L3Crypto_OpenSession(OEMCrypto_SESSION* session) { + if (trace_all_calls) { + printf("-- OEMCryptoResult L3Crypto_OpenSession(OEMCrypto_SESSION *session)\n"); + } + SessionId sid = crypto_engine->CreateSession(); + *session = (OEMCrypto_SESSION)sid; + LOGD("[L3Crypto_OpenSession(): SID=%08x]", sid); + return OEMCrypto_SUCCESS; +} + +extern "C" +OEMCryptoResult L3Crypto_CloseSession(OEMCrypto_SESSION session) { + if (trace_all_calls) { + printf("-- OEMCryptoResult L3Crypto_CloseSession(OEMCrypto_SESSION session)\n"); + } + if (!crypto_engine->DestroySession((SessionId)session)) { + LOGD("[L3Crypto_CloseSession(SID=%08X): failed]", session); + return OEMCrypto_ERROR_CLOSE_SESSION_FAILED; + } else { + LOGD("[L3Crypto_CloseSession(SID=%08X): success]", session); + return OEMCrypto_SUCCESS; + } +} + +extern "C" +OEMCryptoResult L3Crypto_GenerateNonce(OEMCrypto_SESSION session, + uint32_t* nonce) { + if (trace_all_calls) { + printf("-- OEMCryptoResult L3Crypto_GenerateNonce(OEMCrypto_SESSION session,\n"); + } + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[L3Crypto_GenerateNonce(): ERROR_NO_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + + uint32_t nonce_value; + uint8_t* nonce_string = reinterpret_cast(&nonce_value); + + // Generate 4 bytes of random data + if (!RAND_bytes(nonce_string, 4)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + session_ctx->AddNonce(nonce_value); + *nonce = nonce_value; + if (trace_all_calls) { + printf("nonce = %08x\n", nonce_value); + } + return OEMCrypto_SUCCESS; +} + +extern "C" +OEMCryptoResult L3Crypto_GenerateDerivedKeys( + OEMCrypto_SESSION session, + const uint8_t* mac_key_context, + uint32_t mac_key_context_length, + const uint8_t* enc_key_context, + uint32_t enc_key_context_length) { + if (trace_all_calls) { + printf("-- OEMCryptoResult L3Crypto_GenerateDerivedKeys(\n"); + dump_hex("mac_key_context", mac_key_context, (size_t)mac_key_context_length); + dump_hex("enc_key_context", enc_key_context, (size_t)enc_key_context_length); + } + if (NO_ERROR != crypto_engine->ValidateKeybox()) { + LOGE("[L3Crypto_GenerateDerivedKeys(): ERROR_KEYBOX_INVALID]"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[L3Crypto_GenerateDerivedKeys(): ERROR_NO_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + + const std::vector mac_ctx_str(mac_key_context, + mac_key_context + mac_key_context_length); + const std::vector enc_ctx_str(enc_key_context, + enc_key_context + enc_key_context_length); + + // Generate mac and encryption keys for current session context + if (!session_ctx->DeriveKeys(mac_ctx_str, enc_ctx_str)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + return OEMCrypto_SUCCESS; +} + +extern "C" +OEMCryptoResult L3Crypto_GenerateSignature( + OEMCrypto_SESSION session, + const uint8_t* message, + size_t message_length, + uint8_t* signature, + size_t* signature_length) { + if (trace_all_calls) { + printf("-- OEMCryptoResult L3Crypto_GenerateSignature(\n"); + dump_hex("message", message, message_length); + } + + if (NO_ERROR != crypto_engine->ValidateKeybox()) { + LOGE("[L3Crypto_GenerateSignature(): ERROR_KEYBOX_INVALID]"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + + if (message == NULL || message_length == 0 || + signature == NULL || signature_length == 0) { + LOGE("[L3Crypto_GenerateSignature(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[L3Crypto_GenerateSignature(): ERROR_NO_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + + if (session_ctx->GenerateSignature(message, + message_length, + signature, + signature_length)) { + if (trace_all_calls) { + dump_hex("signature", signature, *signature_length); + } + return OEMCrypto_SUCCESS; + } + return OEMCrypto_ERROR_UNKNOWN_FAILURE;; +} + +bool RangeCheck(const uint8_t* message, + uint32_t message_length, + const uint8_t* field, + uint32_t field_length, + bool allow_null) { + if (field == NULL) return allow_null; + if (field < message) return false; + if (field + field_length > message + message_length) return false; + return true; +} + +extern "C" +OEMCryptoResult L3Crypto_LoadKeys(OEMCrypto_SESSION session, + const uint8_t* message, + size_t message_length, + const uint8_t* signature, + size_t signature_length, + const uint8_t* enc_mac_key_iv, + const uint8_t* enc_mac_key, + size_t num_keys, + const OEMCrypto_KeyObject* key_array) { + if (trace_all_calls) { + printf("-- OEMCryptoResult L3Crypto_LoadKeys(OEMCrypto_SESSION session,\n"); + dump_hex("message", message, message_length); + dump_hex("signature", signature, signature_length); + dump_hex("enc_mac_key_iv", enc_mac_key_iv, wvcdm::KEY_IV_SIZE); + dump_hex("enc_mac_key", enc_mac_key, wvcdm::MAC_KEY_SIZE); + for (size_t i = 0; i < num_keys; i++) { + printf("key_array[%d].key_id_length=%d;\n", i, key_array[i].key_id_length); + dump_array_part("key_array", i, "key_id", + key_array[i].key_id, key_array[i].key_id_length); + dump_array_part("key_array", i, "key_data_iv", + key_array[i].key_data_iv, wvcdm::KEY_IV_SIZE); + dump_array_part("key_array", i, "key_data", + key_array[i].key_data, wvcdm::KEY_IV_SIZE); + dump_array_part("key_array", i, "key_control_iv", + key_array[i].key_control_iv, wvcdm::KEY_IV_SIZE); + dump_array_part("key_array", i, "key_control", + key_array[i].key_control, wvcdm::KEY_IV_SIZE); + } + } + + if (NO_ERROR != crypto_engine->ValidateKeybox()) { + LOGE("[L3Crypto_LoadKeys(): ERROR_KEYBOX_INVALID]"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[L3Crypto_LoadKeys(): ERROR_NO_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + + if (message == NULL || message_length == 0 || + signature == NULL || signature_length == 0 || + key_array == NULL || num_keys == 0) { + LOGE("[L3Crypto_LoadKeys(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + // Range check + if (!RangeCheck(message, message_length, enc_mac_key, + wvcdm::MAC_KEY_SIZE, true) || + !RangeCheck(message, message_length, enc_mac_key_iv, + wvcdm::KEY_IV_SIZE, true)) { + LOGE("[L3Crypto_LoadKeys(): OEMCrypto_ERROR_SIGNATURE_FAILURE - range check.]"); + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + } + + for (unsigned int i = 0; i < num_keys; i++) { + if (!RangeCheck(message, message_length, key_array[i].key_id, + key_array[i].key_id_length, false) || + !RangeCheck(message, message_length, key_array[i].key_data, + wvcdm::KEY_SIZE, false) || + !RangeCheck(message, message_length, key_array[i].key_data_iv, + wvcdm::KEY_IV_SIZE, false) || + !RangeCheck(message, message_length, key_array[i].key_control, + wvcdm::KEY_CONTROL_SIZE, true) || + !RangeCheck(message, message_length, key_array[i].key_control_iv, + wvcdm::KEY_IV_SIZE, true)) { + LOGE("[L3Crypto_LoadKeys(): OEMCrypto_ERROR_SIGNATURE_FAILURE -range check %d]", i); + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + } + } + + // Validate message signature + if (!session_ctx->ValidateMessage(message, message_length, signature, signature_length)) { + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + } + + // Decrypt and install keys in key object + std::vector key_id; + std::vector enc_key_data; + std::vector key_data_iv; + std::vector key_control; + std::vector key_control_iv; + for (unsigned int i = 0; i < num_keys; i++) { + key_id.assign(key_array[i].key_id, + key_array[i].key_id + key_array[i].key_id_length); + enc_key_data.assign(key_array[i].key_data, + key_array[i].key_data + wvcdm::KEY_SIZE); + key_data_iv.assign(key_array[i].key_data_iv, + key_array[i].key_data_iv + wvcdm::KEY_IV_SIZE); + if (key_array[i].key_control == NULL) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + key_control.assign(key_array[i].key_control, + key_array[i].key_control + wvcdm::KEY_CONTROL_SIZE); + key_control_iv.assign(key_array[i].key_control_iv, + key_array[i].key_control_iv + wvcdm::KEY_IV_SIZE); + + if (!session_ctx->InstallKey(key_id, enc_key_data, key_data_iv, key_control, + key_control_iv)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + } + + // enc_mac_key can be NULL if license renewal is not supported + if (enc_mac_key == NULL) return OEMCrypto_SUCCESS; + + // V2 license protocol: update mac key after processing license response + const std::vector enc_mac_key_str = std::vector( + enc_mac_key, enc_mac_key + wvcdm::MAC_KEY_SIZE); + const std::vector enc_mac_key_iv_str = std::vector( + enc_mac_key_iv, enc_mac_key_iv + wvcdm::KEY_IV_SIZE); + + if (!session_ctx->UpdateMacKey(enc_mac_key_str, enc_mac_key_iv_str)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + return OEMCrypto_SUCCESS; +} + +extern "C" +OEMCryptoResult L3Crypto_RefreshKeys(OEMCrypto_SESSION session, + const uint8_t* message, + size_t message_length, + const uint8_t* signature, + size_t signature_length, + size_t num_keys, + const OEMCrypto_KeyRefreshObject* key_array) { + if (trace_all_calls) { + printf("-- OEMCryptoResult L3Crypto_RefreshKeys(OEMCrypto_SESSION session,\n"); + } + + if (NO_ERROR != crypto_engine->ValidateKeybox()) { + LOGE("[L3Crypto_RefreshKeys(): ERROR_KEYBOX_INVALID]"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[L3Crypto_RefreshKeys(): ERROR_NO_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + + if (message == NULL || message_length == 0 || + signature == NULL || signature_length == 0) { + LOGE("[L3Crypto_RefreshKeys(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + // Range check + for (unsigned int i = 0; i < num_keys; i++) { + if (!RangeCheck(message, message_length, key_array[i].key_id, + key_array[i].key_id_length, true) || + !RangeCheck(message, message_length, key_array[i].key_control, + wvcdm::KEY_CONTROL_SIZE, true) || + !RangeCheck(message, message_length, key_array[i].key_control_iv, + wvcdm::KEY_IV_SIZE, true)) { + LOGE("[L3Crypto_RefreshKeys(): OEMCrypto_ERROR_SIGNATURE_FAILURE]"); + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + } + } + + // Validate message signature + if (!session_ctx->ValidateMessage(message, message_length, + signature, signature_length)) { + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + } + + // Decrypt and refresh keys in key refresh object + std::vector key_id; + std::vector key_control; + std::vector key_control_iv; + for (unsigned int i = 0; i < num_keys; i++) { + // TODO(gmorgan): key_id may be null if special control key type (TBS) + if (key_array[i].key_id != NULL) { + key_id.assign(key_array[i].key_id, + key_array[i].key_id + key_array[i].key_id_length); + } else { + key_id.clear(); + } + if (key_array[i].key_control != NULL) { + key_control.assign(key_array[i].key_control, + key_array[i].key_control + wvcdm::KEY_CONTROL_SIZE); + key_control_iv.assign(key_array[i].key_control_iv, + key_array[i].key_control_iv + wvcdm::KEY_IV_SIZE); + } else { + key_control.clear(); + key_control_iv.clear(); + } + + if (!session_ctx->RefreshKey(key_id, key_control, key_control_iv)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + } + + return OEMCrypto_SUCCESS; +} + +extern "C" +OEMCryptoResult L3Crypto_SelectKey(const OEMCrypto_SESSION session, + const uint8_t* key_id, + size_t key_id_length) { + if (trace_all_calls) { + printf("-- OEMCryptoResult L3Crypto_SelectKey(const OEMCrypto_SESSION session,\n"); + dump_hex("key_id", key_id, key_id_length); + } + if (NO_ERROR != crypto_engine->ValidateKeybox()) { + LOGE("[L3Crypto_SelectKey(): ERROR_KEYBOX_INVALID]"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[L3Crypto_SelectKey(): ERROR_NO_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + + const std::vector key_id_str = std::vector(key_id, key_id + key_id_length); + if (!session_ctx->SelectContentKey(key_id_str)) { + LOGE("[L3Crypto_SelectKey(): FAIL]"); + return OEMCrypto_ERROR_NO_CONTENT_KEY; + } + + return OEMCrypto_SUCCESS; +} + +extern "C" +OEMCryptoResult L3Crypto_DecryptCTR(OEMCrypto_SESSION session, + const uint8_t *data_addr, + size_t data_length, + bool is_encrypted, + const uint8_t *iv, + size_t offset, + const OEMCrypto_DestBufferDesc* out_buffer) { + if (trace_all_calls) { + printf("-- OEMCryptoResult L3Crypto_DecryptCTR(OEMCrypto_SESSION session,\n"); + } + wvoec_obfs::BufferType buffer_type = BUFFER_TYPE_DIRECT; + void *destination = NULL; + size_t max_length = 0; + switch (out_buffer->type) { + case OEMCrypto_BufferType_Clear: + buffer_type = BUFFER_TYPE_CLEAR; + destination = out_buffer->buffer.clear.address; + max_length = out_buffer->buffer.clear.max_length; + break; + case OEMCrypto_BufferType_Secure: + buffer_type = BUFFER_TYPE_SECURE; + destination = out_buffer->buffer.secure.handle; + max_length = out_buffer->buffer.secure.max_length; + break; + default: + case OEMCrypto_BufferType_Direct: + buffer_type = BUFFER_TYPE_DIRECT; + destination = NULL; + break; + } + + if (buffer_type != BUFFER_TYPE_DIRECT && max_length < data_length) { + LOGE("[L3Crypto_DecryptCTR(): OEMCrypto_ERROR_SHORT_BUFFER]"); + return OEMCrypto_ERROR_SHORT_BUFFER; + } + + if (NO_ERROR != crypto_engine->ValidateKeybox()) { + LOGE("[L3Crypto_DecryptCTR(): ERROR_KEYBOX_INVALID]"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[L3Crypto_DecryptCTR(): ERROR_NO_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + + if (data_addr == NULL || data_length == 0 || + iv == NULL || out_buffer == NULL) { + LOGE("[L3Crypto_DecryptCTR(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + std::vector iv_v(iv, iv+16); + std::vector content(data_addr, data_addr+data_length); + + if (!crypto_engine->DecryptCTR(session_ctx, iv_v, (int)offset, + content, is_encrypted, + destination, buffer_type)) { + LOGE("[L3Crypto_DecryptCTR(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + return OEMCrypto_SUCCESS; +} + +extern "C" +OEMCryptoResult L3Crypto_InstallKeybox(const uint8_t* keybox, + size_t keyBoxLength) { + if (trace_all_calls) { + printf("-- OEMCryptoResult L3Crypto_InstallKeybox(const uint8_t *keybox,\n"); + } + if (crypto_engine->keybox().InstallKeybox(keybox, keyBoxLength)) { + return OEMCrypto_SUCCESS; + } + return OEMCrypto_ERROR_WRITE_KEYBOX; +} + +extern "C" +OEMCryptoResult L3Crypto_IsKeyboxValid(void) { + if (trace_all_calls) { + printf("-- OEMCryptoResult L3Crypto_IsKeyboxValid(void) {\n"); + } + switch(crypto_engine->ValidateKeybox()) { + case NO_ERROR: return OEMCrypto_SUCCESS; + case BAD_CRC: return OEMCrypto_ERROR_BAD_CRC; + case BAD_MAGIC: return OEMCrypto_ERROR_BAD_MAGIC; + default: + case OTHER_ERROR: return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +} + +extern "C" +OEMCryptoResult L3Crypto_GetDeviceID(uint8_t* deviceID, + size_t* idLength) { + if (trace_all_calls) { + printf("-- OEMCryptoResult L3Crypto_GetDeviceID(uint8_t* deviceID,\n"); + } + std::vector dev_id_string = crypto_engine->keybox().device_id(); + if (dev_id_string.empty()) { + LOGE("[L3Crypto_GetDeviceId(): Keybox Invalid]"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + + size_t dev_id_len = dev_id_string.size(); + if (*idLength < dev_id_len) { + *idLength = dev_id_len; + LOGE("[L3Crypto_GetDeviceId(): ERROR_SHORT_BUFFER]"); + return OEMCrypto_ERROR_SHORT_BUFFER; + } + memset(deviceID, 0, *idLength); + memcpy(deviceID, &dev_id_string[0], dev_id_len); + *idLength = dev_id_len; + LOGD("[L3Crypto_GetDeviceId(): success]"); + return OEMCrypto_SUCCESS; +} + +extern "C" +OEMCryptoResult L3Crypto_GetKeyData(uint8_t* keyData, + size_t* keyDataLength) { + if (trace_all_calls) { + printf("-- OEMCryptoResult L3Crypto_GetKeyData(uint8_t* keyData,\n"); + } + size_t length = crypto_engine->keybox().key_data_length(); + if (*keyDataLength < length) { + *keyDataLength = length; + LOGE("[L3Crypto_GetKeyData(): ERROR_SHORT_BUFFER]"); + return OEMCrypto_ERROR_SHORT_BUFFER; + } + memset(keyData, 0, *keyDataLength); + memcpy(keyData, crypto_engine->keybox().key_data(), length); + *keyDataLength = length; + LOGD("[L3Crypto_GetKeyData(): success]"); + return OEMCrypto_SUCCESS; +} + +extern "C" +OEMCryptoResult L3Crypto_GetRandom(uint8_t* randomData, size_t dataLength) { + if (trace_all_calls) { + printf("-- OEMCryptoResult L3Crypto_GetRandom(uint8_t* randomData, size_t dataLength) {\n"); + } + if (!randomData) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (RAND_bytes(randomData, dataLength)) { + return OEMCrypto_SUCCESS; + } + return OEMCrypto_ERROR_UNKNOWN_FAILURE; +} + +extern "C" +OEMCryptoResult L3Crypto_WrapKeybox(const uint8_t* keybox, + size_t keyBoxLength, + uint8_t* wrappedKeybox, + size_t* wrappedKeyBoxLength, + const uint8_t* transportKey, + size_t transportKeyLength) { + if (trace_all_calls) { + printf("-- OEMCryptoResult L3Crypto_WrapKeybox(const uint8_t *keybox,\n"); + } + if (!keybox || !wrappedKeybox || !wrappedKeyBoxLength + || (keyBoxLength != *wrappedKeyBoxLength)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + // This implementation ignores the transport key. For test keys, we + // don't need to encrypt the keybox. + memcpy(wrappedKeybox, keybox, keyBoxLength); + return OEMCrypto_SUCCESS; +} + +}; // namespace wvoec_obfs diff --git a/libwvdrmengine/level3/arm/lock.cpp b/libwvdrmengine/level3/arm/lock.cpp new file mode 100644 index 00000000..0a3b5dca --- /dev/null +++ b/libwvdrmengine/level3/arm/lock.cpp @@ -0,0 +1,54 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Lock class - provides a simple android specific mutex implementation + +#include "lock.h" +#include "utils/Mutex.h" + +namespace wvcdm { + +class Lock::Impl { + public: + android::Mutex lock_; +}; + +Lock::Lock() : impl_(new Lock::Impl()) { +} + +Lock::~Lock() { + delete impl_; + impl_ = NULL; +} + +void Lock::Acquire() { + impl_->lock_.lock(); +} + +void Lock::Release() { + impl_->lock_.unlock(); +} + +bool Lock::Try() { + return (impl_->lock_.tryLock() == 0); +} + +class AutoLock::Impl { + public: + android::Mutex::Autolock *autolock_; +}; + +AutoLock::AutoLock(Lock& lock) : impl_(new AutoLock::Impl()) { + impl_->autolock_ = new android::Mutex::Autolock(lock.impl_->lock_); +} + +AutoLock::AutoLock(Lock* lock) : impl_(new AutoLock::Impl()) { + impl_->autolock_ = new android::Mutex::Autolock(lock->impl_->lock_); +} + +AutoLock::~AutoLock() { + delete impl_->autolock_; + delete impl_; + impl_ = NULL; +} + +}; // namespace wvcdm diff --git a/libwvdrmengine/level3/arm/lock.h b/libwvdrmengine/level3/arm/lock.h new file mode 100644 index 00000000..b099f75a --- /dev/null +++ b/libwvdrmengine/level3/arm/lock.h @@ -0,0 +1,54 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Lock - Platform independent interface for a Mutex class +// +#ifndef L3CRYPTO_LOCK_H_ +#define L3CRYPTO_LOCK_H_ + +#include "wv_cdm_types.h" + +namespace wvcdm { + +// Simple lock class. The implementation is platform dependent. +// +// The lock must be unlocked by the thread that locked it. +// The lock is also not recursive (ie. cannot be taken multiple times). +class Lock { + public: + Lock(); + ~Lock(); + + void Acquire(); + void Release(); + + // Acquires a lock if not held and returns true. + // Returns false if the lock is held by another thread. + bool Try(); + + friend class AutoLock; + + private: + class Impl; + Impl *impl_; + + CORE_DISALLOW_COPY_AND_ASSIGN(Lock); +}; + +// Manages the lock automatically. It will be locked when AutoLock +// is constructed and release when AutoLock goes out of scope +class AutoLock { + public: + explicit AutoLock(Lock& lock); + explicit AutoLock(Lock* lock); + ~AutoLock(); + + private: + class Impl; + Impl *impl_; + + CORE_DISALLOW_COPY_AND_ASSIGN(AutoLock); +}; + +}; // namespace wvcdm + +#endif // L3CRYPTO_LOCK_H_ diff --git a/libwvdrmengine/level3/arm/log.cpp b/libwvdrmengine/level3/arm/log.cpp new file mode 100644 index 00000000..56618032 --- /dev/null +++ b/libwvdrmengine/level3/arm/log.cpp @@ -0,0 +1,33 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Log - implemented using the standard Android logging mechanism + +#define LOG_TAG "WVCdm" +#define LOG_BUF_SIZE 1024 + +#include "log.h" +#include "utils/Log.h" + +namespace wvcdm { + +void log_write(LogPriority level, const char *fmt, ...) { + va_list ap; + char buf[LOG_BUF_SIZE]; + va_start(ap, fmt); + vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); + va_end(ap); + + android_LogPriority prio = ANDROID_LOG_VERBOSE; + + switch(level) { + case LOG_ERROR: prio = ANDROID_LOG_ERROR; break; + case LOG_WARN: prio = ANDROID_LOG_WARN; break; + case LOG_INFO: prio = ANDROID_LOG_INFO; break; + case LOG_DEBUG: prio = ANDROID_LOG_DEBUG; break; + case LOG_VERBOSE: prio = ANDROID_LOG_VERBOSE; break; + } + + __android_log_write(prio, LOG_TAG, buf); +} + +}; // namespace wvcdm diff --git a/libwvdrmengine/level3/arm/log.h b/libwvdrmengine/level3/arm/log.h new file mode 100644 index 00000000..ce4204f3 --- /dev/null +++ b/libwvdrmengine/level3/arm/log.h @@ -0,0 +1,31 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Log - Platform independent interface for a Logging class +// +#ifndef OEMCRYPTO_LOG_H_ +#define OEMCRYPTO_LOG_H_ + +namespace wvcdm { + +// Simple logging class. The implementation is platform dependent. + +typedef enum { + LOG_ERROR, + LOG_WARN, + LOG_INFO, + LOG_DEBUG, + LOG_VERBOSE +} LogPriority; + +void log_write(LogPriority priority, const char *fmt, ...); + +// Log APIs +#define LOGE(...) ((void)log_write(wvcdm::LOG_ERROR, __VA_ARGS__)) +#define LOGW(...) ((void)log_write(wvcdm::LOG_WARN, __VA_ARGS__)) +#define LOGI(...) ((void)log_write(wvcdm::LOG_INFO, __VA_ARGS__)) +#define LOGD(...) ((void)log_write(wvcdm::LOG_DEBUG, __VA_ARGS__)) +#define LOGV(...) ((void)log_write(wvcdm::LOG_VERBOSE, __VA_ARGS__)) + +}; // namespace wvcdm + +#endif // OEMCRYPTO_LOG_H_ diff --git a/libwvdrmengine/level3/arm/string_conversions.cpp b/libwvdrmengine/level3/arm/string_conversions.cpp new file mode 100644 index 00000000..f8221ee8 --- /dev/null +++ b/libwvdrmengine/level3/arm/string_conversions.cpp @@ -0,0 +1,79 @@ +// Copyright 2013 Google Inc. All Rights Reserved. + +#include "string_conversions.h" + +#include +#include +#include + +#include "log.h" + +namespace wvcdm { + +static bool CharToDigit(char ch, unsigned char* digit) { + if (ch >= '0' && ch <= '9') { + *digit = ch - '0'; + } else { + ch = tolower(ch); + if ((ch >= 'a') && (ch <= 'f')) { + *digit = ch - 'a' + 10; + } else { + return false; + } + } + return true; +} + +// converts an ascii hex string(2 bytes per digit) into a decimal byte string +std::vector a2b_hex(const std::string& byte) { + std::vector array(0); + unsigned int count = byte.size(); + if (count == 0 || (count % 2) != 0) { + LOGE("Invalid input size %u for string %s", count, byte.c_str()); + return array; + } + + for (unsigned int i = 0; i < count / 2; ++i) { + unsigned char msb = 0; // most significant 4 bits + unsigned char lsb = 0; // least significant 4 bits + if (!CharToDigit(byte[i * 2], &msb) || + !CharToDigit(byte[i * 2 + 1], &lsb)) { + LOGE("Invalid hex value %c%c at index %d", byte[i*2], byte[i*2+1], i); + return array; + } + array.push_back((msb << 4) | lsb); + } + return array; +} + +std::string b2a_hex(const std::vector& byte) { + return HexEncode(&byte[0], byte.size()); +} + +std::string HexEncode(const uint8_t* in_buffer, unsigned int size) { + static const char kHexChars[] = "0123456789ABCDEF"; + + // Each input byte creates two output hex characters. + std::string out_buffer(size * 2, '\0'); + + for (unsigned int i = 0; i < size; ++i) { + char byte = in_buffer[i]; + out_buffer[(i << 1)] = kHexChars[(byte >> 4) & 0xf]; + out_buffer[(i << 1) + 1] = kHexChars[byte & 0xf]; + } + return out_buffer; +} + +std::string IntToString(int value) { + // log10(2) ~= 0.3 bytes needed per bit or per byte log10(2**8) ~= 2.4. + // So round up to allocate 3 output characters per byte, plus 1 for '-'. + const int kOutputBufSize = 3 * sizeof(int) + 1; + char buffer[kOutputBufSize]; + memset(buffer, 0, kOutputBufSize); + snprintf(buffer, kOutputBufSize, "%d", value); + + std::string out_string(buffer, sizeof(buffer)); + return out_string; +} + +}; // namespace wvcdm diff --git a/libwvdrmengine/level3/arm/string_conversions.h b/libwvdrmengine/level3/arm/string_conversions.h new file mode 100644 index 00000000..7d916288 --- /dev/null +++ b/libwvdrmengine/level3/arm/string_conversions.h @@ -0,0 +1,18 @@ +// Copyright 2013 Google Inc. All Rights Reserved. + +#ifndef OEMCRYPTO_STRING_CONVERSIONS_H_ +#define OEMCRYPTO_STRING_CONVERSIONS_H_ + +#include +#include + +namespace wvcdm { + +std::vector a2b_hex(const std::string& b); +std::string b2a_hex(const std::vector& b); +std::string HexEncode(const uint8_t* bytes, unsigned size); +std::string IntToString(int value); + +}; // namespace wvcdm + +#endif // OEMCRYPTO_STRING_CONVERSIONS_H_ diff --git a/libwvdrmengine/level3/arm/wv_cdm_constants.h b/libwvdrmengine/level3/arm/wv_cdm_constants.h new file mode 100644 index 00000000..b47e690a --- /dev/null +++ b/libwvdrmengine/level3/arm/wv_cdm_constants.h @@ -0,0 +1,14 @@ +// Copyright 2013 Google Inc. All Rights Reserved. + +#ifndef OEMCRYPTO_WV_CDM_CONSTANTS_H_ +#define OEMCRYPTO_WV_CDM_CONSTANTS_H_ + +namespace wvcdm { +static const size_t KEY_CONTROL_SIZE = 16; +static const size_t KEY_IV_SIZE = 16; +static const size_t KEY_PAD_SIZE = 16; +static const size_t KEY_SIZE = 16; +static const size_t MAC_KEY_SIZE = 32; +} // namespace wvcdm + +#endif // OEMCRYPTO_WV_CDM_CONSTANTS_H_ diff --git a/libwvdrmengine/level3/arm/wv_cdm_types.h b/libwvdrmengine/level3/arm/wv_cdm_types.h new file mode 100644 index 00000000..0d29838b --- /dev/null +++ b/libwvdrmengine/level3/arm/wv_cdm_types.h @@ -0,0 +1,49 @@ +// Copyright 2013 Google Inc. All Rights Reserved. + +#ifndef L3CRYPTO_WV_CDM_TYPES_H_ +#define L3CRYPTO_WV_CDM_TYPES_H_ + +#include +#include +#include + +namespace wvcdm { + +typedef std::string CdmKeySystem; +typedef std::string CdmInitData; +typedef std::string CdmKeyMessage; +typedef std::string CdmKeyResponse; +typedef std::string KeyId; +typedef std::string CdmSessionId; +typedef std::string RequestId; +typedef uint32_t CryptoResult; +typedef uint32_t CryptoSessionId; +typedef std::string CryptoKeyId; + +enum CdmResponseType { + NO_ERROR, + UNKNOWN_ERROR, + KEY_ADDED, + KEY_ERROR, + KEY_MESSAGE, + NEED_KEY, + KEY_CANCELED, +}; + +#define CORE_DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) + +enum CdmEventType { + LICENSE_EXPIRED, + LICENSE_RENEWAL_NEEDED +}; + +// forward class references +class KeyMessage; +class Request; +class Key; + +} // namespace wvcdm + +#endif // L3CRYPTO_WV_CDM_TYPES_H_ diff --git a/libwvdrmengine/level3/arm/wv_keybox.h b/libwvdrmengine/level3/arm/wv_keybox.h new file mode 100644 index 00000000..6f937260 --- /dev/null +++ b/libwvdrmengine/level3/arm/wv_keybox.h @@ -0,0 +1,24 @@ +// Copyright 2013 Google Inc. All Rights Reserved. + +#ifndef WV_KEYBOX_H_ +#define WV_KEYBOX_H_ + +namespace wvoec_obfs { + +// This is the format of a Widevine keybox. +typedef struct { // 128 bytes total. + // C character string identifying the device. Null terminated. + uint8_t device_id_[32]; + // 128 bit AES key assigned to device. Generated by Widevine. + uint8_t device_key_[16]; + // Key Data. Encrypted data. + uint8_t data_[72]; + // Constant code used to recognize a valid keybox "kbox" = 0x6b626f78. + uint8_t magic_[4]; + // The CRC checksum of the first 124 bytes of the keybox. + uint8_t crc_[4]; +} WidevineKeybox; + +} + +#endif // WV_KEYBOX_H_ diff --git a/libwvdrmengine/level3/arm/wvcrc.cpp b/libwvdrmengine/level3/arm/wvcrc.cpp new file mode 100644 index 00000000..8398efa4 --- /dev/null +++ b/libwvdrmengine/level3/arm/wvcrc.cpp @@ -0,0 +1,93 @@ +/********************************************************************* + * wvcrc32.cpp + * + * (c) Copyright 2011-2012 Google, Inc. + * + * Compte CRC32 Checksum. Needed for verification of WV Keybox. + *********************************************************************/ + +#include "wvcrc32.h" + +#define INIT_CRC32 0xffffffff + +uint32_t wvrunningcrc32(uint8_t* p_begin, int i_count, uint32_t i_crc) { + static uint32_t CRC32[256] = { + 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, + 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005, + 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, + 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, + 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, + 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, + 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, + 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd, + 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, + 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, + 0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, + 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, + 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, + 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, + 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, + 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, + 0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, + 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, + 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, + 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, + 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, + 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, + 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066, + 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, + 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, + 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692, + 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, + 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, + 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, + 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, + 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, + 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a, + 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, + 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, + 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, + 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, + 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, + 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b, + 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, + 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, + 0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, + 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, + 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, + 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, + 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, + 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, + 0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, + 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, + 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, + 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, + 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, + 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, + 0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30, + 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, + 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, + 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, + 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, + 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, + 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, + 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, + 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, + 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c, + 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, + 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 + }; + + /* Calculate the CRC */ + while (i_count > 0) { + i_crc = (i_crc << 8) ^ CRC32[(i_crc >> 24) ^ ((uint32_t) * p_begin) ]; + p_begin++; + i_count--; + } + + return(i_crc); +} + +uint32_t wvcrc32(uint8_t* p_begin, int i_count) { + return(wvrunningcrc32(p_begin, i_count, INIT_CRC32)); +} diff --git a/libwvdrmengine/level3/arm/wvcrc32.h b/libwvdrmengine/level3/arm/wvcrc32.h new file mode 100644 index 00000000..4eac8d1f --- /dev/null +++ b/libwvdrmengine/level3/arm/wvcrc32.h @@ -0,0 +1,16 @@ +/********************************************************************* + * wvcrc32.h + * + * (c) Copyright 2011-2012 Google, Inc. + * + * Compte CRC32 Checksum. Needed for verification of WV Keybox. + *********************************************************************/ + +#ifndef WV_CRC_32_H_ +#define WV_CRC_32_H_ + +#include + +uint32_t wvcrc32(uint8_t* p_begin, int i_count); + +#endif // WV_CRC_32_H_ diff --git a/libwvdrmengine/mediacrypto/Android.mk b/libwvdrmengine/mediacrypto/Android.mk new file mode 100644 index 00000000..d21a567b --- /dev/null +++ b/libwvdrmengine/mediacrypto/Android.mk @@ -0,0 +1,20 @@ +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + src/WVCryptoPlugin.cpp \ + +LOCAL_C_INCLUDES := \ + bionic \ + external/stlport/stlport \ + frameworks/av/include \ + frameworks/native/include \ + vendor/widevine/libwvdrmengine/cdm/core/include \ + vendor/widevine/libwvdrmengine/cdm/include \ + vendor/widevine/libwvdrmengine/mediacrypto/include \ + +LOCAL_MODULE := libwvdrmcryptoplugin + +LOCAL_MODULE_TAGS := optional + +include $(BUILD_STATIC_LIBRARY) diff --git a/libwvdrmengine/mediacrypto/include/WVCryptoPlugin.h b/libwvdrmengine/mediacrypto/include/WVCryptoPlugin.h new file mode 100644 index 00000000..30ae514b --- /dev/null +++ b/libwvdrmengine/mediacrypto/include/WVCryptoPlugin.h @@ -0,0 +1,37 @@ +// +// Copyright 2013 Google Inc. All Rights Reserved. +// + +#ifndef WV_CRYPTO_PLUGIN_H_ +#define WV_CRYPTO_PLUGIN_H_ + +#include "media/hardware/CryptoAPI.h" +#include "media/stagefright/foundation/ABase.h" +#include "media/stagefright/foundation/AString.h" +#include "wv_content_decryption_module.h" + +namespace wvdrm { + +class WVCryptoPlugin : public android::CryptoPlugin { + public: + WVCryptoPlugin(const void* data, size_t size, + wvcdm::WvContentDecryptionModule* cdm); + virtual ~WVCryptoPlugin() {} + + virtual bool requiresSecureDecoderComponent(const char *mime) const; + + virtual ssize_t decrypt(bool secure, const uint8_t key[16], + const uint8_t iv[16], Mode mode, const void* srcPtr, + const SubSample* subSamples, size_t numSubSamples, + void* dstPtr, android::AString* errorDetailMsg); + + private: + DISALLOW_EVIL_CONSTRUCTORS(WVCryptoPlugin); + + wvcdm::WvContentDecryptionModule* const mCDM; + const wvcdm::CdmSessionId mSessionId; +}; + +} // namespace wvdrm + +#endif // WV_CRYPTO_PLUGIN_H_ diff --git a/libwvdrmengine/mediacrypto/src/WVCryptoPlugin.cpp b/libwvdrmengine/mediacrypto/src/WVCryptoPlugin.cpp new file mode 100644 index 00000000..4f658bcb --- /dev/null +++ b/libwvdrmengine/mediacrypto/src/WVCryptoPlugin.cpp @@ -0,0 +1,106 @@ +// +// Copyright 2013 Google Inc. All Rights Reserved. +// + +//#define LOG_NDEBUG 0 +#define LOG_TAG "WVCdm" +#include + +#include "WVCryptoPlugin.h" + +#include +#include + +#include "utils/Errors.h" +#include "wv_cdm_constants.h" + +namespace wvdrm { + +using namespace android; +using namespace std; +using namespace wvcdm; + +WVCryptoPlugin::WVCryptoPlugin(const void* data, size_t size, + WvContentDecryptionModule* cdm) + : mCDM(cdm), + mSessionId(static_cast(data), size) {} + +bool WVCryptoPlugin::requiresSecureDecoderComponent(const char *mime) const { + // TODO: Determine if we are using L1 or L3 and return an appropriate value. + // For Demo 2, we are always L3. + return false; +} + +// Returns negative values for error code and +// positive values for the size of decrypted data. In theory, the output size +// can be larger than the input size, but in practice this should never happen +// for AES-CTR. +ssize_t WVCryptoPlugin::decrypt(bool secure, const uint8_t key[KEY_ID_SIZE], + const uint8_t iv[KEY_IV_SIZE], Mode mode, + const void* srcPtr, const SubSample* subSamples, + size_t numSubSamples, void* dstPtr, + AString *errorDetailMsg) { + if (mode != kMode_Unencrypted && mode != kMode_AES_CTR) { + return BAD_TYPE; + } + + if (secure) { + // TODO: Can't do secure in the Demo 2 milestone + return -EPERM; + } + + // Convert parameters to the form the CDM wishes to consume them in. + const KeyId keyId(reinterpret_cast(key), KEY_ID_SIZE); + const vector ivVector(iv, iv + KEY_IV_SIZE); + const uint8_t* const source = static_cast(srcPtr); + uint8_t* const dest = static_cast(dstPtr); + + // Iterate through subsamples, sending them to the CDM serially. + size_t offset = 0; + + for (size_t i = 0; i < numSubSamples; ++i) { + const SubSample &subSample = subSamples[i]; + + if (mode == kMode_Unencrypted && subSample.mNumBytesOfEncryptedData != 0) { + return -EINVAL; + } + + // "Decrypt" any unencrypted data. Per the ISO-CENC standard, clear data + // comes before encrypted data. + if (subSample.mNumBytesOfClearData != 0) { + CdmResponseType res = mCDM->Decrypt(mSessionId, false, keyId, + source + offset, + subSample.mNumBytesOfClearData, + ivVector, offset % 16, dest + offset); + + if (res != wvcdm::NO_ERROR) { + ALOGE("Decrypt error result in session %s during unencrypted block: %d", + mSessionId.c_str(), res); + return -EINVAL; + } + + offset += subSample.mNumBytesOfClearData; + } + + // Decrypt any encrypted data. Per the ISO-CENC standard, encrypted data + // comes after clear data. + if (subSample.mNumBytesOfEncryptedData != 0) { + CdmResponseType res = mCDM->Decrypt(mSessionId, true, keyId, + source + offset, + subSample.mNumBytesOfEncryptedData, + ivVector, offset % 16, dest + offset); + + if (res != wvcdm::NO_ERROR) { + ALOGE("Decrypt error result in session %s during encrypted block: %d", + mSessionId.c_str(), res); + return -EINVAL; + } + + offset += subSample.mNumBytesOfEncryptedData; + } + } + + return static_cast(offset); +} + +} // namespace wvdrm diff --git a/libwvdrmengine/mediacrypto/test/Android.mk b/libwvdrmengine/mediacrypto/test/Android.mk new file mode 100644 index 00000000..6b109f9f --- /dev/null +++ b/libwvdrmengine/mediacrypto/test/Android.mk @@ -0,0 +1,54 @@ +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + WVCryptoPlugin_test.cpp \ + +LOCAL_C_INCLUDES := \ + bionic \ + external/gtest/include \ + external/stlport/stlport \ + frameworks/av/include \ + frameworks/native/include \ + vendor/widevine/libwvdrmengine/cdm/core/include \ + vendor/widevine/libwvdrmengine/cdm/include \ + vendor/widevine/libwvdrmengine/mediacrypto/include \ + vendor/widevine/libwvdrmengine/mediacrypto/test \ + vendor/widevine/libwvdrmengine/test/gmock/include \ + +LOCAL_STATIC_LIBRARIES := \ + libcdm \ + libgmock \ + libgmock_main \ + libgtest \ + libprotobuf-cpp-2.3.0-lite \ + libwvdrmcryptoplugin \ + +LOCAL_SHARED_LIBRARIES := \ + liblog \ + liboemcrypto \ + libstlport \ + libutils \ + +# CDM's protobuffers are not part of the library +PROTO_SRC_DIR := $(proto_generated_cc_sources_dir)/$(LOCAL_PATH)/core/src + +LOCAL_SRC_FILES += \ + $(PROTO_SRC_DIR)/license_protocol.pb.cc \ + +LOCAL_C_INCLUDES += \ + $(proto_generated_cc_sources_dir)/$(LOCAL_PATH)/core/src \ + external/protobuf/src \ + +LOCAL_ADDITIONAL_DEPENDENCIES += $(proto_generated_headers) + +LOCAL_WHOLE_STATIC_LIBRARIES := \ + license_protocol_protos \ + +# End protobuf section + +LOCAL_MODULE := libwvdrmmediacrypto_test + +LOCAL_MODULE_TAGS := tests + +include $(BUILD_EXECUTABLE) diff --git a/libwvdrmengine/mediacrypto/test/MockCDM.h b/libwvdrmengine/mediacrypto/test/MockCDM.h new file mode 100644 index 00000000..e20536d0 --- /dev/null +++ b/libwvdrmengine/mediacrypto/test/MockCDM.h @@ -0,0 +1,28 @@ +// +// Copyright 2013 Google Inc. All Rights Reserved. +// + +#ifndef WV_CRYPTO_PLUGIN_MOCK_CDM_H_ +#define WV_CRYPTO_PLUGIN_MOCK_CDM_H_ + +#include +#include + +#include "gmock/gmock.h" +#include "wv_cdm_types.h" +#include "wv_content_decryption_module.h" + + +namespace wvcdm { + +class MockCDM : public WvContentDecryptionModule { + public: + MOCK_METHOD8(Decrypt, CdmResponseType(const CdmSessionId&, bool, const KeyId&, + const uint8_t*, size_t, + const std::vector&, size_t, + void*)); +}; + +} // namespace wvcdm + +#endif // WV_CRYPTO_PLUGIN_MOCK_CDM_H_ diff --git a/libwvdrmengine/mediacrypto/test/WVCryptoPlugin_test.cpp b/libwvdrmengine/mediacrypto/test/WVCryptoPlugin_test.cpp new file mode 100644 index 00000000..408ccc36 --- /dev/null +++ b/libwvdrmengine/mediacrypto/test/WVCryptoPlugin_test.cpp @@ -0,0 +1,118 @@ +// +// Copyright 2013 Google Inc. All Rights Reserved. +// + +#include +#include + +#include "gtest/gtest.h" +#include "media/stagefright/foundation/ABase.h" +#include "media/stagefright/foundation/AString.h" +#include "MockCDM.h" +#include "wv_cdm_constants.h" +#include "WVCryptoPlugin.h" + +using namespace android; +using namespace std; +using namespace testing; +using namespace wvcdm; +using namespace wvdrm; + +class WVCryptoPluginTest : public Test { + protected: + static const uint32_t kSessionIdSize = 16; + uint8_t sessionId[kSessionIdSize]; + + uint8_t keyId[KEY_ID_SIZE]; + uint8_t iv[KEY_IV_SIZE]; + + static const uint32_t kDataSize = 64; + uint8_t in[kDataSize]; + uint8_t out[kDataSize]; + + static const uint32_t kSubSampleCount = 3; + CryptoPlugin::SubSample subSamples[kSubSampleCount]; + + virtual void SetUp() { + FILE* fp = fopen("/dev/urandom", "r"); + fread(sessionId, sizeof(uint8_t), kSessionIdSize, fp); + fread(keyId, sizeof(uint8_t), KEY_ID_SIZE, fp); + fread(iv, sizeof(uint8_t), KEY_IV_SIZE, fp); + fread(in, sizeof(uint8_t), kDataSize, fp); + fclose(fp); + + memset(out, 0, sizeof(out)); + + memset(subSamples, 0, sizeof(subSamples)); + subSamples[0].mNumBytesOfEncryptedData = 16; + subSamples[1].mNumBytesOfClearData = 16; + subSamples[1].mNumBytesOfEncryptedData = 24; + subSamples[2].mNumBytesOfEncryptedData = 8; + + // Set default CdmResponseType value for gMock + DefaultValue::Set(wvcdm::NO_ERROR); + } +}; + +TEST_F(WVCryptoPluginTest, CorrectlyReportsSecureBuffers) { + MockCDM cdm; + WVCryptoPlugin plugin(sessionId, kSessionIdSize, &cdm); + + EXPECT_FALSE(plugin.requiresSecureDecoderComponent("video/mp4")) << + "WVCryptoPlugin incorrectly expects a secure video decoder"; + EXPECT_FALSE(plugin.requiresSecureDecoderComponent("audio/aac")) << + "WVCryptoPlugin incorrectly expects a secure audio decoder"; +} + +TEST_F(WVCryptoPluginTest, RejectsSecureDecode) { + MockCDM cdm; + WVCryptoPlugin plugin(sessionId, kSessionIdSize, &cdm); + + // Decrypt should not be called because we specified an unsupported + // security level + EXPECT_CALL(cdm, Decrypt(_, _, _, _, _, _, _, _)) + .Times(0); + + ssize_t res = plugin.decrypt(true, keyId, iv, CryptoPlugin::kMode_AES_CTR, + in, subSamples, kSubSampleCount, out, NULL); + + EXPECT_EQ(static_cast(-EPERM), res) << + "WVCryptoPlugin allowed decryption to proceed despite being asked for an " + "unsupported security level"; +} + +TEST_F(WVCryptoPluginTest, AttemptsToDecrypt) { + MockCDM cdm; + WVCryptoPlugin plugin(sessionId, kSessionIdSize, &cdm); + + // Specify the expected calls to Decrypt + { + InSequence calls; + + EXPECT_CALL(cdm, Decrypt(ElementsAreArray(sessionId, kSessionIdSize), true, + ElementsAreArray(keyId, KEY_ID_SIZE), in, 16, + ElementsAreArray(iv, KEY_IV_SIZE), 0, out)) + .WillOnce(Return(wvcdm::NO_ERROR)); + + EXPECT_CALL(cdm, Decrypt(ElementsAreArray(sessionId, kSessionIdSize), false, + ElementsAreArray(keyId, KEY_ID_SIZE), in + 16, 16, + ElementsAreArray(iv, KEY_IV_SIZE), 0, out + 16)) + .WillOnce(Return(wvcdm::NO_ERROR)); + + EXPECT_CALL(cdm, Decrypt(ElementsAreArray(sessionId, kSessionIdSize), true, + ElementsAreArray(keyId, KEY_ID_SIZE), in + 32, 24, + ElementsAreArray(iv, KEY_IV_SIZE), 0, out + 32)) + .WillOnce(Return(wvcdm::NO_ERROR)); + + EXPECT_CALL(cdm, Decrypt(ElementsAreArray(sessionId, kSessionIdSize), true, + ElementsAreArray(keyId, KEY_ID_SIZE), in + 56, 8, + ElementsAreArray(iv, KEY_IV_SIZE), 8, out + 56)) + .WillOnce(Return(wvcdm::NO_ERROR)); + } + + ssize_t res = plugin.decrypt(false, keyId, iv, CryptoPlugin::kMode_AES_CTR, + in, subSamples, kSubSampleCount, out, NULL); + + EXPECT_EQ(static_cast(kDataSize), res) << + "WVCryptoPlugin decrypted the wrong number of bytes"; +} \ No newline at end of file diff --git a/libwvdrmengine/mediadrm/Android.mk b/libwvdrmengine/mediadrm/Android.mk new file mode 100644 index 00000000..0ddc6bcc --- /dev/null +++ b/libwvdrmengine/mediadrm/Android.mk @@ -0,0 +1,20 @@ +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + src/WVDrmPlugin.cpp \ + +LOCAL_C_INCLUDES := \ + bionic \ + external/stlport/stlport \ + frameworks/av/include \ + frameworks/native/include \ + vendor/widevine/libwvdrmengine/cdm/core/include \ + vendor/widevine/libwvdrmengine/cdm/include \ + vendor/widevine/libwvdrmengine/mediadrm/include \ + +LOCAL_MODULE := libwvdrmdrmplugin + +LOCAL_MODULE_TAGS := optional + +include $(BUILD_STATIC_LIBRARY) diff --git a/libwvdrmengine/mediadrm/include/WVDrmPlugin.h b/libwvdrmengine/mediadrm/include/WVDrmPlugin.h new file mode 100644 index 00000000..30d66e13 --- /dev/null +++ b/libwvdrmengine/mediadrm/include/WVDrmPlugin.h @@ -0,0 +1,80 @@ +// +// Copyright 2013 Google Inc. All Rights Reserved. +// + +#ifndef WV_DRM_PLUGIN_H_ +#define WV_DRM_PLUGIN_H_ + +#include "media/drm/DrmAPI.h" +#include "media/stagefright/foundation/ABase.h" +#include "media/stagefright/foundation/AString.h" +#include "utils/Errors.h" +#include "utils/KeyedVector.h" +#include "utils/List.h" +#include "utils/String8.h" +#include "utils/Vector.h" +#include "wv_content_decryption_module.h" + +namespace wvdrm { + +using android::KeyedVector; +using android::List; +using android::status_t; +using android::String8; +using android::Vector; + +class WVDrmPlugin : public android::DrmPlugin { + public: + WVDrmPlugin(wvcdm::WvContentDecryptionModule* cdm); + virtual ~WVDrmPlugin() {} + + virtual status_t openSession(Vector& sessionId); + + virtual status_t closeSession(const Vector& sessionId); + + virtual status_t getLicenseRequest( + const Vector& sessionId, + const Vector& initData, + const String8& mimeType, + LicenseType licenseType, + const KeyedVector& optionalParameters, + Vector& request, + String8& defaultUrl); + + virtual status_t provideLicenseResponse(const Vector& sessionId, + const Vector& response); + + virtual status_t removeLicense(const Vector& sessionId); + + virtual status_t queryLicenseStatus( + const Vector& sessionId, + KeyedVector& infoMap) const; + + virtual status_t getProvisionRequest(Vector& request, + String8& defaultUrl); + + virtual status_t provideProvisionResponse(const Vector& response); + + virtual status_t getSecureStops(List >& secureStops); + + virtual status_t releaseSecureStops(const Vector& ssRelease); + + virtual status_t getPropertyString(const String8& name, String8& value) const; + + virtual status_t getPropertyByteArray(const String8& name, + Vector& value) const; + + virtual status_t setPropertyString(const String8& name, const String8& value); + + virtual status_t setPropertyByteArray(const String8& name, + const Vector& value); + + private: + DISALLOW_EVIL_CONSTRUCTORS(WVDrmPlugin); + + wvcdm::WvContentDecryptionModule* mCDM; +}; + +} // namespace wvdrm + +#endif // WV_DRM_PLUGIN_H_ diff --git a/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp b/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp new file mode 100644 index 00000000..90421b41 --- /dev/null +++ b/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp @@ -0,0 +1,257 @@ +// +// Copyright 2013 Google Inc. All Rights Reserved. +// + +//#define LOG_NDEBUG 0 +#define LOG_TAG "WVCdm" +#include + +#include "WVDrmPlugin.h" + +#include +#include + +#include "utils/Errors.h" +#include "wv_cdm_constants.h" + +namespace wvdrm { + +using namespace android; +using namespace std; +using namespace wvcdm; + +WVDrmPlugin::WVDrmPlugin(WvContentDecryptionModule* cdm) : mCDM(cdm) {} + +status_t WVDrmPlugin::openSession(Vector& sessionId) { + CdmSessionId cdmSessionId; + CdmResponseType res = mCDM->OpenSession("com.widevine", &cdmSessionId); + + if (res != wvcdm::NO_ERROR) { + return android::UNKNOWN_ERROR; + } + + sessionId.clear(); + sessionId.appendArray(reinterpret_cast(cdmSessionId.data()), + cdmSessionId.size()); + + return android::OK; +} + +status_t WVDrmPlugin::closeSession(const Vector& sessionId) { + CdmSessionId cdmSessionId(sessionId.begin(), sessionId.end()); + CdmResponseType res = mCDM->CloseSession(cdmSessionId); + + if (res == wvcdm::NO_ERROR) { + return android::OK; + } else { + return android::UNKNOWN_ERROR; + } +} + +status_t WVDrmPlugin::getLicenseRequest( + const Vector& sessionId, + const Vector& initData, + const String8& mimeType, + LicenseType licenseType, + const KeyedVector& optionalParameters, + Vector& request, + String8& defaultUrl) { + CdmLicenseType cdmLicenseType; + if (licenseType == kLicenseType_Offline) { + cdmLicenseType = kLicenseTypeOffline; + } else if (licenseType == kLicenseType_Streaming) { + cdmLicenseType = kLicenseTypeStreaming; + } else { + return BAD_TYPE; + } + + CdmSessionId cdmSessionId(sessionId.begin(), sessionId.end()); + CdmInitData cdmInitData(initData.begin(), initData.end()); + // TODO: Do something with mimeType? + + CdmNameValueMap cdmParameters; + for (size_t i = 0; i < optionalParameters.size(); ++i) { + const String8& key = optionalParameters.keyAt(i); + const String8& value = optionalParameters.valueAt(i); + + string cdmKey(key.string(), key.size()); + string cdmValue(value.string(), value.size()); + + cdmParameters[cdmKey] = cdmValue; + } + + CdmKeyMessage keyRequest; + + CdmResponseType res = mCDM->GenerateKeyRequest(cdmSessionId, cdmInitData, + cdmLicenseType, + cdmParameters, &keyRequest); + + if (res != wvcdm::KEY_MESSAGE) { + return android::UNKNOWN_ERROR; + } + + // TODO: Do something more with defaultUrl? + defaultUrl.clear(); + + request.clear(); + request.appendArray(reinterpret_cast(keyRequest.data()), + keyRequest.size()); + + return android::OK; +} + +status_t WVDrmPlugin::provideLicenseResponse( + const Vector& sessionId, + const Vector& response) { + CdmSessionId cdmSessionId(sessionId.begin(), sessionId.end()); + CdmKeyResponse cdmResponse(response.begin(), response.end()); + + CdmResponseType res = mCDM->AddKey(cdmSessionId, cdmResponse); + + if (res == wvcdm::KEY_ADDED || res == wvcdm::NO_ERROR) { + return android::OK; + } else { + return android::UNKNOWN_ERROR; + } +} + +status_t WVDrmPlugin::removeLicense(const Vector& sessionId) { + CdmSessionId cdmSessionId(sessionId.begin(), sessionId.end()); + + CdmResponseType res = mCDM->CancelKeyRequest(cdmSessionId); + + if (res == wvcdm::NO_ERROR) { + return android::OK; + } else { + return android::UNKNOWN_ERROR; + } +} + +status_t WVDrmPlugin::queryLicenseStatus( + const Vector& sessionId, + KeyedVector& infoMap) const { + CdmSessionId cdmSessionId(sessionId.begin(), sessionId.end()); + CdmNameValueMap cdmLicenseInfo; + + CdmResponseType res = mCDM->QueryKeyStatus(cdmSessionId, &cdmLicenseInfo); + + if (res != wvcdm::NO_ERROR) { + return android::UNKNOWN_ERROR; + } + + infoMap.clear(); + for (CdmNameValueMap::const_iterator iter = cdmLicenseInfo.begin(); + iter != cdmLicenseInfo.end(); + ++iter) { + const string& cdmKey = iter->first; + const string& cdmValue = iter->second; + + String8 key(cdmKey.data(), cdmKey.size()); + String8 value(cdmValue.data(), cdmValue.size()); + + infoMap.add(key, value); + } + + return android::OK; +} + +status_t WVDrmPlugin::getProvisionRequest(Vector& request, + String8& defaultUrl) { + CdmProvisioningRequest cdmProvisionRequest; + string cdmDefaultUrl; + + CdmResponseType res = mCDM->GetProvisioningRequest(&cdmProvisionRequest, + &cdmDefaultUrl); + + if (res != wvcdm::NO_ERROR) { + return android::UNKNOWN_ERROR; + } + + request.clear(); + request.appendArray(reinterpret_cast( + cdmProvisionRequest.data()), + cdmProvisionRequest.size()); + + defaultUrl.clear(); + defaultUrl.setTo(cdmDefaultUrl.data(), cdmDefaultUrl.size()); + + return android::OK; +} + +status_t WVDrmPlugin::provideProvisionResponse( + const Vector& response) { + CdmProvisioningResponse cdmResponse(response.begin(), response.end()); + + CdmResponseType res = mCDM->HandleProvisioningResponse(cdmResponse); + + if (res == wvcdm::NO_ERROR) { + return android::OK; + } else { + return android::UNKNOWN_ERROR; + } +} + +status_t WVDrmPlugin::getSecureStops(List >& secureStops) { + CdmSecureStops cdmSecureStops; + + CdmResponseType res = mCDM->GetSecureStops(&cdmSecureStops); + + if (res != wvcdm::NO_ERROR) { + return android::UNKNOWN_ERROR; + } + + secureStops.clear(); + for (CdmSecureStops::const_iterator iter = cdmSecureStops.begin(); + iter != cdmSecureStops.end(); + ++iter) { + const string& cdmStop = *iter; + + Vector stop; + stop.appendArray(reinterpret_cast(cdmStop.data()), + cdmStop.size()); + + secureStops.push_back(stop); + } + + return android::OK; +} + +status_t WVDrmPlugin::releaseSecureStops(const Vector& ssRelease) { + CdmSecureStopReleaseMessage cdmMessage(ssRelease.begin(), ssRelease.end()); + + CdmResponseType res = mCDM->ReleaseSecureStops(cdmMessage); + + if (res == wvcdm::NO_ERROR) { + return android::OK; + } else { + return android::UNKNOWN_ERROR; + } +} + +status_t WVDrmPlugin::getPropertyString(const String8& name, + String8& value) const { + // TODO: Implement this function once the CDM query API is finalized. + return -EPERM; +} + +status_t WVDrmPlugin::getPropertyByteArray(const String8& name, + Vector& value) const { + // TODO: Implement this function once the CDM query API is finalized. + return -EPERM; +} + +status_t WVDrmPlugin::setPropertyString(const String8& name, + const String8& value) { + // TODO: Implement this function once the CDM query API is finalized. + return -EPERM; +} + +status_t WVDrmPlugin::setPropertyByteArray(const String8& name, + const Vector& value) { + // TODO: Implement this function once the CDM query API is finalized. + return -EPERM; +} + +// TODO: Hook up to event listener methods on CDM once Android API for +// eventing is finalized. +} // namespace wvdrm diff --git a/libwvdrmengine/mediadrm/test/Android.mk b/libwvdrmengine/mediadrm/test/Android.mk new file mode 100644 index 00000000..f08508bf --- /dev/null +++ b/libwvdrmengine/mediadrm/test/Android.mk @@ -0,0 +1,54 @@ +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + WVDrmPlugin_test.cpp \ + +LOCAL_C_INCLUDES := \ + bionic \ + external/gtest/include \ + external/stlport/stlport \ + frameworks/av/include \ + frameworks/native/include \ + vendor/widevine/libwvdrmengine/cdm/core/include \ + vendor/widevine/libwvdrmengine/cdm/include \ + vendor/widevine/libwvdrmengine/mediadrm/include \ + vendor/widevine/libwvdrmengine/mediadrm/test \ + vendor/widevine/libwvdrmengine/test/gmock/include \ + +LOCAL_STATIC_LIBRARIES := \ + libcdm \ + libgmock \ + libgmock_main \ + libgtest \ + libprotobuf-cpp-2.3.0-lite \ + libwvdrmdrmplugin \ + +LOCAL_SHARED_LIBRARIES := \ + liblog \ + liboemcrypto \ + libstlport \ + libutils \ + +# CDM's protobuffers are not part of the library +PROTO_SRC_DIR := $(proto_generated_cc_sources_dir)/$(LOCAL_PATH)/core/src + +LOCAL_SRC_FILES += \ + $(PROTO_SRC_DIR)/license_protocol.pb.cc \ + +LOCAL_C_INCLUDES += \ + $(proto_generated_cc_sources_dir)/$(LOCAL_PATH)/core/src \ + external/protobuf/src \ + +LOCAL_ADDITIONAL_DEPENDENCIES += $(proto_generated_headers) + +LOCAL_WHOLE_STATIC_LIBRARIES := \ + license_protocol_protos \ + +# End protobuf section + +LOCAL_MODULE := libwvdrmdrmplugin_test + +LOCAL_MODULE_TAGS := tests + +include $(BUILD_EXECUTABLE) diff --git a/libwvdrmengine/mediadrm/test/MockCDM.h b/libwvdrmengine/mediadrm/test/MockCDM.h new file mode 100644 index 00000000..8ffd979a --- /dev/null +++ b/libwvdrmengine/mediadrm/test/MockCDM.h @@ -0,0 +1,53 @@ +// +// Copyright 2013 Google Inc. All Rights Reserved. +// + +#ifndef WV_DRM_PLUGIN_MOCK_CDM_H_ +#define WV_DRM_PLUGIN_MOCK_CDM_H_ + +#include +#include + +#include "gmock/gmock.h" +#include "wv_cdm_types.h" +#include "wv_content_decryption_module.h" + + +namespace wvcdm { + +class MockCDM : public WvContentDecryptionModule { + public: + MOCK_METHOD2(OpenSession, CdmResponseType(const CdmKeySystem&, + CdmSessionId*)); + + MOCK_METHOD1(CloseSession, CdmResponseType(CdmSessionId&)); + + MOCK_METHOD5(GenerateKeyRequest, CdmResponseType(const CdmSessionId&, + const CdmInitData&, + const CdmLicenseType, + CdmNameValueMap&, + CdmKeyMessage*)); + + MOCK_METHOD2(AddKey, CdmResponseType(const CdmSessionId&, + const CdmKeyResponse&)); + + MOCK_METHOD1(CancelKeyRequest, CdmResponseType(const CdmSessionId&)); + + MOCK_METHOD2(QueryKeyStatus, CdmResponseType(const CdmSessionId&, + CdmNameValueMap*)); + + MOCK_METHOD2(GetProvisioningRequest, CdmResponseType(CdmProvisioningRequest*, + std::string*)); + + MOCK_METHOD1(HandleProvisioningResponse, + CdmResponseType(CdmProvisioningResponse&)); + + MOCK_METHOD1(GetSecureStops, CdmResponseType(CdmSecureStops*)); + + MOCK_METHOD1(ReleaseSecureStops, + CdmResponseType(const CdmSecureStopReleaseMessage&)); +}; + +} // namespace wvcdm + +#endif // WV_DRM_PLUGIN_MOCK_CDM_H_ diff --git a/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp b/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp new file mode 100644 index 00000000..9a944f1c --- /dev/null +++ b/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp @@ -0,0 +1,372 @@ +// +// Copyright 2013 Google Inc. All Rights Reserved. +// + +#include +#include + +#include "gtest/gtest.h" +#include "media/stagefright/foundation/ABase.h" +#include "media/stagefright/foundation/AString.h" +#include "MockCDM.h" +#include "wv_cdm_constants.h" +#include "WVDrmPlugin.h" + +using namespace android; +using namespace std; +using namespace testing; +using namespace wvcdm; +using namespace wvdrm; + +class WVDrmPluginTest : public Test { + protected: + static const uint32_t kSessionIdSize = 16; + Vector sessionId; + CdmSessionId cdmSessionId; + + virtual void SetUp() { + uint8_t sessionIdRaw[kSessionIdSize]; + FILE* fp = fopen("/dev/urandom", "r"); + fread(sessionIdRaw, sizeof(uint8_t), kSessionIdSize, fp); + fclose(fp); + + sessionId.appendArray(sessionIdRaw, kSessionIdSize); + cdmSessionId.assign(sessionId.begin(), sessionId.end()); + + // Set default CdmResponseType value for gMock + DefaultValue::Set(wvcdm::NO_ERROR); + } +}; + +TEST_F(WVDrmPluginTest, OpensSessions) { + MockCDM cdm; + WVDrmPlugin plugin(&cdm); + + uint8_t sessionIdRaw[kSessionIdSize]; + FILE* fp = fopen("/dev/urandom", "r"); + fread(sessionIdRaw, sizeof(uint8_t), kSessionIdSize, fp); + fclose(fp); + + CdmSessionId cdmSessionId(sessionIdRaw, sessionIdRaw + kSessionIdSize); + + EXPECT_CALL(cdm, OpenSession(StrEq("com.widevine"), _)) + .WillOnce(DoAll(SetArgPointee<1>(cdmSessionId), + Return(wvcdm::NO_ERROR))); + + status_t res = plugin.openSession(sessionId); + + ASSERT_EQ(OK, res); + EXPECT_THAT(sessionId, ElementsAreArray(sessionIdRaw, kSessionIdSize)); +} + +TEST_F(WVDrmPluginTest, ClosesSessions) { + MockCDM cdm; + WVDrmPlugin plugin(&cdm); + + EXPECT_CALL(cdm, CloseSession(cdmSessionId)) + .Times(1); + + status_t res = plugin.closeSession(sessionId); + + ASSERT_EQ(OK, res); +} + +TEST_F(WVDrmPluginTest, GeneratesKeyRequests) { + MockCDM cdm; + WVDrmPlugin plugin(&cdm); + + static const uint32_t kInitDataSize = 128; + uint8_t initDataRaw[kInitDataSize]; + static const uint32_t kRequestSize = 256; + uint8_t requestRaw[kRequestSize]; + FILE* fp = fopen("/dev/urandom", "r"); + fread(initDataRaw, sizeof(uint8_t), kInitDataSize, fp); + fread(requestRaw, sizeof(uint8_t), kRequestSize, fp); + fclose(fp); + + Vector initData; + initData.appendArray(initDataRaw, kInitDataSize); + + CdmKeyMessage cdmRequest(requestRaw, requestRaw + kRequestSize); + Vector request; + + KeyedVector parameters; + CdmNameValueMap cdmParameters; + + parameters.add(String8("paddingScheme"), String8("PKCS7")); + cdmParameters["paddingScheme"] = "PKCS7"; + parameters.add(String8("favoriteParticle"), String8("tetraquark")); + cdmParameters["favoriteParticle"] = "tetraquark"; + parameters.add(String8("answer"), String8("42")); + cdmParameters["answer"] = "42"; + + String8 defaultUrl; + + { + InSequence calls; + + EXPECT_CALL(cdm, GenerateKeyRequest(cdmSessionId, + ElementsAreArray(initDataRaw, + kInitDataSize), + kLicenseTypeOffline, cdmParameters, _)) + .WillOnce(DoAll(SetArgPointee<4>(cdmRequest), + Return(wvcdm::NO_ERROR))); + + EXPECT_CALL(cdm, GenerateKeyRequest(cdmSessionId, + ElementsAreArray(initDataRaw, + kInitDataSize), + kLicenseTypeStreaming, cdmParameters, + _)) + .WillOnce(DoAll(SetArgPointee<4>(cdmRequest), + Return(wvcdm::NO_ERROR))); + } + + status_t res = plugin.getLicenseRequest(sessionId, initData, + String8("video/h264"), + DrmPlugin::kLicenseType_Offline, + parameters, request, defaultUrl); + + ASSERT_EQ(OK, res); + EXPECT_THAT(request, ElementsAreArray(requestRaw, kRequestSize)); + EXPECT_TRUE(defaultUrl.isEmpty()); + + res = plugin.getLicenseRequest(sessionId, initData, String8("video/h264"), + DrmPlugin::kLicenseType_Streaming, parameters, + request, defaultUrl); + + ASSERT_EQ(OK, res); + EXPECT_THAT(request, ElementsAreArray(requestRaw, kRequestSize)); + EXPECT_TRUE(defaultUrl.isEmpty()); +} + +TEST_F(WVDrmPluginTest, AddsKeys) { + MockCDM cdm; + WVDrmPlugin plugin(&cdm); + + static const uint32_t kResponseSize = 256; + uint8_t responseRaw[kResponseSize]; + FILE* fp = fopen("/dev/urandom", "r"); + fread(responseRaw, sizeof(uint8_t), kResponseSize, fp); + fclose(fp); + + Vector response; + response.appendArray(responseRaw, kResponseSize); + + EXPECT_CALL(cdm, AddKey(cdmSessionId, ElementsAreArray(responseRaw, + kResponseSize))) + .Times(1); + + status_t res = plugin.provideLicenseResponse(sessionId, response); + + ASSERT_EQ(OK, res); +} + +TEST_F(WVDrmPluginTest, CancelsKeyRequests) { + MockCDM cdm; + WVDrmPlugin plugin(&cdm); + + EXPECT_CALL(cdm, CancelKeyRequest(cdmSessionId)) + .Times(1); + + status_t res = plugin.removeLicense(sessionId); + + ASSERT_EQ(OK, res); +} + +TEST_F(WVDrmPluginTest, QueriesKeyStatus) { + MockCDM cdm; + WVDrmPlugin plugin(&cdm); + + KeyedVector expectedLicenseStatus; + CdmNameValueMap cdmLicenseStatus; + + expectedLicenseStatus.add(String8("areTheKeysAllRight"), String8("yes")); + cdmLicenseStatus["areTheKeysAllRight"] = "yes"; + expectedLicenseStatus.add(String8("isGMockAwesome"), String8("ohhhhhhYeah")); + cdmLicenseStatus["isGMockAwesome"] = "ohhhhhhYeah"; + expectedLicenseStatus.add(String8("answer"), String8("42")); + cdmLicenseStatus["answer"] = "42"; + + EXPECT_CALL(cdm, QueryKeyStatus(cdmSessionId, _)) + .WillOnce(DoAll(SetArgPointee<1>(cdmLicenseStatus), + Return(wvcdm::NO_ERROR))); + + KeyedVector licenseStatus; + + status_t res = plugin.queryLicenseStatus(sessionId, licenseStatus); + + ASSERT_EQ(OK, res); + + ASSERT_EQ(expectedLicenseStatus.size(), licenseStatus.size()); + for (size_t i = 0; i < expectedLicenseStatus.size(); ++i) { + const String8& key = expectedLicenseStatus.keyAt(i); + EXPECT_NE(android::NAME_NOT_FOUND, licenseStatus.indexOfKey(key)); + EXPECT_EQ(expectedLicenseStatus.valueFor(key), licenseStatus.valueFor(key)); + } +} + +TEST_F(WVDrmPluginTest, GetsProvisioningRequests) { + MockCDM cdm; + WVDrmPlugin plugin(&cdm); + + static const uint32_t kRequestSize = 256; + uint8_t requestRaw[kRequestSize]; + FILE* fp = fopen("/dev/urandom", "r"); + fread(requestRaw, sizeof(uint8_t), kRequestSize, fp); + fclose(fp); + + CdmProvisioningRequest cdmRequest(requestRaw, requestRaw + kRequestSize); + + static const char* kDefaultUrl = "http://google.com/"; + + EXPECT_CALL(cdm, GetProvisioningRequest(_, _)) + .WillOnce(DoAll(SetArgPointee<0>(cdmRequest), + SetArgPointee<1>(kDefaultUrl), + Return(wvcdm::NO_ERROR))); + + Vector request; + String8 defaultUrl; + + status_t res = plugin.getProvisionRequest(request, defaultUrl); + + ASSERT_EQ(OK, res); + EXPECT_THAT(request, ElementsAreArray(requestRaw, kRequestSize)); + EXPECT_EQ(String8(kDefaultUrl), defaultUrl); +} + +TEST_F(WVDrmPluginTest, HandlesProvisioningResponses) { + MockCDM cdm; + WVDrmPlugin plugin(&cdm); + + static const uint32_t kResponseSize = 512; + uint8_t responseRaw[kResponseSize]; + FILE* fp = fopen("/dev/urandom", "r"); + fread(responseRaw, sizeof(uint8_t), kResponseSize, fp); + fclose(fp); + + Vector response; + response.appendArray(responseRaw, kResponseSize); + + EXPECT_CALL(cdm, HandleProvisioningResponse(ElementsAreArray(responseRaw, + kResponseSize))) + .Times(1); + + status_t res = plugin.provideProvisionResponse(response); + + ASSERT_EQ(OK, res); +} + +TEST_F(WVDrmPluginTest, GetsSecureStops) { + MockCDM cdm; + WVDrmPlugin plugin(&cdm); + + static const uint32_t kStopSize = 53; + static const uint32_t kStopCount = 7; + uint8_t stopsRaw[kStopCount][kStopSize]; + FILE* fp = fopen("/dev/urandom", "r"); + for (uint32_t i = 0; i < kStopCount; ++i) { + fread(stopsRaw[i], sizeof(uint8_t), kStopSize, fp); + } + fclose(fp); + + CdmSecureStops cdmStops; + for (uint32_t i = 0; i < kStopCount; ++i) { + cdmStops.push_back(string(stopsRaw[i], stopsRaw[i] + kStopSize)); + } + + EXPECT_CALL(cdm, GetSecureStops(_)) + .WillOnce(DoAll(SetArgPointee<0>(cdmStops), + Return(wvcdm::NO_ERROR))); + + List > stops; + + status_t res = plugin.getSecureStops(stops); + + ASSERT_EQ(OK, res); + + List >::iterator iter = stops.begin(); + uint32_t rawIter = 0; + while (rawIter < kStopCount && iter != stops.end()) { + EXPECT_THAT(*iter, ElementsAreArray(stopsRaw[rawIter], kStopSize)); + + ++iter; + ++rawIter; + } + // Assert that both lists are the same length + EXPECT_EQ(kStopCount, rawIter); + EXPECT_EQ(stops.end(), iter); +} + +TEST_F(WVDrmPluginTest, ReleasesSecureStops) { + MockCDM cdm; + WVDrmPlugin plugin(&cdm); + + static const uint32_t kMessageSize = 128; + uint8_t messageRaw[kMessageSize]; + FILE* fp = fopen("/dev/urandom", "r"); + fread(messageRaw, sizeof(uint8_t), kMessageSize, fp); + fclose(fp); + + Vector message; + message.appendArray(messageRaw, kMessageSize); + + EXPECT_CALL(cdm, ReleaseSecureStops(ElementsAreArray(messageRaw, + kMessageSize))) + .Times(1); + + status_t res = plugin.releaseSecureStops(message); + + ASSERT_EQ(OK, res); +} + +TEST_F(WVDrmPluginTest, DoesNotGetStringProperties) { + MockCDM cdm; + WVDrmPlugin plugin(&cdm); + + String8 result; + + status_t res = plugin.getPropertyString(String8("property"), result); + + ASSERT_NE(OK, res); + EXPECT_TRUE(result.isEmpty()); +} + +TEST_F(WVDrmPluginTest, DoesNotGetByteProperties) { + MockCDM cdm; + WVDrmPlugin plugin(&cdm); + + Vector result; + + status_t res = plugin.getPropertyByteArray(String8("property"), result); + + ASSERT_NE(OK, res); + EXPECT_TRUE(result.isEmpty()); +} + +TEST_F(WVDrmPluginTest, DoesNotSetStringProperties) { + MockCDM cdm; + WVDrmPlugin plugin(&cdm); + + status_t res = plugin.setPropertyString(String8("property"), + String8("ignored")); + + ASSERT_NE(OK, res); +} + +TEST_F(WVDrmPluginTest, DoesNotSetByteProperties) { + MockCDM cdm; + WVDrmPlugin plugin(&cdm); + + static const uint32_t kValueSize = 32; + uint8_t valueRaw[kValueSize]; + FILE* fp = fopen("/dev/urandom", "r"); + fread(valueRaw, sizeof(uint8_t), kValueSize, fp); + fclose(fp); + + Vector value; + value.appendArray(valueRaw, kValueSize); + + status_t res = plugin.setPropertyByteArray(String8("property"), value); + + ASSERT_NE(OK, res); +} diff --git a/libwvdrmengine/oemcrypto/include/L3CryptoCENC.h b/libwvdrmengine/oemcrypto/include/L3CryptoCENC.h new file mode 100644 index 00000000..d9f9a9f4 --- /dev/null +++ b/libwvdrmengine/oemcrypto/include/L3CryptoCENC.h @@ -0,0 +1,109 @@ +/********************************************************************* + * L3CryptoCENC.h + * + * (c) Copyright 2013 Google, Inc. + * + * Reference APIs needed to support Widevine's crypto algorithms. + *********************************************************************/ + +#ifndef L3CRYPTO_CENC_H_ +#define L3CRYPTO_CENC_H_ + +#include "OEMCryptoCENC.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define L3CRYPTO_VERSION "5.0" +static const char l3c_version[] = L3CRYPTO_VERSION; + +typedef uint32_t OEMCrypto_SESSION; + + +#define L3Crypto_Initialize _l3cc01 +#define L3Crypto_Terminate _l3cc02 +#define L3Crypto_InstallKeybox _l3cc03 +#define L3Crypto_GetKeyData _l3cc04 +#define L3Crypto_IsKeyboxValid _l3cc05 +#define L3Crypto_GetRandom _l3cc06 +#define L3Crypto_GetDeviceID _l3cc07 +#define L3Crypto_WrapKeybox _l3cc08 +#define L3Crypto_OpenSession _l3cc09 +#define L3Crypto_CloseSession _l3cc10 +#define L3Crypto_DecryptCTR _l3cc11 +#define L3Crypto_GenerateDerivedKeys _l3cc12 +#define L3Crypto_GenerateSignature _l3cc13 +#define L3Crypto_GenerateNonce _l3cc14 +#define L3Crypto_LoadKeys _l3cc15 +#define L3Crypto_RefreshKeys _l3cc16 +#define L3Crypto_SelectKey _l3cc17 + +OEMCryptoResult L3Crypto_Initialize(void); +OEMCryptoResult L3Crypto_Terminate(void); +OEMCryptoResult L3Crypto_OpenSession(OEMCrypto_SESSION *session); +OEMCryptoResult L3Crypto_CloseSession(OEMCrypto_SESSION session); +OEMCryptoResult L3Crypto_GenerateDerivedKeys( + OEMCrypto_SESSION session, + const uint8_t *mac_key_context, + uint32_t mac_key_context_length, + const uint8_t *enc_key_context, + uint32_t enc_key_context_length); +OEMCryptoResult L3Crypto_GenerateNonce( + OEMCrypto_SESSION session, + uint32_t* nonce); +OEMCryptoResult L3Crypto_GenerateSignature( + OEMCrypto_SESSION session, + const uint8_t* message, + size_t message_length, + uint8_t* signature, + size_t* signature_length); +OEMCryptoResult L3Crypto_LoadKeys(OEMCrypto_SESSION session, + const uint8_t* message, + size_t message_length, + const uint8_t* signature, + size_t signature_length, + const uint8_t* enc_mac_key_iv, + const uint8_t* enc_mac_key, + size_t num_keys, + const OEMCrypto_KeyObject* key_array); +OEMCryptoResult +L3Crypto_RefreshKeys(OEMCrypto_SESSION session, + const uint8_t* message, + size_t message_length, + const uint8_t* signature, + size_t signature_length, + size_t num_keys, + const OEMCrypto_KeyRefreshObject* key_array); +OEMCryptoResult L3Crypto_SelectKey(const OEMCrypto_SESSION session, + const uint8_t* key_id, + size_t key_id_length); +OEMCryptoResult +L3Crypto_DecryptCTR(OEMCrypto_SESSION session, + const uint8_t *data_addr, + size_t data_length, + bool is_encrypted, + const uint8_t *iv, + size_t offset, + const OEMCrypto_DestBufferDesc* out_buffer); +OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t *keybox, + size_t keyBoxLength); +OEMCryptoResult L3Crypto_IsKeyboxValid(void); +OEMCryptoResult L3Crypto_GetDeviceID(uint8_t* deviceID, + size_t *idLength); +OEMCryptoResult L3Crypto_GetKeyData(uint8_t* keyData, + size_t *keyDataLength); +OEMCryptoResult L3Crypto_GetRandom(uint8_t* randomData, + size_t dataLength); +OEMCryptoResult L3Crypto_WrapKeybox(const uint8_t *keybox, + size_t keyBoxLength, + uint8_t *wrappedKeybox, + size_t *wrappedKeyBoxLength, + const uint8_t *transportKey, + size_t transportKeyLength); + +#ifdef __cplusplus +} +#endif + +#endif // L3CRYPTO_CENC_H_ diff --git a/libwvdrmengine/oemcrypto/include/OEMCryptoCENC.h b/libwvdrmengine/oemcrypto/include/OEMCryptoCENC.h new file mode 100644 index 00000000..d3043916 --- /dev/null +++ b/libwvdrmengine/oemcrypto/include/OEMCryptoCENC.h @@ -0,0 +1,982 @@ +/********************************************************************* + * OEMCryptoCENC.h + * + * (c) Copyright 2013 Google, Inc. + * + * Reference APIs needed to support Widevine's crypto algorithms. + *********************************************************************/ + +#ifndef OEMCRYPTO_CENC_H_ +#define OEMCRYPTO_CENC_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define OEMCRYPTO_VERSION "5.0" +static const char oec_version[] = OEMCRYPTO_VERSION; + +typedef uint32_t OEMCrypto_SESSION; + +typedef enum OEMCryptoResult { + OEMCrypto_SUCCESS = 0, + OEMCrypto_ERROR_INIT_FAILED = 1, + OEMCrypto_ERROR_TERMINATE_FAILED = 2, + OEMCrypto_ERROR_OPEN_FAILURE = 3, + OEMCrypto_ERROR_CLOSE_FAILURE = 4, + OEMCrypto_ERROR_ENTER_SECURE_PLAYBACK_FAILED = 5, + OEMCrypto_ERROR_EXIT_SECURE_PLAYBACK_FAILED = 6, + OEMCrypto_ERROR_SHORT_BUFFER = 7, + OEMCrypto_ERROR_NO_DEVICE_KEY = 8, + OEMCrypto_ERROR_NO_ASSET_KEY = 9, + OEMCrypto_ERROR_KEYBOX_INVALID = 10, + OEMCrypto_ERROR_NO_KEYDATA = 11, + OEMCrypto_ERROR_NO_CW = 12, + OEMCrypto_ERROR_DECRYPT_FAILED = 13, + OEMCrypto_ERROR_WRITE_KEYBOX = 14, + OEMCrypto_ERROR_WRAP_KEYBOX = 15, + OEMCrypto_ERROR_BAD_MAGIC = 16, + OEMCrypto_ERROR_BAD_CRC = 17, + OEMCrypto_ERROR_NO_DEVICEID = 18, + OEMCrypto_ERROR_RNG_FAILED = 19, + OEMCrypto_ERROR_RNG_NOT_SUPPORTED = 20, + OEMCrypto_ERROR_SETUP = 21, + OEMCrypto_ERROR_OPEN_SESSION_FAILED = 22, + OEMCrypto_ERROR_CLOSE_SESSION_FAILED = 23, + OEMCrypto_ERROR_INVALID_SESSION = 24, + OEMCrypto_ERROR_NOT_IMPLEMENTED = 25, + OEMCrypto_ERROR_NO_CONTENT_KEY = 26, + OEMCrypto_ERROR_CONTROL_INVALID = 27, + OEMCrypto_ERROR_UNKNOWN_FAILURE = 28, + OEMCrypto_ERROR_INVALID_CONTEXT = 29, + OEMCrypto_ERROR_SIGNATURE_FAILURE = 30, + OEMCrypto_ERROR_TOO_MANY_SESSIONS = 31, + OEMCrypto_ERROR_INVALID_NONCE = 32, + OEMCrypto_ERROR_TOO_MANY_KEYS = 33, + OEMCrypto_ERROR_DEVICE_NOT_RSA_PROVISIONED = 34, + OEMCrypto_ERROR_INVALID_RSA_KEY = 35, +} OEMCryptoResult; + +/* + * OEMCrypto_DestBufferDesc + * Describes the type and access information for the memory to receive + * decrypted data. + * + * The OEMCrypto API supports a range of client device architectures. + * Different architectures have different methods for acquiring and securing + * buffers that will hold portions of the audio or video stream after + * decryption. Three basic strategies are recognized for handling decrypted + * stream data: + * 1. Return the decrypted data in the clear into normal user memory + * (ClearBuffer). The caller uses normal memory allocation methods to + * acquire a buffer, and supplies the memory address of the buffer in the + * descriptor. + * 2. Place the decrypted data into protected memory (SecureBuffer). The + * caller uses a platform-specific method to acquire the protected buffer + * and a user-memory handle that references it. The handle is supplied + * to the decrypt call in the descriptor. + * 3. Place the decrypted data directly into the audio or video decoder fifo + * (Direct). The caller will use platform-specific methods to initialize + * the fifo and the decoders. The decrypted stream data is not accessible + * to the caller. + * + * Specific fields are as follows: + * + * (type == OEMCrypto_BufferType_Clear) + * address - Address of start of user memory buffer. + * max_length - Size of user memory buffer. + * (type == OEMCrypto_BufferType_Secure) + * buffer - handle to a platform-specific secure buffer. + * max_length - Size of platform-specific secure buffer. + * (type == OEMCrypto_BufferType_Direct) + * is_video - If true, decrypted bytes are routed to the video + * decoder. If false, decrypted bytes are routed to the + * audio decoder. + */ +typedef enum OEMCryptoBufferType { + OEMCrypto_BufferType_Clear, + OEMCrypto_BufferType_Secure, + OEMCrypto_BufferType_Direct +} OEMCrytoBufferType; + +typedef struct { + OEMCryptoBufferType type; + union { + struct { // type == OEMCrypto_BufferType_Clear + uint8_t* address; + size_t max_length; + } clear; + struct { // type == OEMCrypto_BufferType_Secure + void* handle; + size_t max_length; + } secure; + struct { // type == OEMCrypto_BufferType_Direct + bool is_video; + } direct; + } buffer; +} OEMCrypto_DestBufferDesc; + +/* + * OEMCrypto_KeyObject + * Points to the relevant fields for a content key. The fields are extracted + * from the License Response message offered to OEMCrypto_LoadKeys(). Each + * field points to one of the components of the key. Key data, key control, + * and both IV fields are 128 bits (16 bytes): + * key_id - the unique id of this key. + * key_id_length - the size of key_id. + * key_data_iv - the IV for performing AES-128-CBC decryption of the + * key_data field. + * key_data - the key data. It is encrypted (AES-128-CBC) with the + * session's derived encrypt key and the key_data_iv. + * key_control_iv - the IV for performing AES-128-CBC decryption of the + * key_control field. + * key_control - the key control block. It is encrypted (AES-128-CBC) with + * the content key from the key_data field. + * + * The memory for the OEMCrypto_KeyObject fields is allocated and freed + * by the caller of OEMCrypto_LoadKeys(). + */ +typedef struct { + const uint8_t* key_id; + size_t key_id_length; + const uint8_t* key_data_iv; + const uint8_t* key_data; + const uint8_t* key_control_iv; + const uint8_t* key_control; +} OEMCrypto_KeyObject; + +/* + * OEMCrypto_KeyRefreshObject + * Points to the relevant fields for renewing a content key. The fields are + * extracted from the License Renewal Response message offered to + * OEMCrypto_RefreshKeys(). Each field points to one of the components of + * the key. All fields are 128 bits (16 bytes): + * key_id - the unique id of this key. + * key_control_iv - the IV for performing AES-128-CBC decryption of the + * key_control field. + * key_control - the key control block. It is encrypted (AES-128-CBC) with + * the content key from the key_data field. + * + * The key_data is unchanged from the original OEMCrypto_LoadKeys() call. Some + * Key Control Block fields, especially those related to key lifetime, may + * change. + * + * The memory for the OEMCrypto_KeyRefreshObject fields is allocated and freed + * by the caller of OEMCrypto_RefreshKeys(). + */ +typedef struct { + const uint8_t* key_id; + size_t key_id_length; + const uint8_t* key_control_iv; + const uint8_t* key_control; +} OEMCrypto_KeyRefreshObject; + +#define OEMCrypto_Initialize _oecc01 +#define OEMCrypto_Terminate _oecc02 +#define OEMCrypto_InstallKeybox _oecc03 +#define OEMCrypto_GetKeyData _oecc04 +#define OEMCrypto_IsKeyboxValid _oecc05 +#define OEMCrypto_GetRandom _oecc06 +#define OEMCrypto_GetDeviceID _oecc07 +#define OEMCrypto_WrapKeybox _oecc08 +#define OEMCrypto_OpenSession _oecc09 +#define OEMCrypto_CloseSession _oecc10 +#define OEMCrypto_DecryptCTR _oecc11 +#define OEMCrypto_GenerateDerivedKeys _oecc12 +#define OEMCrypto_GenerateSignature _oecc13 +#define OEMCrypto_GenerateNonce _oecc14 +#define OEMCrypto_LoadKeys _oecc15 +#define OEMCrypto_RefreshKeys _oecc16 +#define OEMCrypto_SelectKey _oecc17 +#define OEMCrypto_RewrapDeviceRSAKey _oecc18 +#define OEMCrypto_LoadDeviceRSAKey _oecc19 +#define OEMCrypto_GenerateRSASignature _oecc20 +#define OEMCrypto_DeriveKeysFromSessionKey _oecc21 + +/* + * OEMCrypto_Initialize + * + * Description: + * Initialize the crypto firmware/hardware. + * + * Parameters: + * N/A + * + * Threading: + * No other function calls will be made while this function is running. This + * function will not be called again before OEMCrypto_Terminate. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_INIT_FAILED failed to initialize crypto hardware + */ +OEMCryptoResult OEMCrypto_Initialize(void); + +/* + * OEMCrypto_Terminate + * + * Description: + * The API closes the crypto operation and releases all resources used. + * + * Parameters: + * N/A + * + * Threading: + * No other OEMCrypto calls are made while this function is running. After + * this function is called, no other OEMCrypto calls will be made until another + * call to OEMCrypto_Initialize is made. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_TERMINATE_FAILED failed to de-initialize crypto hardware + */ +OEMCryptoResult OEMCrypto_Terminate(void); + +/* + * OEMCrypto_OpenSession + * + * Description: + * The API provides for session based crypto initialization for AES CTR mode. + * + * Parameters: + * session (out) - pointer to crypto session identifier. + * + * Threading: + * No other Open/Close session calls will be made while this function is + * running. Functions on existing sessions may be called while this function + * is active. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_TOO_MANY_SESSIONS failed because too many sessions are open + * OEMCrypto_ERROR_OPEN_SESSION_FAILED failed to initialize the crypto session + */ +OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION *session); + +/* + * OEMCrypto_CloseSession + * + * Description: + * The API provides for session based crypto termination for AES CTR mode. + * + * Parameters: + * session (in) - crypto session identifier. + * + * Threading: + * No other Open/Close session calls will be made while this function is + * running. Functions on existing sessions may be called while this function + * is active. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_INVALID_SESSION no open session with that id. + * OEMCrypto_ERROR_CLOSE_SESSION_FAILED failed to terminate the crypto session + */ +OEMCryptoResult OEMCrypto_CloseSession(OEMCrypto_SESSION session); + +/* + * OEMCrypto_GenerateDerivedKeys + * + * Description: + * Generates a pair of secondary keys, mac_key and encrypt_key, for handling + * signing and content key decryption under the license server protocol + * for AES CTR mode. + * + * Refer to document "OEMCrypto Changes for V2 License Protocol" for details. + * This function computes the AES-128-CMAC of the enc_key_context and stores + * it in secure memory as the encrypt_key. + * It then computes two cycles of AES-128-CMAC of the mac_key_context and + * stores it in the mac_key. These two keys will be stored until the next + * call to LoadKeys. + * + * Parameters: + * session (in) - crypto session identifier. + * mac_key_context (in) - pointer to memory containing context data for + * computing the HMAC generation key. + * mac_key_context_length (in) - length of the HMAC key context data. + * enc_key_context (in) - pointer to memory containing context data for + * computing the encryption key. + * enc_key_context_length (in) - length of the encryption key context data. + * + * Results: + * mac_key: the 256 bit mac key is generated and stored in secure memory. + * enc_key: the 128 bit encryption key is generated and stored in secure memory. + * + * Threading: + * This function may be called simultaneously with functions on other sessions, + * but not with other functions on this session. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_NO_DEVICE_KEY + * OEMCrypto_ERROR_INVALID_SESSION + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * OEMCrypto_ERROR_INVALID_CONTEXT + */ +OEMCryptoResult OEMCrypto_GenerateDerivedKeys( + OEMCrypto_SESSION session, + const uint8_t *mac_key_context, + uint32_t mac_key_context_length, + const uint8_t *enc_key_context, + uint32_t enc_key_context_length); + +/* + * OEMCrypto_GenerateNonce + * + * Description: + * Generates a 32-bit nonce to detect possible replay attack on the key + * control block. The nonce is stored in secure memory and will be used + * for the next call to LoadKeys. + * + * Refer to documents "OEMCrypto Changes for V2 License Protocol" and "Key + * Control Block Definition" for details. + * + * Parameters: + * session (in) - crypto session identifier. + * nonce (out) - pointer to memory to received the computed nonce. + * + * Results: + * nonce: the nonce is also stored in secure memory. + * + * Threading: + * This function may be called simultaneously with functions on other sessions, + * but not with other functions on this session. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_NO_DEVICE_KEY + * OEMCrypto_ERROR_INVALID_SESSION + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * OEMCrypto_ERROR_INVALID_CONTEXT + */ +OEMCryptoResult OEMCrypto_GenerateNonce( + OEMCrypto_SESSION session, + uint32_t* nonce); + +/* + * OEMCrypto_GenerateSignature + * + * Description: + * Generates a HMAC-SHA256 signature for license request signing under the + * license server protocol for AES CTR mode. + * + * NOTE: OEMCrypto_GenerateDerivedKeys() must be called first to establish the + * mac_key + * + * Refer to document "OEMCrypto Changes for V2 License Protocol" for details. + * + * Parameters: + * session (in) - crypto session identifier. + * message (in) - pointer to memory containing message to be signed. + * message_length (in) - length of the message. + * signature (out) - pointer to memory to received the computed signature. + * signature_length (in/out) - (in) length of the signature buffer. + * (out) actual length of the signature + * + * Threading: + * This function may be called simultaneously with functions on other sessions, + * but not with other functions on this session. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_NO_DEVICE_KEY + * OEMCrypto_ERROR_INVALID_SESSION + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * OEMCrypto_ERROR_INVALID_CONTEXT + */ +OEMCryptoResult OEMCrypto_GenerateSignature( + OEMCrypto_SESSION session, + const uint8_t* message, + size_t message_length, + uint8_t* signature, + size_t* signature_length); + +/* + * OEMCrypto_LoadKeys + * + * Description: + * Installs a set of keys for performing decryption in the current session. + * + * The relevant fields have been extracted from the License Response protocol + * message, but the entire message and associated signature are provided so + * the message can be verified (using HMAC-SHA256 with the derived mac_key). + * If the signature verification fails, ignore all other arguments and return + * OEMCrypto_ERROR_SIGNATURE_FAILURE. Otherwise, add the keys to the session + * context. + * + * The keys will be decrypted using the current encrypt_key (AES-128-CBC) and + * the IV given in the KeyObject. Each key control block will be decrypted + * using the corresponding content key (AES-128-CBC) and the IV given in the + * KeyObject. + * + * If any key's control block does not have valid verification fields, return + * OEMCrypto_ERROR_INVALID_CONTEXT and do not install any keys. + * + * If any key's control block requires a nonce, and the nonce in the control + * block is different from the current nonce, return + * OEMCrypto_ERROR_INVALID_NONCE. In that case, do not install any keys. + * + * The new mac_key is decrypted with the current encrypt_key and the offered + * IV. It replaces the current mac_key. + * + * The mac_key and encrypt_key were generated and stored by the previous call + * to OEMCrypto_GenerateDerivedKeys(). The nonce was generated and stored by + * the previous call to OEMCrypto_GenerateNonce(). + * + * This session’s elapsed time clock is started at 0. The clock will be used + * in OEMCrypto_DecryptCTR. + * + * NOTE: OEMCrypto_GenerateDerivedKeys() must be called first to establish the + * mac_key and encrypt_key. + * + * Refer to document "OEMCrypto Changes for V2 License Protocol" for details. + * + * Parameters: + * session (in) - crypto session identifier. + * message (in) - pointer to memory containing message to be verified. + * message_length (in) - length of the message. + * signature (in) - pointer to memory containing the signature. + * signature_length (in) - length of the signature. + * enc_mac_key_iv (in) - IV for decrypting new mac_key. Size is 128 bits. + * enc_mac_key (in) - encrypted mac_key for generating new mac_key. Size is + * 256 bits. + * num_keys (in) - number of keys present. + * key_array (in) - set of keys to be installed. + * + * Threading: + * This function may be called simultaneously with functions on other sessions, + * but not with other functions on this session. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_NO_DEVICE_KEY + * OEMCrypto_ERROR_INVALID_SESSION + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * OEMCrypto_ERROR_INVALID_CONTEXT + * OEMCrypto_ERROR_SIGNATURE_FAILURE + * OEMCrypto_ERROR_INVALID_NONCE + * OEMCrypto_ERROR_TOO_MANY_KEYS + */ +OEMCryptoResult OEMCrypto_LoadKeys(OEMCrypto_SESSION session, + const uint8_t* message, + size_t message_length, + const uint8_t* signature, + size_t signature_length, + const uint8_t* enc_mac_key_iv, + const uint8_t* enc_mac_key, + size_t num_keys, + const OEMCrypto_KeyObject* key_array); + +/* + * OEMCrypto_RefreshKeys + * + * Description: + * Updates an existing set of keys for continuing decryption in the + * current session. + * + * The relevant fields have been extracted from the Renewal Response protocol + * message, but the entire message and associated signature are provided so + * the message can be verified (using HMAC-SHA256 with the current mac_key). + * If the signature verification fails, ignore all other arguments and return + * OEMCrypto_ERROR_SIGNATURE_FAILURE. Otherwise, add the keys to the session + * context. + * + * NOTE: OEMCrypto_GenerateDerivedKeys() or OEMCrypto_LoadKeys() must be called + * first to establish the mac_key + * + * Refer to document OEMCrypto Changes for V2 License Protocol for details. + * + * Parameters: + * session (in) - crypto session identifier. + * message (in) - pointer to memory containing message to be verified. + * message_length (in) - length of the message. + * signature (in) - pointer to memory containing the signature. + * signature_length (in) - length of the signature. + * num_keys (in) - number of keys present. + * key_array (in) - set of keys to be installed. + * + * Threading: + * This function may be called simultaneously with functions on other sessions, + * but not with other functions on this session. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_NO_DEVICE_KEY + * OEMCrypto_ERROR_INVALID_SESSION + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * OEMCrypto_ERROR_INVALID_CONTEXT + * OEMCrypto_ERROR_INVALID_NONCE + * OEMCrypto_ERROR_SIGNATURE_FAILURE + */ +OEMCryptoResult +OEMCrypto_RefreshKeys(OEMCrypto_SESSION session, + const uint8_t* message, + size_t message_length, + const uint8_t* signature, + size_t signature_length, + size_t num_keys, + const OEMCrypto_KeyRefreshObject* key_array); + +/* + * OEMCrypto_SelectKey + * + * Description: + * Select a content key and install it in the hardware key ladder for + * subsequent decryption operations (OEMCrypto_DecryptCTR()) for this session. + * The specified key must have been previously "installed" via + * OEMCrypto_LoadKeys() or OEMCrypto_RefreshKeys(). + * + * This session’s elapsed time clock is started at 0. The clock will be used + * in OEMCrypto_DecryptCTR. + * + * A key control block is associated with the key and the session, and is used + * to configure the session context. The Key Control data is documented in + * "Key Control Block Definition". + * + * Step 1: Lookup the content key data via the offered key_id. The key data + * includes the key value, the content key IV, the key control + * block, and the key control block IV. + * + * Step 2: Latch the content key into the hardware key ladder. Set + * permission flags and timers based on the key's control block. + * + * Step 3: use the latched content key to decrypt (AES-128-CTR) + * to decrypt buffers passed in via OEMCrypto_DecryptCTR(). Continue + * to use this key until OEMCrypto_SelectKey() is called again, or + * until OEMCrypto_CloseSession() is called. + * + * Parameters: + * session (in) - crypto session identifier + * key_id (in) - pointer to the Key ID + * key_id_length (in) - length of the Key ID in bytes + * + * Threading: + * This function may be called simultaneously with functions on other sessions, + * but not with other functions on this session. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_INVALID_SESSION crypto session ID invalid or not open + * OEMCrypto_ERROR_NO_DEVICE_KEY failed to decrypt device key + * OEMCrypto_ERROR_NO_CONTENT_KEY failed to decrypt content key + * OEMCrypto_ERROR_CONTROL_INVALID invalid or unsupported control input + * OEMCrypto_ERROR_KEYBOX_INVALID cannot decrypt and read from Keybox + */ +OEMCryptoResult OEMCrypto_SelectKey(const OEMCrypto_SESSION session, + const uint8_t* key_id, + size_t key_id_length); + +/* + * OEMCrypto_DecryptCTR + * + * Description: + * + * The API decrypts (AES-CTR) or copies the payload in the buffer referenced by + * the data_addr parameter to the buffer determined by out_buffer, using the key + * previously set by a call to OEMCrypto_SelectKey for the specified session. + * + * Parameters: + * session (in) - crypto session identifier. + * data_addr (in) - An unaligned pointer to this segment of the stream. + * data_length (in) - The length of this segment of the stream. + * is_encrypted (in) - True if the buffer described by data_addr, + * data_length is encrypted. If is_encrypted is false, only the + * data_addr and data_length parameters are used. The iv and offset + * arguments are ignored. + * iv (in) - The initial value block to be used for content decryption. + * This is discussed further below. + * offset (in) - If non-zero, the decryption block boundary is different + * from the start of the data. offset should be subtracted from + * data_addr to compute the starting address of the first decrypted + * block. The bytes between the decryption block start address and + * data_addr are discarded after decryption. + * out_buffer (in) - A caller-owned descriptor that specifies the + * handling of the decrypted byte stream. See OEMCrypto_DestbufferDesc + * for details. + * + * AES CTR is a stream cipher. The stream may be composed of arbitrary- + * length clear and encrypted segments. The encrypted portions of a sample + * are collectively treated as a continuous sequence of decryption + * block-sized blocks even though the sequence is interrupted by clear blocks. + * This means a given encrypted segment may not start or end on a decryption + * block boundary. + * + * If data_addr is not aligned with a decryption block boundary (offset != 0), + * the additional offset bytes before data_addr (pre-padding) are included in + * the decrypt operation, and they are dropped after decryption. If + * data_length + offset is not a multiple of the decryption block size, the + * extra bytes in the final decryption block (post-padding) are also dropped + * after decryption. The caller is responsible for guaranteeing that all + * memory addresses from (data-addr - pre-padding) to (data-addr + + * data-length + post-padding) are valid memory addresses. + * + * After decrypting the entire buffer including any pre-padding and + * post-padding, send data_length bytes starting at data_addr to the decoder. + * + * NOTES: + * IV points to the counter value to be used for the initial + * encrypted block of the input buffer. The IV length is the AES + * block size. For subsequent encrypted AES blocks the IV is + * calculated by incrementing the lower 64 bits (byte 8-15) of the + * IV value used for the previous block. The counter rolls over to + * zero when it reaches its maximum value (0xFFFFFFFFFFFFFFFF). + * The upper 64 bits (byte 0-7) of the IV do not change. + * + * Threading: + * This function may be called simultaneously with functions on other sessions, + * but not with other functions on this session. + * + * Returns: + * OEMCrypto_SUCCESS + * OEMCrypto_ERROR_NO_DEVICE_KEY + * OEMCrypto_ERROR_INVALID_SESSION + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * OEMCrypto_ERROR_INVALID_CONTEXT + * OEMCrypto_ERROR_DECRYPT_FAILED + */ +OEMCryptoResult +OEMCrypto_DecryptCTR(OEMCrypto_SESSION session, + const uint8_t *data_addr, + size_t data_length, + bool is_encrypted, + const uint8_t *iv, + size_t offset, + const OEMCrypto_DestBufferDesc* out_buffer); + +/* + * OEMCrypto_InstallKeybox + * + * Description: + * Unwrap and store the keybox to persistent memory. + * The device key must be stored securely. + * + * This function is used once to load the keybox onto the device at + * provisioning time. + * + * Parameters: + * keybox (in) - Pointer to clear keybox data. Must have been originally + * wrapped with OEMCrypto_WrapKeybox. + * keyboxLength (in) - Length of the keybox data in bytes. + * + * Threading: + * This function is not called simultaneously with any other functions. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_WRITE_KEYBOX failed to handle and store Keybox + */ +OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t *keybox, + size_t keyBoxLength); + +/* + * OEMCrypto_IsKeyboxValid + * + * Description: + * Validate the Widevine Keybox stored on the device. + * + * The API performs two verification steps on the Keybox. It first verifies + * the MAGIC field contains a valid signature (must be 'kbox'). The API then + * computes the CRC using CRC-32 (Posix 1003.2 standard) and compares the + * checksum to the CRC stored in the Keybox. The CRC is computed over the + * entire Keybox excluding the 4 CRC bytes (i.e. Keybox[0..123]). + * + * Parameters: + * none + * + * Threading: + * This function may be called simultaneously with any session functions. + * + * Returns: + * OEMCrypto_SUCCESS + * OEMCrypto_ERROR_BAD_MAGIC + * OEMCrypto_ERROR_BAD_CRC + */ +OEMCryptoResult OEMCrypto_IsKeyboxValid(void); + +/* + * OEMCrypto_GetDeviceID + * + * Description: + * Retrieve the device's unique identifier from the Keybox. + * + * Parameters: + * deviceId (out) - pointer to the buffer that receives the Device ID + * idLength (in/out) - on input, size of the caller's device ID buffer. + * On output, the number of bytes written into the buffer. + * + * Threading: + * This function may be called simultaneously with any session functions. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_SHORT_BUFFER buffer is too small to return the device ID + * OEMCrypto_ERROR_NO_DEVICEID failed to return Device Id + */ +OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID, + size_t *idLength); + +/* + * OEMCrypto_GetKeyData + * + * Description: + * Returns the Key Data field from the Keybox. The Key Data field does not + * need to be encrypted by an OEM root key, but may be if desired. + * + * If the Key Data field was encrypted with an OEM root key when the Keybox + * was stored on the device, then this function should decrypt it and return + * the clear Key Data. If the Key Data was not encrypted, then this function + * should just access and return the clear Key data. + * + * Parameters: + * keyData (out) - pointer to a caller-managed buffer to hold the Key Data + * field from the Keybox + * dataLength (in/out) - on input, the allocated buffer size. On output, + * the number of bytes in KeyData. + * + * Threading: + * This function may be called simultaneously with any session functions. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_SHORT_BUFFER the buffer is too small to return the KeyData + * OEMCrypto_ERROR_NO_KEYDATA failed to return KeyData + */ +OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData, + size_t *keyDataLength); + +/* + * OEMCrypto_GetRandom + * + * Description: + * Return a buffer filled with hardware-generated random bytes. If the + * hardware feature does not exist, return OEMCrypto_ERROR_RNG_NOT_SUPPORTED. + * + * Parameters: + * randomData (out) - Pointer to caller-manager buffer that will receive the + * random data. + * dataLength (in) - Length of the random data buffer in bytes. + * + * Threading: + * This function may be called simultaneously with any session functions. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_RNG_FAILED failed to generate random number + * OEMCrypto_ERROR_RNG_NOT_SUPPORTED function not supported + */ +OEMCryptoResult OEMCrypto_GetRandom(uint8_t* randomData, + size_t dataLength); + +/* + * OEMCrypto_WrapKeybox + * + * Description: + * Wrap the Keybox with a key derived for the device key. If transportKey + * is not NULL, the input keybox is encrypted with transportKey. If so, + * decrypt the input keybox before wrapping it, using transportKey in AES-CBC + * mode with an IV of all zeroes. This function is only needed if the + * provisioning method involves saving the keybox to the file system. + * + * Parameters: + * keybox (in) - Pointer to keybox data. + * keyboxLength - Length of the Keybox data in bytes + * wrappedKeybox (out) - Pointer to wrapped keybox + * wrappedKeyboxLength (out) - Pointer to the length of the wrapped keybox in + * bytes + * transportKey (in) - An optional AES transport key. If provided, the input + * keybox is encrypted with this transport key with AES-CBC + * and a null IV. + * transportKeyLength - number of bytes in the transportKey + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_WRAP_KEYBOX failed to wrap Keybox + * OEMCrypto_ERROR_NOT_IMPLEMENTED + */ +OEMCryptoResult OEMCrypto_WrapKeybox(const uint8_t *keybox, + size_t keyBoxLength, + uint8_t *wrappedKeybox, + size_t *wrappedKeyBoxLength, + const uint8_t *transportKey, + size_t transportKeyLength); + + +/* + * OEMCrypto_RewrapDeviceRSAKey + * + * Description: + * Verifies an RSA provisioning response is valid and corresponds + * to the previous provisioning request by checking the nonce. The RSA + * private key is decrypted and stored in secure memory. The RSA key is then + * re-encrypted for storage on the filesystem. The OEM may either encrypt it + * with the private key from the Widevine Keybox, or with an OEM specific + * device key. + * + * Parameters: + * session (in) - crypto session identifier. + * message (in) - pointer to memory containing message to be + * - verified. + * message_length (in) - length of the message, in bytes. + * signature (in) - pointer to memory containing the HMAC-SHA256 + * - signature for + * - message, received from the provisioning server. + * signature_length (in) - length of the signature, in bytes. + * nonce (in) - The nonce provided in the provisioning response. + * enc_rsa_key (in) - Encrypted device private RSA key received from + * - the provisioning server. Format is PKCS#1, binary + * - DER encoded, and encrypted with the derived + * - encryption key, using AES-128-CBC with PKCS#5 + * - padding. + * enc_rsa_key_length (in) - length of the encrypted RSA key, in bytes. + * enc_rsa_key_iv (in) - IV for decrypting RSA key. Size is 128 bits. + * wrapped_rsa_key (out) - pointer to buffer in which encrypted RSA key + * - should be stored. May be null on the first call + * - in order to find required buffer size. + * wrapped_rsa_key_length (in/out) - length of the encrypted RSA key, in bytes. + * wrapped_rsa_key_iv (out) - IV for encrypting/decrypting the RSA private key. + * - Size is 128 bits. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_NO_DEVICE_KEY + * OEMCrypto_ERROR_INVALID_SESSION + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * OEMCrypto_ERROR_INVALID_RSA_KEY + * OEMCrypto_ERROR_SIGNATURE_FAILURE + * OEMCrypto_ERROR_INVALID_NONCE + * OEMCrypto_ERROR_SHORT_BUFFER + */ + +OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session, + const uint8_t* message, + size_t message_length, + const uint8_t* signature, + size_t signature_length, + uint32_t *nonce, + const uint8_t* enc_rsa_key, + size_t enc_rsa_key_length, + const uint8_t* enc_rsa_key_iv, + uint8_t* wrapped_rsa_key, + size_t *wrapped_rsa_key_length); + +/* + * OEMCrypto_LoadDeviceRSAKey + * + * Description: + * Loads a wrapped RSA private key to secure memory for use by this session + * in future calls to OEMCrypto_GenerateRSASignature. The wrapped RSA key + * will be one verified and wrapped by OEMCrypto_RewrapDeviceRSAKey. The RSA + * private key should be stored in secure memory. + * + * Parameters: + * session (in) - crypto session identifier. + * wrapped_rsa_key (in) - wrapped device RSA key stored on the device. + * - Format is PKCS#1, binary DER encoded, and + * - encrypted with a key internal to the OEMCrypto + * - instance, using AES-128-CBC with PKCS#5 + * - padding. This is the wrapped key generated + * - by OEMCrypto_RewrapDeviceRSAKey. + * wrapped_rsa_key_length (in) - length of the wrapped key buffer, in bytes. + * wrapped_rsa_key_iv (in) - The initialization vector used to encrypt + * - wrapped_rsa_key. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_NO_DEVICE_KEY + * OEMCrypto_ERROR_INVALID_SESSION + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * OEMCrypto_ERROR_INVALID_RSA_KEY + */ +OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(OEMCrypto_SESSION session, + const uint8_t* wrapped_rsa_key, + size_t wrapped_rsa_key_length); + +/* + * OEMCrypto_GenerateRSASignature + * + * Description: + * The OEMCrypto_GenerateRSASignature method is used to sign messages using + * the device private RSA key, specifically, it is used to sign the initial + * license request. + * + * Refer to the document "Widevine Security Integration Guide for DASH" for + * more details. + * + * Parameters: + * session (in) - crypto session identifier. + * message (in) - pointer to memory containing message to be + * - signed. + * message_length (in) - length of the message, in bytes. + * signature (out) - buffer to hold the message signature. On + * - return, it will contain the message signature + * - generated with the device private RSA key using + * - RSASSA-PSS. + * signature_length (in/out) - (in) length of the signature buffer, in bytes. + * - (out) actual length of the signature + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_INVALID_SESSION + * OEMCrypto_ERROR_SHORT_BUFFER if the signature buffer is too small. + * OEMCrypto_ERROR_CLOSE_SESSION_FAILED illegal/unrecognized handle or the + * security engine is not properly initialized. + */ +OEMCryptoResult OEMCrypto_GenerateRSASignature(OEMCrypto_SESSION session, + const uint8_t* message, + size_t message_length, + uint8_t* signature, + size_t *signature_length); + +/* + * OEMCrypto_DeriveKeysFromSessionKey + * + * Description: + * Generates a pair of secondary keys, mac_key and encrypt_key, for handling + * signing and content key decryption under the license server protocol for + * AES CTR mode. + * + * This function is similar to OEMCrypto_GenerateDerivedKeys, except that it + * uses a session key to generate the secondary keys instead of the Widevine + * Keybox device key. These two keys will be stored in secure memory until + * the next call to LoadKeys. The session key is passed in encrypted by the + * device RSA public key, and must be decrypted with the RSA private key + * before use. Once the enc_key and mac_key have been generated, all calls + * to LoadKeys and RefreshKeys proceed in the same manner for license + * requests using RSA or using a Widevine keybox token. + * + * Parameters: + * session (in) - crypto session identifier. + * enc_session_key (in) - session key, encrypted with the device RSA key + * - (from the device certifcate) using RSA-OAEP. + * enc_session_key_length (in) - length of session_key, in bytes. + * mac_key_context (in) - pointer to memory containing context data for + * - computing the HMAC generation key. + * mac_key_context_length (in) - length of the HMAC key context data, in bytes. + * enc_key_context (in) - pointer to memory containing context data for + * - computing the encryption key. + * enc_key_context_length (in) - length of the encryption key context data, in + * - bytes. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_DEVICE_NOT_RSA_PROVISIONED + * OEMCrypto_ERROR_INVALID_SESSION + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * OEMCrypto_ERROR_INVALID_CONTEXT + */ +OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey(OEMCrypto_SESSION session, + const uint8_t* enc_session_key, + size_t enc_session_key_length, + const uint8_t *mac_key_context, + size_t mac_key_context_length, + const uint8_t *enc_key_context, + size_t enc_key_context_length); + +#ifdef __cplusplus +} +#endif + +#endif // OEMCRYPTO_CENC_H_ diff --git a/libwvdrmengine/oemcrypto/mock/Android.mk b/libwvdrmengine/oemcrypto/mock/Android.mk new file mode 100644 index 00000000..bd19649a --- /dev/null +++ b/libwvdrmengine/oemcrypto/mock/Android.mk @@ -0,0 +1,38 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + src/oemcrypto_engine_mock.cpp \ + src/oemcrypto_key_mock.cpp \ + src/oemcrypto_keybox_mock.cpp \ + src/oemcrypto_mock.cpp \ + src/lock.cpp \ + src/log.cpp \ + src/string_conversions.cpp \ + src/wvcrc.cpp \ + +LOCAL_MODULE_TAGS := tests + +LOCAL_C_INCLUDES += \ + $(LOCAL_PATH)/../include \ + $(LOCAL_PATH)/src \ + bionic \ + external/gtest/include \ + external/openssl/include \ + external/openssl/include/openssl \ + external/stlport/stlport \ + +LOCAL_SHARED_LIBRARIES := \ + libcrypto \ + libcutils \ + libdl \ + liblog \ + libstlport \ + libutils \ + libz \ + +LOCAL_MODULE := liboemcrypto + +include $(BUILD_SHARED_LIBRARY) + diff --git a/libwvdrmengine/oemcrypto/mock/Makefile b/libwvdrmengine/oemcrypto/mock/Makefile new file mode 100644 index 00000000..52d4dbb0 --- /dev/null +++ b/libwvdrmengine/oemcrypto/mock/Makefile @@ -0,0 +1,109 @@ +# +# Builds liboemcrypto_mock.so +# + +#PROJECTS_ROOT = ~projects +# +ifndef PROJECTS_ROOT +PROJECTS_ROOT = ../../../.. +endif + +CDM_ROOT = $(PROJECTS_ROOT)/cdm +CDM_SRC_PATH = $(CDM_ROOT)/cdm +CDM_INCLUDE_PATH = $(CDM_SRC_PATH)/include + +EUREKA_ROOT = $(PROJECTS_ROOT)/eureka/eureka/src +CHROME_ROOT = $(EUREKA_ROOT)/chromium/src +# +# build outputs should go into Chrome repository, such as ../chromium/src/out +# or some local equivalent. +# WARNING: splitting outputs from CHROME_ROOT can lead to build errors +ifndef CHROME_ROOT +CHROME_ROOT = $(CDM_ROOT)/out +endif + +# TARGET_PLATFORM from {x86,eureka} +ifndef TARGET_PLATFORM +TARGET_PLATFORM = x86 +endif + +# TARGET_BUILD from {debug,release} +ifndef TARGET_BUILD +TARGET_BUILD = debug +endif + +ifeq ($(TARGET_PLATFORM),x86) + BUILDPLATFORM = out_x86_linux +else ifeq ($(TARGET_PLATFORM),eureka) + BUILDPLATFORM = out_arm_eureka +else + BUILDPLATFORM = UNKNOWN +endif + +ifeq ($(TARGET_BUILD),debug) + BUILDTYPE = Debug +else ifeq ($(TARGET_BUILD),release) + BUILDTYPE = Release +else + BUILDTYPE = UNKNOWN +endif + +BUILDPATH = $(CHROME_ROOT)/$(BUILDPLATFORM)/$(BUILDTYPE) +OBJPATH = $(BUILDPATH)/obj + +#primary build target +TARGET_LIB = liboemcrypto_mock.so + +LIB_OBJECTS = \ + oemcrypto_mock.o \ + oemcrypto_engine_mock.o \ + oemcrypto_key_mock.o \ + oemcrypto_keybox_mock.o + +INCLUDES = \ + -I$(CDM_INCLUDE_PATH) + +OBJECTDIR = $(OBJPATH)/mock + +INSTALLDIR = $(BUILDPATH) + +OBJECTS := $(patsubst %.o,$(OBJECTDIR)/%.o,$(LIB_OBJECTS)) + +CXXFLAGS = -m64 -fPIC -W -Wall -g -DCDM_TEST +LINK = $(CXX) +MKDIR = mkdir -p + +$(INSTALLDIR)/$(TARGET_LIB): $(OBJECTDIR) $(INSTALLDIR) $(OBJECTS) + $(LINK) -v -fPIC -m64 -shared -static-libgcc $(SHLIBFLAGS) \ + $(OBJECTS) \ + --retain-symbols-file=OECsymbols.txt \ + -Wl,-Bstatic \ + -Wl,-Bdynamic -lcrypto \ + -o $@ + +$(OBJECTDIR)/%.o: src/%.cpp + $(CXX) -c -DCDM_TEST $(CXXFLAGS) $(INCLUDES) $< -o $@ + +$(OBJECTDIR)/%.o: src/%.cc + $(CXX) -c -DCDM_TEST $(CXXFLAGS) $(INCLUDES) $< -o $@ + +test: + make -C test + +clean: + $(RM) -rf $(OBJECTDIR) + $(RM) -rf $(INSTALLDIR)/$(TARGET_LIB) + +$(OBJECTDIR): + @$(MKDIR) $@ + +$(INSTALLDIR): + @$(MKDIR) $@ + +.PHONY: $(OBJECTDIR) + +.PHONY: $(INSTALLDIR) + +.PHONY: clean + +.PHONY: test diff --git a/libwvdrmengine/oemcrypto/mock/src/lock.cpp b/libwvdrmengine/oemcrypto/mock/src/lock.cpp new file mode 100644 index 00000000..cfec2477 --- /dev/null +++ b/libwvdrmengine/oemcrypto/mock/src/lock.cpp @@ -0,0 +1,54 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Lock class - provides a simple android specific mutex implementation + +#include "lock.h" +#include "utils/Mutex.h" + +namespace wvcdm { + +class Lock::Impl { + public: + android::Mutex lock_; +}; + +Lock::Lock() : impl_(new Lock::Impl()) { +} + +Lock::~Lock() { + delete impl_; + impl_ = NULL; +} + +void Lock::Acquire() { + impl_->lock_.lock(); +} + +void Lock::Release() { + impl_->lock_.unlock(); +} + +bool Lock::Try() { + return (impl_->lock_.tryLock() == 0); +} + +class AutoLock::Impl { + public: + android::Mutex::Autolock* autolock_; +}; + +AutoLock::AutoLock(Lock& lock) : impl_(new AutoLock::Impl()) { + impl_->autolock_ = new android::Mutex::Autolock(lock.impl_->lock_); +} + +AutoLock::AutoLock(Lock* lock) : impl_(new AutoLock::Impl()) { + impl_->autolock_ = new android::Mutex::Autolock(lock->impl_->lock_); +} + +AutoLock::~AutoLock() { + delete impl_->autolock_; + delete impl_; + impl_ = NULL; +} + +}; // namespace wvcdm diff --git a/libwvdrmengine/oemcrypto/mock/src/lock.h b/libwvdrmengine/oemcrypto/mock/src/lock.h new file mode 100644 index 00000000..bd9402e6 --- /dev/null +++ b/libwvdrmengine/oemcrypto/mock/src/lock.h @@ -0,0 +1,54 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Lock - Platform independent interface for a Mutex class +// +#ifndef OEMCRYPTO_LOCK_H_ +#define OEMCRYPTO_LOCK_H_ + +#include "wv_cdm_types.h" + +namespace wvcdm { + +// Simple lock class. The implementation is platform dependent. +// +// The lock must be unlocked by the thread that locked it. +// The lock is also not recursive (ie. cannot be taken multiple times). +class Lock { + public: + Lock(); + ~Lock(); + + void Acquire(); + void Release(); + + // Acquires a lock if not held and returns true. + // Returns false if the lock is held by another thread. + bool Try(); + + friend class AutoLock; + + private: + class Impl; + Impl* impl_; + + CORE_DISALLOW_COPY_AND_ASSIGN(Lock); +}; + +// Manages the lock automatically. It will be locked when AutoLock +// is constructed and release when AutoLock goes out of scope +class AutoLock { + public: + explicit AutoLock(Lock& lock); + explicit AutoLock(Lock* lock); + ~AutoLock(); + + private: + class Impl; + Impl* impl_; + + CORE_DISALLOW_COPY_AND_ASSIGN(AutoLock); +}; + +}; // namespace wvcdm + +#endif // OEMCRYPTO_LOCK_H_ diff --git a/libwvdrmengine/oemcrypto/mock/src/log.cpp b/libwvdrmengine/oemcrypto/mock/src/log.cpp new file mode 100644 index 00000000..9d5559a7 --- /dev/null +++ b/libwvdrmengine/oemcrypto/mock/src/log.cpp @@ -0,0 +1,33 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Log - implemented using the standard Android logging mechanism + +#define LOG_TAG "WVCdm" +#define LOG_BUF_SIZE 1024 + +#include "log.h" +#include "utils/Log.h" + +namespace wvcdm { + +void log_write(LogPriority level, const char* fmt, ...) { + va_list ap; + char buf[LOG_BUF_SIZE]; + va_start(ap, fmt); + vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); + va_end(ap); + + android_LogPriority prio = ANDROID_LOG_VERBOSE; + + switch(level) { + case LOG_ERROR: prio = ANDROID_LOG_ERROR; break; + case LOG_WARN: prio = ANDROID_LOG_WARN; break; + case LOG_INFO: prio = ANDROID_LOG_INFO; break; + case LOG_DEBUG: prio = ANDROID_LOG_DEBUG; break; + case LOG_VERBOSE: prio = ANDROID_LOG_VERBOSE; break; + } + + __android_log_write(prio, LOG_TAG, buf); +} + +}; // namespace wvcdm diff --git a/libwvdrmengine/oemcrypto/mock/src/log.h b/libwvdrmengine/oemcrypto/mock/src/log.h new file mode 100644 index 00000000..ce4204f3 --- /dev/null +++ b/libwvdrmengine/oemcrypto/mock/src/log.h @@ -0,0 +1,31 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Log - Platform independent interface for a Logging class +// +#ifndef OEMCRYPTO_LOG_H_ +#define OEMCRYPTO_LOG_H_ + +namespace wvcdm { + +// Simple logging class. The implementation is platform dependent. + +typedef enum { + LOG_ERROR, + LOG_WARN, + LOG_INFO, + LOG_DEBUG, + LOG_VERBOSE +} LogPriority; + +void log_write(LogPriority priority, const char *fmt, ...); + +// Log APIs +#define LOGE(...) ((void)log_write(wvcdm::LOG_ERROR, __VA_ARGS__)) +#define LOGW(...) ((void)log_write(wvcdm::LOG_WARN, __VA_ARGS__)) +#define LOGI(...) ((void)log_write(wvcdm::LOG_INFO, __VA_ARGS__)) +#define LOGD(...) ((void)log_write(wvcdm::LOG_DEBUG, __VA_ARGS__)) +#define LOGV(...) ((void)log_write(wvcdm::LOG_VERBOSE, __VA_ARGS__)) + +}; // namespace wvcdm + +#endif // OEMCRYPTO_LOG_H_ diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.cpp b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.cpp new file mode 100644 index 00000000..48c0e87b --- /dev/null +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.cpp @@ -0,0 +1,742 @@ +/******************************************************************************* + * + * Copyright 2013 Google Inc. All Rights Reserved. + * + * mock implementation of OEMCrypto APIs + * + ******************************************************************************/ + +#include "oemcrypto_engine_mock.h" + +#include +#include +#include + +#include "log.h" +#include "oemcrypto_key_mock.h" +#include "openssl/aes.h" +#include "openssl/cmac.h" +#include "openssl/err.h" +#include "openssl/evp.h" +#include "openssl/hmac.h" +#include "openssl/rand.h" +#include +#include "openssl/sha.h" +#include "string_conversions.h" +#include "wv_cdm_constants.h" + +namespace { +// Increment counter for AES-CTR +void ctr128_inc(uint8_t* counter) { + uint32_t n = 16; + do { + if (++counter[--n] != 0) return; + } while (n); +} +void dump_openssl_error() { + while (unsigned long err = ERR_get_error()) { + char buffer[120]; + LOGE("openssl error: %lu: %s", + err, ERR_error_string(err, buffer)); + } +} +} + +namespace wvoec_mock { + +SessionKeyTable::~SessionKeyTable() { + for (KeyMap::iterator i = keys_.begin(); i != keys_.end(); ++i) { + if (NULL != i->second) { + delete i->second; + } + } +} + +bool SessionKeyTable::Insert(const KeyId key_id, const Key& key_data) { + if (keys_.find(key_id) != keys_.end()) return false; + keys_[key_id] = new Key(key_data); + return true; +} + +Key* SessionKeyTable::Find(const KeyId key_id) { + if (keys_.find(key_id) == keys_.end()) { + return NULL; + } + return keys_[key_id]; +} + +void SessionKeyTable::Remove(const KeyId key_id) { + if (keys_.find(key_id) != keys_.end()) { + delete keys_[key_id]; + keys_.erase(key_id); + } +} + +void SessionContext::Open() { +} + +void SessionContext::Close() { +} + +// Internal utility function to derive key using CMAC-128 +bool SessionContext::DeriveKey(const std::vector& key, + const std::vector& context, + int counter, + std::vector* out) { + if (key.empty() || counter > 2 || context.empty() || out == NULL) { + LOGE("[DeriveKey(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return false; + } + + const EVP_CIPHER* cipher = EVP_aes_128_cbc(); + CMAC_CTX* cmac_ctx = CMAC_CTX_new(); + + if (!CMAC_Init(cmac_ctx, &key[0], key.size(), cipher, 0)) { + LOGE("[DeriveKey(): OEMCrypto_ERROR_CMAC_FAILURE]"); + return false; + } + + std::vector message; + message.push_back(counter); + message.insert(message.end(), context.begin(), context.end()); + + if (!CMAC_Update(cmac_ctx, &message[0], message.size())) { + LOGE("[DeriveKey(): OEMCrypto_ERROR_CMAC_FAILURE]"); + return false; + } + + size_t reslen; + uint8_t res[128]; + if (!CMAC_Final(cmac_ctx, res, &reslen)) { + LOGE("[DeriveKey(): OEMCrypto_ERROR_CMAC_FAILURE]"); + return false; + } + + out->assign(res, res + reslen); + + CMAC_CTX_free(cmac_ctx); + + return true; +} + +bool SessionContext::DeriveKeys(const std::vector& mac_key_context, + const std::vector& enc_key_context) { + // Generate derived key for mac key + std::vector device_key = ce_->keybox().device_key().value(); + std::vector mac_key; + std::vector mac_key_part2; + if (!DeriveKey(device_key, mac_key_context, 1, &mac_key)) { + return false; + } + if (!DeriveKey(device_key, mac_key_context, 2, &mac_key_part2)) { + return false; + } + mac_key.insert(mac_key.end(), mac_key_part2.begin(), mac_key_part2.end()); + + // Generate derived key for encryption key + std::vector enc_key; + if (!DeriveKey(device_key, enc_key_context, 1, &enc_key)) { + return false; + } + + set_mac_key(mac_key); + set_encryption_key(enc_key); + return true; +} + +bool SessionContext::RSADeriveKeys(const std::vector& enc_session_key, + const std::vector& mac_key_context, + const std::vector& enc_key_context) { + if (!rsa_key_) { + LOGE("[RSADeriveKeys(): no RSA key set]"); + return false; + } + session_key_.resize(wvcdm::KEY_SIZE); + if (-1 == RSA_private_decrypt(wvcdm::KEY_SIZE, &enc_session_key[0], + &session_key_[0], rsa_key_, + RSA_PKCS1_OAEP_PADDING)) { + LOGE("[RSADeriveKeys(): error decrypting session key.]"); + dump_openssl_error(); + return false; + } + // Generate derived key for mac key + std::vector mac_key; + std::vector mac_key_part2; + if (!DeriveKey(session_key_, mac_key_context, 1, &mac_key)) { + return false; + } + if (!DeriveKey(session_key_, mac_key_context, 2, &mac_key_part2)) { + return false; + } + mac_key.insert(mac_key.end(), mac_key_part2.begin(), mac_key_part2.end()); + + // Generate derived key for encryption key + std::vector enc_key; + if (!DeriveKey(session_key_, enc_key_context, 1, &enc_key)) { + return false; + } + + set_mac_key(mac_key); + set_encryption_key(enc_key); + return true; +} + +// Utility function to generate a message signature +bool SessionContext::GenerateSignature(const uint8_t* message, + size_t message_length, + uint8_t* signature, + size_t* signature_length) { + + if (message == NULL || message_length == 0 || + signature == NULL || signature_length == 0) { + LOGE("[OEMCrypto_GenerateSignature(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return false; + } + + if (mac_key_.empty() || mac_key_.size() != wvcdm::MAC_KEY_SIZE) { + LOGE("[GenerateSignature(): No MAC Key]"); + return false; + } + + if (*signature_length < SHA256_DIGEST_LENGTH) { + *signature_length = SHA256_DIGEST_LENGTH; + return false; + } + + unsigned int md_len = *signature_length; + if (HMAC(EVP_sha256(), &mac_key_[0], SHA256_DIGEST_LENGTH, + message, message_length, signature, &md_len)) { + *signature_length = md_len; + return true; + } + return false; +} + +bool SessionContext::GenerateRSASignature(const uint8_t* message, + size_t message_length, + uint8_t* signature, + size_t* signature_length) { + if (message == NULL || message_length == 0 || + signature == NULL || signature_length == 0) { + LOGE("[GenerateRSASignature(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return false; + } + if (!rsa_key_) { + LOGE("[GenerateRSASignature(): no RSA key set]"); + return false; + } + if (*signature_length < static_cast(RSA_size(rsa_key_))) { + *signature_length = RSA_size(rsa_key_); + return false; + } + // TODO(fredgc): This uses the wrong algorithm for signing. + // This code needs to be fixed!! + LOGE("COmputing signature using RSASSA-PKCS1v1.5 instead of RSASSA-PSS"); + uint8_t hash[SHA_DIGEST_LENGTH]; + if (!SHA1(message, message_length, hash)) { + LOGE("[GeneratRSASignature(): error creating signature hash.]"); + dump_openssl_error(); + return false; + } + int ret = RSA_sign(NID_sha1, hash, SHA_DIGEST_LENGTH, + signature, signature_length, rsa_key_); + if (ret != 1) { + LOGE("[GeneratRSASignature(): error signing signature hash.]"); + dump_openssl_error(); + return false; + } + return true; +} + +// Validate message signature +bool SessionContext::ValidateMessage(const uint8_t* given_message, + size_t message_length, + const uint8_t* given_signature, + size_t signature_length) { + + if (signature_length != SHA256_DIGEST_LENGTH) { + return false; + } + uint8_t computed_signature[signature_length]; + if (! GenerateSignature(given_message, message_length, + computed_signature, &signature_length)) { + return false; + } + if (memcmp(given_signature, computed_signature, signature_length)) { + LOGE("Invalid signature given: %s", + wvcdm::HexEncode(given_signature, signature_length).c_str()); + LOGE("Invalid signature computed: %s", + wvcdm::HexEncode(computed_signature, signature_length).c_str()); + return false; + } + return true; +} + +bool SessionContext::ParseKeyControl( + const std::vector& key_control_string, + KeyControlBlock& key_control_block) { + + key_control_block.Invalidate(); + + if (key_control_string.size() < wvcdm::KEY_CONTROL_SIZE) { + return false; + } + if (!key_control_block.SetFromString(key_control_string)) { + LOGE("KCB: BAD Size or Structure"); + return false; + } + + if (!key_control_block.Validate()) { + LOGE("KCB: BAD Signature"); + return false; + } + if (!CheckNonce(key_control_block.nonce())) { + LOGE("KCB: BAD Nonce"); + return false; + } + + LOGD("KCB:"); + LOGD(" valid: %d", key_control_block.valid()); + LOGD(" duration: %d", key_control_block.duration()); + LOGD(" nonce: %08X", key_control_block.nonce()); + LOGD(" bits: %08X", key_control_block.control_bits()); + LOGD(" bit kControlObserveDataPath %s.", + (key_control_block.control_bits() & kControlObserveDataPath) ? "set" : "unset"); + LOGD(" bit kControlObserveHDCP %s.", + (key_control_block.control_bits() & kControlObserveHDCP) ? "set" : "unset"); + LOGD(" bit kControlObserveCGMS %s.", + (key_control_block.control_bits() & kControlObserveCGMS) ? "set" : "unset"); + LOGD(" bit kControlDataPathSecure %s.", + (key_control_block.control_bits() & kControlDataPathSecure) ? "set" : "unset"); + LOGD(" bit kControlNonceEnabled %s.", + (key_control_block.control_bits() & kControlNonceEnabled) ? "set" : "unset"); + LOGD(" bit kControlHDCPRequired %s.", + (key_control_block.control_bits() & kControlHDCPRequired) ? "set" : "unset"); + uint32_t cgms_bits = key_control_block.control_bits() & 0x3; + const char* cgms_values[4] = {"free", "BAD", "once", "never"}; + LOGD(" CGMS = %s", cgms_values[cgms_bits]); + + return true; +} + +void SessionContext::StartTimer() { + timer_start_ = time(NULL); +} + +uint32_t SessionContext::CurrentTimer() { + time_t now = time(NULL); + return now - timer_start_; +} + +bool SessionContext::InstallKey(const KeyId& key_id, + const std::vector& key_data, + const std::vector& key_data_iv, + const std::vector& key_control, + const std::vector& key_control_iv) { + + // Decrypt encrypted key_data using derived encryption key and offered iv + std::vector content_key; + std::vector key_control_str; + KeyControlBlock key_control_block; + + if (!ce_->DecryptMessage(this, encryption_key_, key_data_iv, + key_data, &content_key)) { + LOGE("[Installkey(): Could not decrypt key data]"); + return false; + } + +#if 0 // Print content key to stdout. + std::cout << " InstallKey: key_id = " + << wvcdm::b2a_hex(key_id) << std::endl; + std::cout << " InstallKey: content_key = " + << wvcdm::b2a_hex(content_key) << std::endl; + std::cout << " InstallKey: key_control = " + << wvcdm::b2a_hex(content_key) << std::endl; +#endif + + // Key control must be supplied by license server + if (key_control.empty()) { + LOGE("[Installkey(): WARNING: No Key Control]"); + key_control_block.Invalidate(); + return false; + } else { + if (key_control_iv.empty()) { + LOGE("[Installkey(): ERROR: No Key Control IV]"); + return false; + } + if (!ce_->DecryptMessage(this, content_key, key_control_iv, + key_control, &key_control_str)) { + LOGE("[Installkey(): ERROR: Could not decrypt content key]"); + return false; + } + + if (!ParseKeyControl(key_control_str, key_control_block)) { + return false; + } + } + + Key key(KEYTYPE_CONTENT, content_key, key_control_block); + session_keys_.Insert(key_id, key); + return true; +} + +bool SessionContext::EncryptRSAKey(uint8_t* wrapped_rsa_key, + size_t wrapped_rsa_key_length, + uint8_t* wrapped_rsa_key_iv) { + std::vector buffer(wrapped_rsa_key_length); + uint8_t* p = &buffer[0]; + int len = i2d_RSAPrivateKey(rsa_key_, &p); + if (len < 0) { + LOGE("[RewrapRSAKey(): Could not decode rsa key]"); + dump_openssl_error(); + return false; + } + + // Encrypt rsa key with keybox. + uint8_t iv_buffer[ wvcdm::KEY_IV_SIZE]; + memcpy(iv_buffer, wrapped_rsa_key_iv, wvcdm::KEY_IV_SIZE); + AES_KEY aes_key; + AES_set_encrypt_key(&encryption_key_[0], 128, &aes_key); + AES_cbc_encrypt(&buffer[0], wrapped_rsa_key, wrapped_rsa_key_length, + &aes_key, iv_buffer, AES_ENCRYPT); + return true; +} + +bool SessionContext::LoadRSAKey(const uint8_t* enc_rsa_key, + size_t enc_rsa_key_length, + const uint8_t* enc_rsa_key_iv, + const uint8_t* message, + size_t message_length, + const uint8_t* signature, + size_t signature_length) { + std::vector enc_rsa_key_v(enc_rsa_key, + enc_rsa_key + enc_rsa_key_length); + std::vector iv(enc_rsa_key_iv, enc_rsa_key_iv + wvcdm::KEY_IV_SIZE); + std::vector rsa_key; // unencrypted. + + // Validate message signature + if (!ValidateMessage(message, message_length, signature, signature_length)) { + LOGE("[LoadRSAKey(): Could not verify signature]"); + return false; + } + if (!ce_->DecryptMessage(this, encryption_key_, iv, + enc_rsa_key_v, &rsa_key)) { + LOGE("[LoadRSAKey(): Could not decrypt key data]"); + return false; + } + if (rsa_key_) { + RSA_free(rsa_key_); + rsa_key_ = NULL; + } + uint8_t const* p = &rsa_key[0]; + RSA* rsa = d2i_RSAPrivateKey(0, &p, rsa_key.size()); + rsa_key_ = rsa; + if (! rsa_key_) { + LOGE("[LoadRSAKey(): Could decode unencrypted rsa key]"); + dump_openssl_error(); + return false; + } + switch (RSA_check_key(rsa_key_)) { + case 1: // valid. + return true; + case 0: // not valid. + LOGE("[LoadRSAKey(): rsa key not valid]"); + dump_openssl_error(); + return false; + default: // -1 == check failed. + LOGE("[LoadRSAKey(): error checking rsa key]"); + dump_openssl_error(); + return false; + } +} + + +bool SessionContext::RefreshKey(const KeyId& key_id, + const std::vector& key_control, + const std::vector& key_control_iv) { + if (key_id.empty()) { + return false; + } + + Key* content_key = session_keys_.Find(key_id); + + if (NULL == content_key) { + return false; + } + + if (!key_control.empty()) { + const std::vector content_key_value = content_key->value(); + + // Decrypt encrypted key control block + // We don't actually make use of it in Oemcrypto mock, just to verify its + // validity + std::vector control; + if (key_control_iv.empty()) { + control = key_control; + } else if (!ce_->DecryptMessage(this, content_key_value, key_control_iv, + key_control, &control)) { + return false; + } + + KeyControlBlock key_control_block; + if (!ParseKeyControl(control, key_control_block)) { + return false; + } + + if (!content_key->UpdateControl(key_control_block)) { + return false; + } + } + + return true; +} + +bool SessionContext::UpdateMacKey(const std::vector& enc_mac_key, + const std::vector& iv) { + + // Decrypt mac key from enc_mac_key using device_key + std::vector mac_key; + if (!ce_->DecryptMessage(this, encryption_key_, iv, + enc_mac_key, &mac_key)) { + return false; + } + mac_key_ = mac_key; + return true; +} + +bool SessionContext::SelectContentKey(const KeyId& key_id) { + const Key* content_key = session_keys_.Find(key_id); + if (NULL == content_key) { + LOGE("[SelectContentKey(): No key matches key id]"); + return false; + } + current_content_key_ = content_key; + return true; +} + +void SessionContext::AddNonce(uint32_t nonce) { + nonce_table_.AddNonce(nonce); +} + +bool SessionContext::CheckNonce(uint32_t nonce) { + return nonce_table_.CheckNonce(nonce); +} + +void SessionContext::FlushNonces() { + nonce_table_.Flush(); +} + +CryptoEngine::CryptoEngine() : + ce_state_(CE_INITIALIZED), current_session_(NULL) { + valid_ = true; + ERR_load_crypto_strings(); +} + +CryptoEngine::~CryptoEngine() { + current_session_ = NULL; + sessions_.clear(); +} + +void CryptoEngine::Terminate() { +} + +KeyboxError CryptoEngine::ValidateKeybox() { return keybox_.Validate(); } + +SessionId CryptoEngine::CreateSession() { + wvcdm::AutoLock lock(session_table_lock_); + static int unique_id = 1; + SessionId sid = (SessionId)++unique_id; + SessionContext* sctx = new SessionContext(this, sid); + sessions_[sid] = sctx; + return sid; +} + +bool CryptoEngine::DestroySession(SessionId sid) { + SessionContext* sctx = FindSession(sid); + wvcdm::AutoLock lock(session_table_lock_); + if (sctx) { + sessions_.erase(sid); + delete sctx; + return true; + } else { + return false; + } +} + +SessionContext* CryptoEngine::FindSession(SessionId sid) { + wvcdm::AutoLock lock(session_table_lock_); + ActiveSessions::iterator it = sessions_.find(sid); + if (it != sessions_.end()) { + return it->second; + } + return NULL; +} + +// Internal utility function to decrypt the message +bool CryptoEngine::DecryptMessage(SessionContext* session, + const std::vector& key, + const std::vector& iv, + const std::vector& message, + std::vector* decrypted) { + if (key.empty() || iv.empty() || message.empty() || !decrypted) { + LOGE("[DecryptMessage(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return false; + } + + decrypted->resize(message.size()); + uint8_t iv_buffer[16]; + memcpy(iv_buffer, &iv[0], 16); + AES_KEY aes_key; + AES_set_decrypt_key(&key[0], 128, &aes_key); + AES_cbc_encrypt(&message[0], &(decrypted->front()), message.size(), + &aes_key, iv_buffer, AES_DECRYPT); + return true; +} + +bool CryptoEngine::DecryptCTR(SessionContext* session, + const std::vector& iv, + size_t byte_offset, + const std::vector& cipher_data, + bool is_encrypted, + void* clear_data, + BufferType buffer_type) { + + // Check there is a content key + if (session->current_content_key() == NULL) { + LOGE("[DecryptCTR(): OEMCrypto_ERROR_NO_CONTENT_KEY]"); + return false; + } + const KeyControlBlock& control = session->current_content_key()->control(); + if (control.control_bits() & kControlDataPathSecure) { + if (buffer_type == kBufferTypeClear) { + LOGE("[DecryptCTR(): Secure key with insecure buffer]"); + return false; + } + } + if (control.control_bits() & kControlHDCPRequired) { + // For reference implementation, we do not implement any HDCP. + return false; + } + if (control.duration() > 0) { + if (control.duration() < session->CurrentTimer()) { + return false; + } + } + + const std::vector& content_key = session->current_content_key()->value(); + + // Set the AES key. + if (static_cast(content_key.size()) != AES_BLOCK_SIZE) { + LOGE("[DecryptCTR(): CONTENT_KEY has wrong size."); + return false; + } + const uint8_t* key_u8 = &content_key[0]; + AES_KEY aes_key; + if (AES_set_encrypt_key(key_u8, AES_BLOCK_SIZE * 8, &aes_key) != 0) { + LOGE("[DecryptCTR(): FAILURE]"); + return false; + } + + if (buffer_type == kBufferTypeDirect) { + // For reference implementation, we quietly drop direct video. + return true; + } + + if (buffer_type == kBufferTypeSecure) { + // For reference implementation, we also quietly drop secure data. + return true; + } + + if (! is_encrypted) { + memcpy(reinterpret_cast(clear_data), + &cipher_data[0], cipher_data.size()); + return true; + } + + // Local copy (will be modified). + uint8_t aes_iv[AES_BLOCK_SIZE]; + if (static_cast(iv.size()) != AES_BLOCK_SIZE) { + LOGE("[DecryptCTR(): FAILURE: iv has wrong length]"); + return false; + } + memcpy(aes_iv, &iv[0], AES_BLOCK_SIZE); + + // Encrypt the IV. + uint8_t ecount_buf[AES_BLOCK_SIZE]; + if (byte_offset != 0) { + // The context is needed only when not starting a new block. + AES_encrypt(aes_iv, ecount_buf, &aes_key); + ctr128_inc(aes_iv); + } + + // Decryption. + unsigned int byte_offset_cur = byte_offset; + AES_ctr128_encrypt( + &cipher_data[0], reinterpret_cast(clear_data), cipher_data.size(), + &aes_key, aes_iv, ecount_buf, &byte_offset_cur); + if (byte_offset_cur != ((byte_offset + cipher_data.size()) % AES_BLOCK_SIZE)) { + LOGE("[DecryptCTR(): FAILURE: byte offset wrong.]"); + return false; + } + return true; +} + +void NonceTable::AddNonce(uint32_t nonce) { + int new_slot = -1; + int oldest_slot = -1; + + // Flush any nonces that have been checked but not flushed. + // After flush, nonces will be either valid or invalid. + Flush(); + + for (int i = 0; i < kTableSize; ++i) { + // Increase age of all valid nonces. + if (kNTStateValid == state_[i]) { + ++age_[i]; + if (-1 == oldest_slot) { + oldest_slot = i; + } else { + if (age_[i] > age_[oldest_slot]) { + oldest_slot = i; + } + } + } else { + if (-1 == new_slot) { + age_[i] = 0; + nonces_[i] = nonce; + state_[i] = kNTStateValid; + new_slot = i; + } + } + } + if (-1 == new_slot) { + // reuse oldest + // assert (oldest_slot != -1) + int i = oldest_slot; + age_[i] = 0; + nonces_[i] = nonce; + state_[i] = kNTStateValid; + } +} + +bool NonceTable::CheckNonce(uint32_t nonce) { + for (int i = 0; i < kTableSize; ++i) { + if (kNTStateInvalid != state_[i]) { + if (nonce == nonces_[i]) { + state_[i] = kNTStateFlushPending; + return true; + } + } + } + return false; +} + +void NonceTable::Flush() { + for (int i = 0; i < kTableSize; ++i) { + if (kNTStateFlushPending == state_[i]) { + state_[i] = kNTStateInvalid; + } + } +} + +}; // namespace wvoec_mock diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h new file mode 100644 index 00000000..9e9a8d7d --- /dev/null +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h @@ -0,0 +1,232 @@ +/******************************************************************************* + * + * Copyright 2013 Google Inc. All Rights Reserved. + * + * mock implementation of OEMCrypto APIs + * + ******************************************************************************/ + +#ifndef OEMCRYPTO_ENGINE_MOCK_H_ +#define OEMCRYPTO_ENGINE_MOCK_H_ + +#include +#include +#include +#include +#include + +#include "lock.h" +#include "oemcrypto_key_mock.h" +#include "oemcrypto_keybox_mock.h" +#include "wv_cdm_types.h" + +namespace wvoec_mock { + +enum BufferType { + kBufferTypeClear, + kBufferTypeSecure, + kBufferTypeDirect +}; + +class SessionContext; +class CryptoEngine; + +typedef uint32_t SessionId; +typedef std::map ActiveSessions; + +typedef std::vector KeyId; +typedef std::map KeyMap; + +// SessionKeyTable holds the keys for the current session +class SessionKeyTable { + public: + SessionKeyTable() {} + ~SessionKeyTable(); + + bool Insert(const KeyId key_id, const Key& key_data); + Key* Find(const KeyId key_id); + void Remove(const KeyId key_id); + + private: + KeyMap keys_; + + CORE_DISALLOW_COPY_AND_ASSIGN(SessionKeyTable); +}; + +class NonceTable { + public: + static const int kTableSize = 16; + NonceTable() { + for (int i = 0; i < kTableSize; ++i) { + state_[i] = kNTStateInvalid; + } + } + ~NonceTable() {}; + void AddNonce(uint32_t nonce); + bool CheckNonce(uint32_t nonce); + void Flush(); + private: + enum NonceTableState { + kNTStateInvalid, + kNTStateValid, + kNTStateFlushPending + }; + NonceTableState state_[kTableSize]; + uint32_t age_[kTableSize]; + uint32_t nonces_[kTableSize]; +}; + +class SessionContext { + private: + SessionContext() {} + + public: + explicit SessionContext(CryptoEngine* ce, SessionId sid) + : valid_(true), ce_(ce), id_(sid), current_content_key_(NULL), + rsa_key_(NULL) {} + ~SessionContext() {} + + void Open(); + void Close(); + + bool isValid() { return valid_; } + + bool DeriveKeys(const std::vector& mac_context, + const std::vector& enc_context); + bool RSADeriveKeys(const std::vector& enc_session_key, + const std::vector& mac_context, + const std::vector& enc_context); + bool GenerateSignature(const uint8_t* message, + size_t message_length, + uint8_t* signature, + size_t* signature_length); + bool GenerateRSASignature(const uint8_t* message, + size_t message_length, + uint8_t* signature, + size_t* signature_length); + + bool ValidateMessage(const uint8_t* message, + size_t message_length, + const uint8_t* signature, + size_t signature_length); + void StartTimer(); + uint32_t CurrentTimer(); // (seconds). + bool InstallKey(const KeyId& key_id, + const std::vector& key_data, + const std::vector& key_data_iv, + const std::vector& key_control, + const std::vector& key_control_iv); + bool EncryptRSAKey(uint8_t* wrapped_rsa_key, + size_t wrapped_rsa_key_length, + uint8_t* wrapped_rsa_key_iv); + bool LoadRSAKey(const uint8_t* enc_rsa_key, + size_t enc_rsa_key_length, + const uint8_t* enc_rsa_key_iv, + const uint8_t* message, + size_t message_length, + const uint8_t* signature, + size_t signature_length); + bool ParseKeyControl(const std::vector& key_control_string, + KeyControlBlock& key_control_block); + bool RefreshKey(const KeyId& key_id, + const std::vector& key_control, + const std::vector& key_control_iv); + bool UpdateMacKey(const std::vector& mac_key, const std::vector& iv); + bool SelectContentKey(const KeyId& key_id); + const Key* current_content_key(void) {return current_content_key_;} + void set_mac_key(const std::vector& mac_key) { mac_key_ = mac_key; } + const std::vector& mac_key() { return mac_key_; } + + void set_encryption_key(const std::vector& enc_key) { + encryption_key_ = enc_key; + } + const std::vector& encryption_key() { return encryption_key_; } + + void AddNonce(uint32_t nonce); + bool CheckNonce(uint32_t nonce); + void FlushNonces(); + + private: + + bool DeriveKey(const std::vector& key, const std::vector& context, + int counter, std::vector* out); + + bool valid_; + CryptoEngine* ce_; + SessionId id_; + std::vector mac_key_; + std::vector encryption_key_; + std::vector session_key_; + const Key* current_content_key_; + SessionKeyTable session_keys_; + NonceTable nonce_table_; + RSA* rsa_key_; + time_t timer_start_; + + CORE_DISALLOW_COPY_AND_ASSIGN(SessionContext); +}; + +class CryptoEngine { + + private: + + enum CryptoEngineState { + CE_ILLEGAL, + CE_INITIALIZED, + CE_HAS_KEYBOX, + CE_HAS_SESSIONS, + CE_ERROR + }; + + public: + + CryptoEngine(); + ~CryptoEngine(); + + bool Initialized() { return (ce_state_ != CE_ILLEGAL); } + + void Terminate(); + + bool isValid() { return valid_; } + + KeyboxError ValidateKeybox(); + WvKeybox& keybox() { return keybox_; } + + SessionId CreateSession(); + + bool DestroySession(SessionId sid); + + SessionContext* FindSession(SessionId sid); + + void set_current_session_(SessionContext* current) { + current_session_ = current; + } + + bool DecryptMessage(SessionContext* session, + const std::vector& key, + const std::vector& iv, + const std::vector& message, + std::vector* decrypted); + + bool DecryptCTR(SessionContext* session, + const std::vector& iv, + size_t byte_offset, + const std::vector& cipher_data, + bool is_encrypted, + void* clear_data, + BufferType buffer_type); + + private: + + bool valid_; + CryptoEngineState ce_state_; + SessionContext* current_session_; + ActiveSessions sessions_; + WvKeybox keybox_; + wvcdm::Lock session_table_lock_; + + CORE_DISALLOW_COPY_AND_ASSIGN(CryptoEngine); +}; + +}; // namespace wvoec_eng +#endif diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.cpp b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.cpp new file mode 100644 index 00000000..27f42c3e --- /dev/null +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.cpp @@ -0,0 +1,106 @@ +/******************************************************************************* + * + * Copyright 2013 Google Inc. All Rights Reserved. + * + * mock implementation of OEMCrypto APIs + * + ******************************************************************************/ + +#include "oemcrypto_key_mock.h" + +#include + +#include "log.h" +#include "wv_cdm_constants.h" + +namespace wvoec_mock { + +bool KeyControlBlock::Validate() { + + valid_ = false; + if (0x6b63746c != verification_) { // kctl. + LOGE("KCB: BAD verification string: %08X (not %08X)", verification_, + 0x6b63746c); + return false; + } + // TODO(gmorgan): validate control bits + valid_ = true; + return valid_; +} + +// This extracts 4 bytes in network byte order to a 32 bit integer in +// host byte order. +uint32_t KeyControlBlock::ExtractField(const std::vector& str, int idx) { + int bidx = idx * 4; + uint32_t t = static_cast(str[bidx]) << 24; + t |= static_cast(str[bidx + 1]) << 16; + t |= static_cast(str[bidx + 2]) << 8; + t |= static_cast(str[bidx + 3]); + return t; +} + +bool KeyControlBlock::SetFromString(const std::vector& key_control_string) { + if (key_control_string.size() < wvcdm::KEY_CONTROL_SIZE) { + LOGE("KCB: BAD Size: %d (not %d)",key_control_string.size(), + wvcdm::KEY_CONTROL_SIZE); + return false; + } + + verification_ = ExtractField(key_control_string, 0); + duration_ = ExtractField(key_control_string, 1); + nonce_ = ExtractField(key_control_string, 2); + control_bits_ = ExtractField(key_control_string, 3); + + return Validate(); +} + +Key::Key(KeyType ktype, const std::vector& key_string, + const KeyControlBlock& control) : + valid_(true), type_(ktype), + value_(key_string), has_control_(true), + control_(control) { +} + +bool Key::setValue(const char* key_string, size_t key_string_length) { + valid_ = false; + if (!key_string || key_string_length == 0) { + return false; + } + + value_.assign(key_string, key_string + key_string_length); + + if (isValidType() && has_control_) { + valid_ = true; + } + return valid_; +} + +bool Key::setType(KeyType ktype) { + valid_ = false; + type_ = ktype; + if (value_.empty()) { + return false; + } + + if (isValidType() && has_control_) { + valid_ = true; + } + return valid_; +} + +bool Key::setControl(const KeyControlBlock& control) { + valid_ = false; + if (!control.valid()) { + return false; + } + + control_ = control; + has_control_ = true; + + if (isValidType() && !value_.empty()) { + valid_ = true; + } + return valid_; +} + +}; // namespace wvoec_eng diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.h b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.h new file mode 100644 index 00000000..d16a7076 --- /dev/null +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_key_mock.h @@ -0,0 +1,117 @@ +/******************************************************************************* + * + * Copyright 2013 Google Inc. All Rights Reserved. + * + * mock implementation of OEMCrypto APIs + * + ******************************************************************************/ + +#ifndef OEMCRYPTO_KEY_MOCK_H_ +#define OEMCRYPTO_KEY_MOCK_H_ + +#include +#include +#include + +namespace wvoec_mock { + +enum KeyType { + KEYTYPE_UNKNOWN, + KEYTYPE_PREPROV, + KEYTYPE_ROOT, + KEYTYPE_DEVICE, + KEYTYPE_CONTENT, + KEYTYPE_CONTENT_AUDIO, + KEYTYPE_CONTENT_VIDEO, + KEYTYPE_MAX +}; + +const uint32_t kControlObserveDataPath = (1<<31); +const uint32_t kControlObserveHDCP = (1<<30); +const uint32_t kControlObserveCGMS = (1<<29); +const uint32_t kControlDataPathSecure = (1<<4); +const uint32_t kControlNonceEnabled = (1<<3); +const uint32_t kControlHDCPRequired = (1<<2); +const uint32_t kControlCGMSMask = (0x03); +const uint32_t kControlCGMSCopyFreely = (0x00); +const uint32_t kControlCGMSCopyOnce = (0x02); +const uint32_t kControlCGMSCopyNever = (0x03); + +class KeyControlBlock { + public: + KeyControlBlock() {} + KeyControlBlock(const std::vector& key_control_string) { + valid_ = SetFromString(key_control_string); + } + ~KeyControlBlock() {} + + bool SetFromString(const std::vector& key_control_string); + bool Validate(); + void Invalidate() { valid_ = false; } + + bool valid() const { return valid_; } + uint32_t duration() const { return duration_; } + uint32_t nonce() const { return nonce_; } + uint32_t control_bits() const { return control_bits_; } + + private: + + uint32_t ExtractField(const std::vector& str, int idx); + + bool valid_; + uint32_t verification_; + uint32_t duration_; + uint32_t nonce_; + uint32_t control_bits_; +}; + +// AES-128 crypto key +class Key { + public: + Key() : valid_(false), type_(KEYTYPE_UNKNOWN), has_control_(false) {} + Key(const Key& key) : valid_(key.valid_), type_(key.type_), + value_(key.value_), + has_control_(key.has_control_), + control_(key.control_) {} + Key(KeyType type, const std::vector& key_string, + const KeyControlBlock& control); + + virtual ~Key() {}; + + // Key is valid iff setValue(), setType(), and setControl() have been called + bool setValue(const char* key_string, size_t key_string_length); + bool setType(KeyType ktype); + bool setControl(const KeyControlBlock& control); + bool UpdateControl(const KeyControlBlock& control) { return true; } + + KeyType keyType() { return type_; } + const std::vector& value() const { return value_; } + const KeyControlBlock& control() const { return control_; } + + bool isDeviceKey() { return (KEYTYPE_DEVICE == type_); } + bool isRootKey() { return (KEYTYPE_ROOT == type_); } + bool isPreprovKey() { return (KEYTYPE_PREPROV == type_); } + bool isContentKey() { + bool ctypes = (KEYTYPE_CONTENT == type_) || + (KEYTYPE_CONTENT_AUDIO == type_) || + (KEYTYPE_CONTENT_VIDEO == type_); + return ctypes; + } + bool isValidType() { + return ((KEYTYPE_UNKNOWN < type_) && (KEYTYPE_MAX > type_)); + } + bool isValid() { return valid_; } + + void clear() { value_.clear(); valid_ = false; } + + private: + bool valid_; + KeyType type_; + std::vector value_; + bool has_control_; + KeyControlBlock control_; +}; + +}; // namespace wvoec_eng + +#endif diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_keybox_mock.cpp b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_keybox_mock.cpp new file mode 100644 index 00000000..37bac013 --- /dev/null +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_keybox_mock.cpp @@ -0,0 +1,109 @@ +/******************************************************************************* + * + * Copyright 2013 Google Inc. All Rights Reserved. + * + * mock implementation of OEMCrypto APIs + * + ******************************************************************************/ + +#include "oemcrypto_keybox_mock.h" +#include // TODO(fredgc): Add ntoh to wv_cdm_utilities.h +#include +#include +#include +#include "log.h" +#include "wvcrc32.h" +#include "wv_keybox.h" + +namespace wvoec_mock { + +const WidevineKeybox kDefaultKeybox = { + // Sample keybox used for test vectors + { + // deviceID + 0x54, 0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x30, // TestKey01 + 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + }, { + // key + 0xfb, 0xda, 0x04, 0x89, 0xa1, 0x58, 0x16, 0x0e, + 0xa4, 0x02, 0xe9, 0x29, 0xe3, 0xb6, 0x8f, 0x04, + }, { + // data + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x10, 0x19, + 0x07, 0xd9, 0xff, 0xde, 0x13, 0xaa, 0x95, 0xc1, + 0x22, 0x67, 0x80, 0x53, 0x36, 0x21, 0x36, 0xbd, + 0xf8, 0x40, 0x8f, 0x82, 0x76, 0xe4, 0xc2, 0xd8, + 0x7e, 0xc5, 0x2b, 0x61, 0xaa, 0x1b, 0x9f, 0x64, + 0x6e, 0x58, 0x73, 0x49, 0x30, 0xac, 0xeb, 0xe8, + 0x99, 0xb3, 0xe4, 0x64, 0x18, 0x9a, 0x14, 0xa8, + 0x72, 0x02, 0xfb, 0x02, 0x57, 0x4e, 0x70, 0x64, + 0x0b, 0xd2, 0x2e, 0xf4, 0x4b, 0x2d, 0x7e, 0x39, + }, { + // magic + 0x6b, 0x62, 0x6f, 0x78, + }, { + // Crc + 0x0a, 0x7a, 0x2c, 0x35, + } +}; + +WvKeybox::WvKeybox() : valid_(false) { + Prepare(); +} + +bool WvKeybox::Prepare() { + InstallKeybox(reinterpret_cast(&kDefaultKeybox), + sizeof(kDefaultKeybox)); + valid_ = true; + return valid_; +} + +KeyboxError WvKeybox::Validate() { + if (!valid_) { + LOGE("[KEYBOX NOT LOADED]"); + return OTHER_ERROR; + } + if (strncmp(reinterpret_cast(magic_), "kbox", 4) != 0) { + LOGE("[KEYBOX HAS BAD MAGIC]"); + return BAD_MAGIC; + } + uint32_t crc_computed; + uint32_t* crc_stored = (uint32_t*)crc_; + WidevineKeybox keybox; + memset(&keybox, 0, sizeof(keybox)); + memcpy(keybox.device_id_, &device_id_[0], device_id_.size()); + memcpy(keybox.device_key_, &device_key_.value()[0], sizeof(keybox.device_key_)); + memcpy(keybox.data_, key_data_, sizeof(keybox.data_)); + memcpy(keybox.magic_, magic_, sizeof(keybox.magic_)); + + crc_computed = ntohl(wvcrc32(reinterpret_cast(&keybox), + sizeof(keybox) - 4)); // Don't include last 4 bytes. + if (crc_computed != *crc_stored) { + LOGE("[KEYBOX CRC problem: computed = %08x, stored = %08x]\n", + crc_computed, *crc_stored); + return BAD_CRC; + } + return NO_ERROR; +} + +bool WvKeybox::InstallKeybox(const uint8_t* buffer, size_t keyBoxLength) { + if (keyBoxLength != 128) { + return false; + } + + const WidevineKeybox* keybox + = reinterpret_cast(buffer); + device_id_.assign(keybox->device_id_, + keybox->device_id_ + sizeof(keybox->device_id_)); + device_key_.setValue(reinterpret_cast(keybox->device_key_), + sizeof(keybox->device_key_)); + device_key_.setType(KEYTYPE_DEVICE); + memcpy(key_data_, keybox->data_, sizeof(keybox->data_)); + memcpy(magic_, keybox->magic_, sizeof(keybox->magic_)); + memcpy(crc_, keybox->crc_, sizeof(keybox->crc_)); + return true; +} + +}; // namespace wvoec_eng diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_keybox_mock.h b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_keybox_mock.h new file mode 100644 index 00000000..b6ea72a4 --- /dev/null +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_keybox_mock.h @@ -0,0 +1,50 @@ +/******************************************************************************* + * + * Copyright 2013 Google Inc. All Rights Reserved. + * + * mock implementation of OEMCrypto APIs + * + ******************************************************************************/ + +#ifndef OEMCRYPTO_KEYBOX_MOCK_H_ +#define OEMCRYPTO_KEYBOX_MOCK_H_ + +#include "oemcrypto_key_mock.h" + +namespace wvoec_mock { + +const int DEVICE_KEY_LENGTH = 16; +typedef uint8_t WvKeyboxKey[DEVICE_KEY_LENGTH]; + +const int KEY_DATA_LENGTH = 72; +typedef uint8_t WvKeyboxKeyData[KEY_DATA_LENGTH]; + +enum KeyboxError { NO_ERROR, BAD_CRC, BAD_MAGIC, OTHER_ERROR }; + +// Widevine keybox +class WvKeybox { + public: + WvKeybox(); + ~WvKeybox() {} + + KeyboxError Validate(); + const std::vector& device_id() { return device_id_; } + Key& device_key() { return device_key_; } + const WvKeyboxKeyData& key_data() { return key_data_; } + size_t key_data_length() { return KEY_DATA_LENGTH; } + bool InstallKeybox(const uint8_t* keybox, size_t keyBoxLength); + + private: + + bool Prepare(); + + bool valid_; + std::vector device_id_; + Key device_key_; + WvKeyboxKeyData key_data_; + uint8_t magic_[4]; + uint8_t crc_[4]; +}; + +}; // namespace wvoec_eng +#endif diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp new file mode 100644 index 00000000..3a0ebf99 --- /dev/null +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp @@ -0,0 +1,893 @@ +/******************************************************************************* + * + * Copyright 2013 Google Inc. All Rights Reserved. + * + * mock implementation of OEMCrypto APIs + * + ******************************************************************************/ + +#include "OEMCryptoCENC.h" + +#include +#include +#include +#include +#include "log.h" +#include "oemcrypto_engine_mock.h" +#include "openssl/rand.h" +#include "wv_cdm_constants.h" + +namespace wvoec_mock { + +static CryptoEngine* crypto_engine = NULL; + +// Set this to true when you are generating test vectors. +const bool trace_all_calls = false; + +typedef struct { + uint8_t signature[wvcdm::MAC_KEY_SIZE]; + uint8_t context[wvcdm::MAC_KEY_SIZE]; + uint8_t iv[wvcdm::KEY_IV_SIZE]; + uint8_t enc_rsa_key[]; +} WrappedRSAKey; + +static void dump_hex(std::string name, const uint8_t* vector, size_t length) { + printf("%s = ", name.c_str()); + if (vector == NULL) { + printf("NULL;\n"); + return; + } + // TODO(fredgc): replace with HEXEncode. + for (size_t i = 0; i < length; i++) { + if (i == 0) { + printf("\n wvcdm::a2b_hex(\""); + } else if (i % 32 == 0) { + printf("\"\n \""); + } + printf("%02X", vector[i]); + } + printf("\");\n"); +} + +void dump_array_part(std::string array, size_t index, + std::string name, const uint8_t* vector, size_t length) { + if (vector == NULL) { + printf("%s[%zu].%s = NULL;\n", array.c_str(), index, name.c_str()); + return; + } + printf("std::string s%zu_", index); + dump_hex(name, vector, length); + printf("%s[%zu].%s = message_ptr + message.find(s%zu_%s.data());\n", + array.c_str(), index, name.c_str(), index, name.c_str()); +} + +extern "C" +OEMCryptoResult OEMCrypto_Initialize(void) { + if (trace_all_calls) { + printf("------------------------- OEMCrypto_Initialize(void)\n"); + } + + crypto_engine = new CryptoEngine; + + if (!crypto_engine || !crypto_engine->Initialized()) { + LOGE("[OEMCrypto_Initialize(): failed]"); + return OEMCrypto_ERROR_INIT_FAILED; + } + LOGD("[OEMCrypto_Initialize(): success]"); + return OEMCrypto_SUCCESS; +} + +extern "C" +OEMCryptoResult OEMCrypto_Terminate(void) { + if (trace_all_calls) { + printf("----------------- OEMCryptoResult OEMCrypto_Terminate(void)\n"); + } + + if (!crypto_engine) { + LOGE("[OEMCrypto_Terminate(): failed]"); + return OEMCrypto_ERROR_TERMINATE_FAILED; + } + + if (crypto_engine->Initialized()) { + crypto_engine->Terminate(); + } + + delete crypto_engine; + crypto_engine = NULL; + LOGD("[OEMCrypto_Terminate(): success]"); + return OEMCrypto_SUCCESS; +} + +extern "C" +OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session) { + if (trace_all_calls) { + printf("-- OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION *session)\n"); + } + SessionId sid = crypto_engine->CreateSession(); + *session = (OEMCrypto_SESSION)sid; + LOGD("[OEMCrypto_OpenSession(): SID=%08x]", sid); + return OEMCrypto_SUCCESS; +} + +extern "C" +OEMCryptoResult OEMCrypto_CloseSession(OEMCrypto_SESSION session) { + if (trace_all_calls) { + printf("-- OEMCryptoResult OEMCrypto_CloseSession(OEMCrypto_SESSION session)\n"); + } + if (!crypto_engine->DestroySession((SessionId)session)) { + LOGD("[OEMCrypto_CloseSession(SID=%08X): failed]", session); + return OEMCrypto_ERROR_CLOSE_SESSION_FAILED; + } else { + LOGD("[OEMCrypto_CloseSession(SID=%08X): success]", session); + return OEMCrypto_SUCCESS; + } +} + +extern "C" +OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session, + uint32_t* nonce) { + if (trace_all_calls) { + printf("-- OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session,\n"); + } + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_GenerateNonce(): ERROR_NO_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + + uint32_t nonce_value; + uint8_t* nonce_string = reinterpret_cast(&nonce_value); + + // Generate 4 bytes of random data + if (!RAND_bytes(nonce_string, 4)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + session_ctx->AddNonce(nonce_value); + *nonce = nonce_value; + if (trace_all_calls) { + printf("nonce = %08x\n", nonce_value); + } + return OEMCrypto_SUCCESS; +} + +extern "C" +OEMCryptoResult OEMCrypto_GenerateDerivedKeys(OEMCrypto_SESSION session, + const uint8_t* mac_key_context, + uint32_t mac_key_context_length, + const uint8_t* enc_key_context, + uint32_t enc_key_context_length) { + if (trace_all_calls) { + printf("-- OEMCryptoResult OEMCrypto_GenerateDerivedKeys(\n"); + dump_hex("mac_key_context", mac_key_context, (size_t)mac_key_context_length); + dump_hex("enc_key_context", enc_key_context, (size_t)enc_key_context_length); + } + if (NO_ERROR != crypto_engine->ValidateKeybox()) { + LOGE("[OEMCrypto_GenerateDerivedKeys(): ERROR_KEYBOX_INVALID]"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_GenerateDerivedKeys(): ERROR_NO_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + + const std::vector mac_ctx_str(mac_key_context, + mac_key_context + mac_key_context_length); + const std::vector enc_ctx_str(enc_key_context, + enc_key_context + enc_key_context_length); + + // Generate mac and encryption keys for current session context + if (!session_ctx->DeriveKeys(mac_ctx_str, enc_ctx_str)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (trace_all_calls) { + dump_hex("mac_key", &session_ctx->mac_key()[0], + session_ctx->mac_key().size()); + dump_hex("enc_key", &session_ctx->encryption_key()[0], + session_ctx->encryption_key().size()); + } + return OEMCrypto_SUCCESS; +} + +extern "C" +OEMCryptoResult OEMCrypto_GenerateSignature( + OEMCrypto_SESSION session, + const uint8_t* message, + size_t message_length, + uint8_t* signature, + size_t* signature_length) { + if (trace_all_calls) { + printf("-- OEMCryptoResult OEMCrypto_GenerateSignature(\n"); + dump_hex("message", message, message_length); + } + + if (NO_ERROR != crypto_engine->ValidateKeybox()) { + LOGE("[OEMCrypto_GenerateSignature(): ERROR_KEYBOX_INVALID]"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + + if (message == NULL || message_length == 0 || + signature == NULL || signature_length == 0) { + LOGE("[OEMCrypto_GenerateSignature(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_GenerateSignature(): ERROR_NO_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + + if (session_ctx->GenerateSignature(message, + message_length, + signature, + signature_length)) { + if (trace_all_calls) { + dump_hex("signature", signature, *signature_length); + } + return OEMCrypto_SUCCESS; + } + return OEMCrypto_ERROR_UNKNOWN_FAILURE;; +} + +bool RangeCheck(const uint8_t* message, + uint32_t message_length, + const uint8_t* field, + uint32_t field_length, + bool allow_null) { + if (field == NULL) return allow_null; + if (field < message) return false; + if (field + field_length > message + message_length) return false; + return true; +} + +extern "C" +OEMCryptoResult OEMCrypto_LoadKeys(OEMCrypto_SESSION session, + const uint8_t* message, + size_t message_length, + const uint8_t* signature, + size_t signature_length, + const uint8_t* enc_mac_key_iv, + const uint8_t* enc_mac_key, + size_t num_keys, + const OEMCrypto_KeyObject* key_array) { + if (trace_all_calls) { + printf("-- OEMCryptoResult OEMCrypto_LoadKeys(OEMCrypto_SESSION session,\n"); + dump_hex("message", message, message_length); + dump_hex("signature", signature, signature_length); + dump_hex("enc_mac_key_iv", enc_mac_key_iv, wvcdm::KEY_IV_SIZE); + dump_hex("enc_mac_key", enc_mac_key, wvcdm::MAC_KEY_SIZE); + for (size_t i = 0; i < num_keys; i++) { + printf("key_array[%zu].key_id_length=%zu;\n", i, key_array[i].key_id_length); + dump_array_part("key_array", i, "key_id", + key_array[i].key_id, key_array[i].key_id_length); + dump_array_part("key_array", i, "key_data_iv", + key_array[i].key_data_iv, wvcdm::KEY_IV_SIZE); + dump_array_part("key_array", i, "key_data", + key_array[i].key_data, wvcdm::KEY_IV_SIZE); + dump_array_part("key_array", i, "key_control_iv", + key_array[i].key_control_iv, wvcdm::KEY_IV_SIZE); + dump_array_part("key_array", i, "key_control", + key_array[i].key_control, wvcdm::KEY_IV_SIZE); + } + } + + if (NO_ERROR != crypto_engine->ValidateKeybox()) { + LOGE("[OEMCrypto_LoadKeys(): ERROR_KEYBOX_INVALID]"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_LoadKeys(): ERROR_NO_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + + if (message == NULL || message_length == 0 || + signature == NULL || signature_length == 0 || + key_array == NULL || num_keys == 0) { + LOGE("[OEMCrypto_LoadKeys(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + // Range check + if (!RangeCheck(message, message_length, enc_mac_key, + wvcdm::MAC_KEY_SIZE, true) || + !RangeCheck(message, message_length, enc_mac_key_iv, + wvcdm::KEY_IV_SIZE, true)) { + LOGE("[OEMCrypto_LoadKeys(): OEMCrypto_ERROR_SIGNATURE_FAILURE - range check.]"); + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + } + + for (unsigned int i = 0; i < num_keys; i++) { + if (!RangeCheck(message, message_length, key_array[i].key_id, + key_array[i].key_id_length, false) || + !RangeCheck(message, message_length, key_array[i].key_data, + wvcdm::KEY_SIZE, false) || + !RangeCheck(message, message_length, key_array[i].key_data_iv, + wvcdm::KEY_IV_SIZE, false) || + !RangeCheck(message, message_length, key_array[i].key_control, + wvcdm::KEY_CONTROL_SIZE, true) || + !RangeCheck(message, message_length, key_array[i].key_control_iv, + wvcdm::KEY_IV_SIZE, true)) { + LOGE("[OEMCrypto_LoadKeys(): OEMCrypto_ERROR_SIGNATURE_FAILURE -range check %d]", i); + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + } + } + + // Validate message signature + if (!session_ctx->ValidateMessage(message, message_length, signature, signature_length)) { + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + } + + session_ctx->StartTimer(); + + // Decrypt and install keys in key object + // Each key will have a key control block. They will all have the same nonce. + std::vector key_id; + std::vector enc_key_data; + std::vector key_data_iv; + std::vector key_control; + std::vector key_control_iv; + for (unsigned int i = 0; i < num_keys; i++) { + key_id.assign(key_array[i].key_id, + key_array[i].key_id + key_array[i].key_id_length); + enc_key_data.assign(key_array[i].key_data, + key_array[i].key_data + wvcdm::KEY_SIZE); + key_data_iv.assign(key_array[i].key_data_iv, + key_array[i].key_data_iv + wvcdm::KEY_IV_SIZE); + if (key_array[i].key_control == NULL) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + key_control.assign(key_array[i].key_control, + key_array[i].key_control + wvcdm::KEY_CONTROL_SIZE); + key_control_iv.assign(key_array[i].key_control_iv, + key_array[i].key_control_iv + wvcdm::KEY_IV_SIZE); + + if (!session_ctx->InstallKey(key_id, enc_key_data, key_data_iv, key_control, + key_control_iv)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + } + + // All keys processed. Flush nonce table + session_ctx->FlushNonces(); + + // enc_mac_key can be NULL if license renewal is not supported + if (enc_mac_key == NULL) return OEMCrypto_SUCCESS; + + // V2 license protocol: update mac key after processing license response + const std::vector enc_mac_key_str = std::vector( + enc_mac_key, enc_mac_key + wvcdm::MAC_KEY_SIZE); + const std::vector enc_mac_key_iv_str = std::vector( + enc_mac_key_iv, enc_mac_key_iv + wvcdm::KEY_IV_SIZE); + + if (!session_ctx->UpdateMacKey(enc_mac_key_str, enc_mac_key_iv_str)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + return OEMCrypto_SUCCESS; +} + +extern "C" +OEMCryptoResult OEMCrypto_RefreshKeys(OEMCrypto_SESSION session, + const uint8_t* message, + size_t message_length, + const uint8_t* signature, + size_t signature_length, + size_t num_keys, + const OEMCrypto_KeyRefreshObject* key_array) { + if (trace_all_calls) { + printf("-- OEMCryptoResult OEMCrypto_RefreshKeys(OEMCrypto_SESSION session,\n"); + } + + if (NO_ERROR != crypto_engine->ValidateKeybox()) { + LOGE("[OEMCrypto_RefreshKeys(): ERROR_KEYBOX_INVALID]"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_RefreshKeys(): ERROR_NO_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + + if (message == NULL || message_length == 0 || + signature == NULL || signature_length == 0) { + LOGE("[OEMCrypto_RefreshKeys(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + // Range check + for (unsigned int i = 0; i < num_keys; i++) { + if (!RangeCheck(message, message_length, key_array[i].key_id, + key_array[i].key_id_length, true) || + !RangeCheck(message, message_length, key_array[i].key_control, + wvcdm::KEY_CONTROL_SIZE, true) || + !RangeCheck(message, message_length, key_array[i].key_control_iv, + wvcdm::KEY_IV_SIZE, true)) { + LOGE("[OEMCrypto_RefreshKeys(): OEMCrypto_ERROR_SIGNATURE_FAILURE]"); + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + } + } + + // Validate message signature + if (!session_ctx->ValidateMessage(message, message_length, + signature, signature_length)) { + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + } + + // Decrypt and refresh keys in key refresh object + std::vector key_id; + std::vector key_control; + std::vector key_control_iv; + for (unsigned int i = 0; i < num_keys; i++) { + // TODO(gmorgan): key_id may be null if special control key type (TBS) + if (key_array[i].key_id != NULL) { + key_id.assign(key_array[i].key_id, + key_array[i].key_id + key_array[i].key_id_length); + } else { + key_id.clear(); + } + if (key_array[i].key_control != NULL) { + key_control.assign(key_array[i].key_control, + key_array[i].key_control + wvcdm::KEY_CONTROL_SIZE); + key_control_iv.assign(key_array[i].key_control_iv, + key_array[i].key_control_iv + wvcdm::KEY_IV_SIZE); + } else { + key_control.clear(); + key_control_iv.clear(); + } + + if (!session_ctx->RefreshKey(key_id, key_control, key_control_iv)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + } + session_ctx->StartTimer(); + return OEMCrypto_SUCCESS; +} + +extern "C" +OEMCryptoResult OEMCrypto_SelectKey(const OEMCrypto_SESSION session, + const uint8_t* key_id, + size_t key_id_length) { + if (trace_all_calls) { + printf("-- OEMCryptoResult OEMCrypto_SelectKey(const OEMCrypto_SESSION session,\n"); + dump_hex("key_id", key_id, key_id_length); + } + if (NO_ERROR != crypto_engine->ValidateKeybox()) { + LOGE("[OEMCrypto_SelectKey(): ERROR_KEYBOX_INVALID]"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_SelectKey(): ERROR_NO_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + + const std::vector key_id_str = std::vector(key_id, key_id + key_id_length); + if (!session_ctx->SelectContentKey(key_id_str)) { + LOGE("[OEMCrypto_SelectKey(): FAIL]"); + return OEMCrypto_ERROR_NO_CONTENT_KEY; + } + + return OEMCrypto_SUCCESS; +} + +extern "C" +OEMCryptoResult OEMCrypto_DecryptCTR(OEMCrypto_SESSION session, + const uint8_t* data_addr, + size_t data_length, + bool is_encrypted, + const uint8_t* iv, + size_t offset, + const OEMCrypto_DestBufferDesc* out_buffer) { + if (trace_all_calls) { + printf("-- OEMCryptoResult OEMCrypto_DecryptCTR(OEMCrypto_SESSION session,\n"); + } + wvoec_mock::BufferType buffer_type = kBufferTypeDirect; + void* destination = NULL; + size_t max_length = 0; + switch (out_buffer->type) { + case OEMCrypto_BufferType_Clear: + buffer_type = kBufferTypeClear; + destination = out_buffer->buffer.clear.address; + max_length = out_buffer->buffer.clear.max_length; + break; + case OEMCrypto_BufferType_Secure: + buffer_type = kBufferTypeSecure; + destination = out_buffer->buffer.secure.handle; + max_length = out_buffer->buffer.secure.max_length; + break; + default: + case OEMCrypto_BufferType_Direct: + buffer_type = kBufferTypeDirect; + destination = NULL; + break; + } + + if (buffer_type != kBufferTypeDirect && max_length < data_length) { + LOGE("[OEMCrypto_DecryptCTR(): OEMCrypto_ERROR_SHORT_BUFFER]"); + return OEMCrypto_ERROR_SHORT_BUFFER; + } + + if (NO_ERROR != crypto_engine->ValidateKeybox()) { + LOGE("[OEMCrypto_DecryptCTR(): ERROR_KEYBOX_INVALID]"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_DecryptCTR(): ERROR_NO_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + + if (data_addr == NULL || data_length == 0 || + iv == NULL || out_buffer == NULL) { + LOGE("[OEMCrypto_DecryptCTR(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + std::vector iv_v(iv, iv + 16); + std::vector content(data_addr, data_addr + data_length); + + if (!crypto_engine->DecryptCTR(session_ctx, iv_v, (int)offset, + content, is_encrypted, + destination, buffer_type)) { + LOGE("[OEMCrypto_DecryptCTR(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + return OEMCrypto_SUCCESS; +} + +extern "C" +OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t* keybox, + size_t keyBoxLength) { + if (trace_all_calls) { + printf("-- OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t *keybox,\n"); + } + if (crypto_engine->keybox().InstallKeybox(keybox, keyBoxLength)) { + return OEMCrypto_SUCCESS; + } + return OEMCrypto_ERROR_WRITE_KEYBOX; +} + +extern "C" +OEMCryptoResult OEMCrypto_IsKeyboxValid(void) { + if (trace_all_calls) { + printf("-- OEMCryptoResult OEMCrypto_IsKeyboxValid(void) {\n"); + } + switch(crypto_engine->ValidateKeybox()) { + case NO_ERROR: return OEMCrypto_SUCCESS; + case BAD_CRC: return OEMCrypto_ERROR_BAD_CRC; + case BAD_MAGIC: return OEMCrypto_ERROR_BAD_MAGIC; + default: + case OTHER_ERROR: return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +} + +extern "C" +OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID, + size_t* idLength) { + if (trace_all_calls) { + printf("-- OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID,\n"); + } + std::vector dev_id_string = crypto_engine->keybox().device_id(); + if (dev_id_string.empty()) { + LOGE("[OEMCrypto_GetDeviceId(): Keybox Invalid]"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + + size_t dev_id_len = dev_id_string.size(); + if (*idLength < dev_id_len) { + *idLength = dev_id_len; + LOGE("[OEMCrypto_GetDeviceId(): ERROR_SHORT_BUFFER]"); + return OEMCrypto_ERROR_SHORT_BUFFER; + } + memset(deviceID, 0, *idLength); + memcpy(deviceID, &dev_id_string[0], dev_id_len); + *idLength = dev_id_len; + LOGD("[OEMCrypto_GetDeviceId(): success]"); + return OEMCrypto_SUCCESS; +} + +extern "C" +OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData, + size_t* keyDataLength) { + if (trace_all_calls) { + printf("-- OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData,\n"); + } + size_t length = crypto_engine->keybox().key_data_length(); + if (*keyDataLength < length) { + *keyDataLength = length; + LOGE("[OEMCrypto_GetKeyData(): ERROR_SHORT_BUFFER]"); + return OEMCrypto_ERROR_SHORT_BUFFER; + } + memset(keyData, 0, *keyDataLength); + memcpy(keyData, crypto_engine->keybox().key_data(), length); + *keyDataLength = length; + LOGD("[OEMCrypto_GetKeyData(): success]"); + return OEMCrypto_SUCCESS; +} + +extern "C" +OEMCryptoResult OEMCrypto_GetRandom(uint8_t* randomData, size_t dataLength) { + if (trace_all_calls) { + printf("-- OEMCryptoResult OEMCrypto_GetRandom(uint8_t* randomData, size_t dataLength) {\n"); + } + if (!randomData) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (RAND_bytes(randomData, dataLength)) { + return OEMCrypto_SUCCESS; + } + return OEMCrypto_ERROR_UNKNOWN_FAILURE; +} + +extern "C" +OEMCryptoResult OEMCrypto_WrapKeybox(const uint8_t* keybox, + size_t keyBoxLength, + uint8_t* wrappedKeybox, + size_t* wrappedKeyBoxLength, + const uint8_t* transportKey, + size_t transportKeyLength) { + if (trace_all_calls) { + printf("-- OEMCryptoResult OEMCrypto_WrapKeybox(const uint8_t *keybox,\n"); + } + if (!keybox || !wrappedKeybox || !wrappedKeyBoxLength + || (keyBoxLength != *wrappedKeyBoxLength)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + // This implementation ignores the transport key. For test keys, we + // don't need to encrypt the keybox. + memcpy(wrappedKeybox, keybox, keyBoxLength); + return OEMCrypto_SUCCESS; +} + +extern "C" +OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session, + const uint8_t* message, + size_t message_length, + const uint8_t* signature, + size_t signature_length, + uint32_t* nonce, + const uint8_t* enc_rsa_key, + size_t enc_rsa_key_length, + const uint8_t* enc_rsa_key_iv, + uint8_t* wrapped_rsa_key, + size_t* wrapped_rsa_key_length) { + if (trace_all_calls) { + printf("-- OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey()\n"); + dump_hex("message", message, message_length); + dump_hex("signature", signature, signature_length); + printf("nonce = %08X;\n", *nonce); + dump_hex("enc_rsa_key", enc_rsa_key, enc_rsa_key_length); + dump_hex("enc_rsa_key_iv", enc_rsa_key_iv, wvcdm::KEY_IV_SIZE); + } + if (wrapped_rsa_key_length == NULL) { + LOGE("[OEMCrypto_RewrapDeviceRSAKey(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + // For the reference implementation, the wrapped key and the encrypted + // key are the same size -- just encrypted with different keys. + // We add 32 bytes for a context, and 32 bytes for a signature. + // Important: This layout must match OEMCrypto_LoadDeviceRSAKey below. + size_t buffer_size = enc_rsa_key_length + sizeof(WrappedRSAKey); + + if (wrapped_rsa_key == NULL || *wrapped_rsa_key_length < buffer_size) { + LOGW("[OEMCrypto_RewrapDeviceRSAKey(): ERROR_KEYBOX_INVALID]"); + *wrapped_rsa_key_length = buffer_size; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + *wrapped_rsa_key_length = buffer_size; // Tell caller how much space we used. + if (NO_ERROR != crypto_engine->ValidateKeybox()) { + LOGE("[OEMCrypto_RewrapDeviceRSAKey(): ERROR_KEYBOX_INVALID]"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_RewrapDeviceRSAKey(): ERROR_NO_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + if (message == NULL || message_length == 0 || signature == NULL + || signature_length == 0 || nonce == NULL || enc_rsa_key == NULL) { + LOGE("[OEMCrypto_RewrapDeviceRSAKey(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + // Range check + if (!RangeCheck(message, message_length, reinterpret_cast(nonce), + sizeof(uint32_t), true) || + !RangeCheck(message, message_length, enc_rsa_key, enc_rsa_key_length, + true) || + !RangeCheck(message, message_length, enc_rsa_key_iv, wvcdm::KEY_IV_SIZE, + true)) { + LOGE("[OEMCrypto_LoadKeys(): OEMCrypto_ERROR_SIGNATURE_FAILURE - range check.]"); + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + } + + + // Validate nonce + if (!session_ctx->CheckNonce(*nonce)) { + return OEMCrypto_ERROR_INVALID_NONCE; + } + session_ctx->FlushNonces(); + + // Decrypt key and verify signature. + if (!session_ctx->LoadRSAKey(enc_rsa_key, enc_rsa_key_length, + enc_rsa_key_iv, message, message_length, + signature, signature_length)) { + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + // return OEMCrypto_ERROR_INVALID_RSA_KEY; + } + + // Now we generate a wrapped keybox. + WrappedRSAKey* wrapped = reinterpret_cast(wrapped_rsa_key); + // Pick a random context and IV for generating keys. + if (!RAND_bytes(wrapped->context, sizeof(wrapped->context))) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!RAND_bytes(wrapped->iv, sizeof(wrapped->iv))) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + const std::vector mac_ctx_str(wrapped->context, + wrapped->context + sizeof(wrapped->context)); + // Generate mac and encryption keys for encrypting the signature. + if (!session_ctx->DeriveKeys(mac_ctx_str, mac_ctx_str)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + size_t sig_length = sizeof(wrapped->signature); + if (!session_ctx->GenerateSignature(wrapped->context, // start signing here. + buffer_size - sizeof(wrapped->signature), + wrapped->signature, + &sig_length)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + // Encrypt rsa key with keybox. + if (!session_ctx->EncryptRSAKey(wrapped->enc_rsa_key, + enc_rsa_key_length, + wrapped->iv)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + if (trace_all_calls) { + dump_hex("wrapped_rsa_key", wrapped_rsa_key, *wrapped_rsa_key_length); + } + return OEMCrypto_SUCCESS; +} + +extern "C" +OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(OEMCrypto_SESSION session, + const uint8_t* wrapped_rsa_key, + size_t wrapped_rsa_key_length) { + if (trace_all_calls) { + printf("-- OEMCryptoResult OEMCrypto_LoadDeviceRSAKey()\n"); + dump_hex("wrapped_rsa_key", wrapped_rsa_key, wrapped_rsa_key_length); + } + + if (wrapped_rsa_key == NULL) { + LOGE("[OEMCrypto_LoadDeviceRSAKey(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + if (NO_ERROR != crypto_engine->ValidateKeybox()) { + LOGE("[OEMCrypto_RewrapDeviceRSAKey(): ERROR_KEYBOX_INVALID]"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_RewrapDeviceRSAKey(): ERROR_NO_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + // Now we generate a wrapped keybox. + const WrappedRSAKey* wrapped + = reinterpret_cast(wrapped_rsa_key); + const std::vector mac_ctx_str(wrapped->context, + wrapped->context + sizeof(wrapped->context)); + // Generate mac and encryption keys for encrypting the signature. + if (!session_ctx->DeriveKeys(mac_ctx_str, mac_ctx_str)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + // Decrypt key and verify signature. + if (!session_ctx->LoadRSAKey(wrapped->enc_rsa_key, + wrapped_rsa_key_length - sizeof(WrappedRSAKey), + wrapped->iv, + wrapped->context, + wrapped_rsa_key_length - sizeof(wrapped->signature), + wrapped->signature, + sizeof(wrapped->signature))) { + return OEMCrypto_ERROR_INVALID_RSA_KEY; + } + return OEMCrypto_SUCCESS; +} + +extern "C" +OEMCryptoResult OEMCrypto_GenerateRSASignature(OEMCrypto_SESSION session, + const uint8_t* message, + size_t message_length, + uint8_t* signature, + size_t* signature_length) { + if (trace_all_calls) { + printf("-- OEMCryptoResult OEMCrypto_GenerateRSASignature()\n"); + dump_hex("message", message, message_length); + } + if (NO_ERROR != crypto_engine->ValidateKeybox()) { + LOGE("[OEMCrypto_GenerateRSASignature(): ERROR_KEYBOX_INVALID]"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + + if (message == NULL || message_length == 0 || + signature == NULL || signature_length == 0) { + LOGE("[OEMCrypto_GenerateRSASignature(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_GenerateRSASignature(): ERROR_NO_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + if (session_ctx->GenerateRSASignature(message, + message_length, + signature, + signature_length)) { + if (trace_all_calls) { + dump_hex("signature", signature, *signature_length); + } + return OEMCrypto_SUCCESS; + } + return OEMCrypto_ERROR_UNKNOWN_FAILURE;; +} + +extern "C" +OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey( + OEMCrypto_SESSION session, + const uint8_t* enc_session_key, + size_t enc_session_key_length, + const uint8_t* mac_key_context, + size_t mac_key_context_length, + const uint8_t* enc_key_context, + size_t enc_key_context_length) { + if (trace_all_calls) { + printf("-- OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey(\n"); + dump_hex("enc_session_key", enc_session_key, enc_session_key_length); + dump_hex("mac_key_context", mac_key_context, (size_t)mac_key_context_length); + dump_hex("enc_key_context", enc_key_context, (size_t)enc_key_context_length); + } + if (NO_ERROR != crypto_engine->ValidateKeybox()) { + LOGE("[OEMCrypto_GenerateDerivedKeys(): ERROR_KEYBOX_INVALID]"); + return OEMCrypto_ERROR_KEYBOX_INVALID; + } + + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_GenerateDerivedKeys(): ERROR_NO_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + + const std::vector ssn_key_str(enc_session_key, + enc_session_key + enc_session_key_length); + const std::vector mac_ctx_str(mac_key_context, + mac_key_context + mac_key_context_length); + const std::vector enc_ctx_str(enc_key_context, + enc_key_context + enc_key_context_length); + + // Generate mac and encryption keys for current session context + if (!session_ctx->RSADeriveKeys(ssn_key_str, mac_ctx_str, enc_ctx_str)) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (trace_all_calls) { + dump_hex("mac_key", &session_ctx->mac_key()[0], + session_ctx->mac_key().size()); + dump_hex("enc_key", &session_ctx->encryption_key()[0], + session_ctx->encryption_key().size()); + } + return OEMCrypto_SUCCESS; +} + +}; // namespace wvoec_mock diff --git a/libwvdrmengine/oemcrypto/mock/src/string_conversions.cpp b/libwvdrmengine/oemcrypto/mock/src/string_conversions.cpp new file mode 100644 index 00000000..4fe9cc8a --- /dev/null +++ b/libwvdrmengine/oemcrypto/mock/src/string_conversions.cpp @@ -0,0 +1,91 @@ +// Copyright 2013 Google Inc. All Rights Reserved. + +#include "string_conversions.h" + +#include +#include +#include + +#include "log.h" + +namespace wvcdm { + +static bool CharToDigit(char ch, unsigned char* digit) { + if (ch >= '0' && ch <= '9') { + *digit = ch - '0'; + } else { + ch = tolower(ch); + if ((ch >= 'a') && (ch <= 'f')) { + *digit = ch - 'a' + 10; + } else { + return false; + } + } + return true; +} + +// converts an ascii hex string(2 bytes per digit) into a decimal byte string +std::vector a2b_hex(const std::string& byte) { + std::vector array(0); + unsigned int count = byte.size(); + if (count == 0 || (count % 2) != 0) { + LOGE("Invalid input size %u for string %s", count, byte.c_str()); + return array; + } + + for (unsigned int i = 0; i < count / 2; ++i) { + unsigned char msb = 0; // most significant 4 bits + unsigned char lsb = 0; // least significant 4 bits + if (!CharToDigit(byte[i * 2], &msb) || + !CharToDigit(byte[i * 2 + 1], &lsb)) { + LOGE("Invalid hex value %c%c at index %d", byte[i*2], byte[i*2+1], i); + return array; + } + array.push_back((msb << 4) | lsb); + } + return array; +} + +std::string b2a_hex(const std::vector& byte) { + return HexEncode(&byte[0], byte.size()); +} + +std::string HexEncode(const uint8_t* in_buffer, unsigned int size) { + static const char kHexChars[] = "0123456789ABCDEF"; + + // Each input byte creates two output hex characters. + std::string out_buffer(size * 2, '\0'); + + for (unsigned int i = 0; i < size; ++i) { + char byte = in_buffer[i]; + out_buffer[(i << 1)] = kHexChars[(byte >> 4) & 0xf]; + out_buffer[(i << 1) + 1] = kHexChars[byte & 0xf]; + } + return out_buffer; +} + +std::string IntToString(int value) { + // log10(2) ~= 0.3 bytes needed per bit or per byte log10(2**8) ~= 2.4. + // So round up to allocate 3 output characters per byte, plus 1 for '-'. + const int kOutputBufSize = 3 * sizeof(int) + 1; + char buffer[kOutputBufSize]; + memset(buffer, 0, kOutputBufSize); + snprintf(buffer, kOutputBufSize, "%d", value); + + std::string out_string(buffer, sizeof(buffer)); + return out_string; +} + +std::string UintToString(unsigned int value) { + // log10(2) ~= 0.3 bytes needed per bit or per byte log10(2**8) ~= 2.4. + // So round up to allocate 3 output characters per byte. + const int kOutputBufSize = 3 * sizeof(unsigned int); + char buffer[kOutputBufSize]; + memset(buffer, 0, kOutputBufSize); + snprintf(buffer, kOutputBufSize, "%u", value); + + std::string out_string(buffer, sizeof(buffer)); + return out_string; +} + +}; // namespace wvcdm diff --git a/libwvdrmengine/oemcrypto/mock/src/string_conversions.h b/libwvdrmengine/oemcrypto/mock/src/string_conversions.h new file mode 100644 index 00000000..bf41eb9d --- /dev/null +++ b/libwvdrmengine/oemcrypto/mock/src/string_conversions.h @@ -0,0 +1,20 @@ +// Copyright 2013 Google Inc. All Rights Reserved. + +#ifndef OEMCRYPTO_STRING_CONVERSIONS_H_ +#define OEMCRYPTO_STRING_CONVERSIONS_H_ + +#include +#include + +namespace wvcdm { + +std::vector a2b_hex(const std::string& b); +std::string b2a_hex(const std::vector& b); +std::string HexEncode(const uint8_t* bytes, unsigned size); +std::string IntToString(int value); +std::string UintToString(unsigned int value); + + +}; // namespace wvcdm + +#endif // OEMCRYPTO_STRING_CONVERSIONS_H_ diff --git a/libwvdrmengine/oemcrypto/mock/src/wv_cdm_constants.h b/libwvdrmengine/oemcrypto/mock/src/wv_cdm_constants.h new file mode 100644 index 00000000..b47e690a --- /dev/null +++ b/libwvdrmengine/oemcrypto/mock/src/wv_cdm_constants.h @@ -0,0 +1,14 @@ +// Copyright 2013 Google Inc. All Rights Reserved. + +#ifndef OEMCRYPTO_WV_CDM_CONSTANTS_H_ +#define OEMCRYPTO_WV_CDM_CONSTANTS_H_ + +namespace wvcdm { +static const size_t KEY_CONTROL_SIZE = 16; +static const size_t KEY_IV_SIZE = 16; +static const size_t KEY_PAD_SIZE = 16; +static const size_t KEY_SIZE = 16; +static const size_t MAC_KEY_SIZE = 32; +} // namespace wvcdm + +#endif // OEMCRYPTO_WV_CDM_CONSTANTS_H_ diff --git a/libwvdrmengine/oemcrypto/mock/src/wv_cdm_types.h b/libwvdrmengine/oemcrypto/mock/src/wv_cdm_types.h new file mode 100644 index 00000000..bd3e21d6 --- /dev/null +++ b/libwvdrmengine/oemcrypto/mock/src/wv_cdm_types.h @@ -0,0 +1,49 @@ +// Copyright 2013 Google Inc. All Rights Reserved. + +#ifndef OEMCRYPTO_WV_CDM_TYPES_H_ +#define OEMCRYPTO_WV_CDM_TYPES_H_ + +#include +#include +#include + +namespace wvcdm { + +typedef std::string CdmKeySystem; +typedef std::string CdmInitData; +typedef std::string CdmKeyMessage; +typedef std::string CdmKeyResponse; +typedef std::string KeyId; +typedef std::string CdmSessionId; +typedef std::string RequestId; +typedef uint32_t CryptoResult; +typedef uint32_t CryptoSessionId; +typedef std::string CryptoKeyId; + +enum CdmResponseType { + NO_ERROR, + UNKNOWN_ERROR, + KEY_ADDED, + KEY_ERROR, + KEY_MESSAGE, + NEED_KEY, + KEY_CANCELED, +}; + +#define CORE_DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) + +enum CdmEventType { + LICENSE_EXPIRED, + LICENSE_RENEWAL_NEEDED +}; + +// forward class references +class KeyMessage; +class Request; +class Key; + +} // namespace wvcdm + +#endif // OEMCRYPTO_WV_CDM_TYPES_H_ diff --git a/libwvdrmengine/oemcrypto/mock/src/wv_keybox.h b/libwvdrmengine/oemcrypto/mock/src/wv_keybox.h new file mode 100644 index 00000000..06a53353 --- /dev/null +++ b/libwvdrmengine/oemcrypto/mock/src/wv_keybox.h @@ -0,0 +1,24 @@ +// Copyright 2013 Google Inc. All Rights Reserved. + +#ifndef WV_KEYBOX_H_ +#define WV_KEYBOX_H_ + +namespace wvoec_mock { + +// This is the format of a Widevine keybox. +typedef struct { // 128 bytes total. + // C character string identifying the device. Null terminated. + uint8_t device_id_[32]; + // 128 bit AES key assigned to device. Generated by Widevine. + uint8_t device_key_[16]; + // Key Data. Encrypted data. + uint8_t data_[72]; + // Constant code used to recognize a valid keybox "kbox" = 0x6b626f78. + uint8_t magic_[4]; + // The CRC checksum of the first 124 bytes of the keybox. + uint8_t crc_[4]; +} WidevineKeybox; + +} + +#endif // WV_KEYBOX_H_ diff --git a/libwvdrmengine/oemcrypto/mock/src/wvcrc.cpp b/libwvdrmengine/oemcrypto/mock/src/wvcrc.cpp new file mode 100644 index 00000000..79d5fe05 --- /dev/null +++ b/libwvdrmengine/oemcrypto/mock/src/wvcrc.cpp @@ -0,0 +1,93 @@ +/********************************************************************* + * wvcrc32.cpp + * + * (c) Copyright 2011-2012 Google, Inc. + * + * Compte CRC32 Checksum. Needed for verification of WV Keybox. + *********************************************************************/ + +#include "wvcrc32.h" + +#define INIT_CRC32 0xffffffff + +uint32_t wvrunningcrc32(uint8_t* p_begin, int i_count, uint32_t i_crc) { + static uint32_t CRC32[256] = { + 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, + 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005, + 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, + 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, + 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, + 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, + 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, + 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd, + 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, + 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, + 0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, + 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, + 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, + 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, + 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, + 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, + 0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, + 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, + 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, + 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, + 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, + 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, + 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066, + 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, + 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, + 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692, + 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, + 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, + 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, + 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, + 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, + 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a, + 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, + 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, + 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, + 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, + 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, + 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b, + 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, + 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, + 0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, + 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, + 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, + 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, + 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, + 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, + 0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, + 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, + 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, + 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, + 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, + 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, + 0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30, + 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, + 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, + 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, + 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, + 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, + 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, + 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, + 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, + 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c, + 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, + 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 + }; + + /* Calculate the CRC */ + while (i_count > 0) { + i_crc = (i_crc << 8) ^ CRC32[(i_crc >> 24) ^ ((uint32_t) * p_begin) ]; + p_begin++; + i_count--; + } + + return(i_crc); +} + +uint32_t wvcrc32(uint8_t* p_begin, int i_count) { + return(wvrunningcrc32(p_begin, i_count, INIT_CRC32)); +} diff --git a/libwvdrmengine/oemcrypto/mock/src/wvcrc32.h b/libwvdrmengine/oemcrypto/mock/src/wvcrc32.h new file mode 100644 index 00000000..94e3f7e9 --- /dev/null +++ b/libwvdrmengine/oemcrypto/mock/src/wvcrc32.h @@ -0,0 +1,16 @@ +/********************************************************************* + * wvcrc32.h + * + * (c) Copyright 2011-2012 Google, Inc. + * + * Compte CRC32 Checksum. Needed for verification of WV Keybox. + *********************************************************************/ + +#ifndef WV_CRC_32_H_ +#define WV_CRC_32_H_ + +#include + +uint32_t wvcrc32(uint8_t* p_begin, int i_count); + +#endif // WV_CRC_32_H_ diff --git a/libwvdrmengine/oemcrypto/oemcrypto.gyp b/libwvdrmengine/oemcrypto/oemcrypto.gyp new file mode 100644 index 00000000..a9a7024a --- /dev/null +++ b/libwvdrmengine/oemcrypto/oemcrypto.gyp @@ -0,0 +1,128 @@ +# Copyright 2012 Google Inc. All Rights Reserved. +# Author: rkuroiwa@google.com (Rintaro Kuroiwa) + +{ + 'variables': { + 'chromium_code': 1, + 'oec_target_type': "", + }, + 'targets': [ + { + 'target_name': 'oec_lib', + 'type': 'none', + 'conditions': [ + ['target_arch=="arm" and oec_target_type != "CAN_INSTALL_KEYBOX"', { + 'dependencies': [ + 'oec_mrvl', + ], + 'libraries': [ + ], + }, { + 'dependencies': [ + 'oec_mock', + ], + }], + ], + }, + { + 'target_name': 'oec_client', + 'type': 'static_library', + 'sources': [ + 'client/oemcrypto_client.h', + 'client/oemcrypto_client.cpp', + ], + 'dependencies': [ + '../../../base/base.gyp:base', + ], + 'include_dirs': [ + 'client', + '../include/widevine', + '../core/include', + ], + }, + { + 'target_name': 'oec_mock', + 'type': 'static_library', + 'conditions': [ + [ 'use_openssl==1', { + 'sources!': [ + 'mock/src/encryptor_nss.cpp', + ], + }, { + 'sources!': [ + 'mock/src/encryptor_openssl.cpp', + ], + },], + ], + 'sources': [ + 'mock/src/oemcrypto_mock.cpp', + 'mock/src/oemcrypto_engine_mock.cpp', + 'mock/src/oemcrypto_engine_mock.h', + 'mock/src/oemcrypto_key_mock.cpp', + 'mock/src/oemcrypto_key_mock.h', + 'mock/src/oemcrypto_keybox_mock.cpp', + 'mock/src/oemcrypto_keybox_mock.h', + 'mock/src/encryptor.h', + 'mock/src/encryptor.cpp', + 'mock/src/encryptor_nss.cpp', + 'mock/src/encryptor_openssl.cpp', + 'mock/src/cmac.h', + 'mock/src/cmac.c', + ], + 'dependencies': [ + '../../../base/base.gyp:base', + '../../../crypto/crypto.gyp:crypto', + ], + 'include_dirs': [ + 'mock/src', + '../include', + '../core/include', + ], + }, + { + 'target_name': 'oec_mrvl', + 'type': 'static_library', + 'sources': [ + 'eureka/src/oemcrypto_mrvl.cpp', + ], + 'dependencies': [ + '../../../base/base.gyp:base', + '../../../crypto/crypto.gyp:crypto', + ], + 'include_dirs': [ + '../include', + '../core/include', + ], + 'cflags': [ + '-Wsign-conversion', + ], + 'link_settings': { + 'libraries': [ + '-lOSAL', + '-lPEAgent', + ], + }, + }, + { + 'target_name': 'oec_unittest', + 'type': '<(gtest_target_type)', + 'conditions': [ + ['target_arch!="arm" or oec_target_type == "CAN_INSTALL_KEYBOX"', { + 'defines': [ 'CAN_INSTALL_KEYBOX', ], + },], + ], + 'sources': [ + 'test/oemcrypto_test.cpp', + ], + 'include_dirs': [ + '../include', + '../../../testing/gtest/include', + ], + 'dependencies': [ + 'oec_lib', + '../../../base/base.gyp:base', + '../../../testing/gtest.gyp:gtest', + ], + }, + ], +} diff --git a/libwvdrmengine/oemcrypto/test/Android.mk b/libwvdrmengine/oemcrypto/test/Android.mk new file mode 100644 index 00000000..7ef20f49 --- /dev/null +++ b/libwvdrmengine/oemcrypto/test/Android.mk @@ -0,0 +1,39 @@ +LOCAL_PATH:= $(call my-dir) + +# THIS IS FOR THE MOCK TESTS: +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + oemcrypto_test.cpp \ + oemcrypto_keybox_test.cpp + +LOCAL_MODULE_TAGS := tests + +# Define CAN_INSTALL_KEYBOX and the unit test will install a known keybox and test decryption. +LOCAL_CFLAGS += -DCAN_INSTALL_KEYBOX + +LOCAL_C_INCLUDES += \ + bionic \ + external/gtest/include \ + external/openssl/include \ + external/stlport/stlport \ + $(LOCAL_PATH)/../include \ + $(LOCAL_PATH)/../mock/src \ + +LOCAL_STATIC_LIBRARIES := \ + libgtest \ + libgtest_main \ + +LOCAL_SHARED_LIBRARIES := \ + libcrypto \ + libcutils \ + libdl \ + liblog \ + liboemcrypto \ + libstlport \ + libutils \ + libz \ + +LOCAL_MODULE:=oemcrypto_test + +include $(BUILD_EXECUTABLE) diff --git a/libwvdrmengine/oemcrypto/test/Makefile b/libwvdrmengine/oemcrypto/test/Makefile new file mode 100644 index 00000000..87f27b05 --- /dev/null +++ b/libwvdrmengine/oemcrypto/test/Makefile @@ -0,0 +1,109 @@ +# +# Builds oemcrypto_unittests +# +#PROJECTS_ROOT = ~projects +# +ifndef PROJECTS_ROOT +PROJECTS_ROOT = ../../../../.. +endif + +CDM_ROOT = $(PROJECTS_ROOT)/cdm +CDM_SRC_PATH = $(CDM_ROOT)/cdm +CDM_BASE_INCLUDE_PATH = $(CDM_SRC_PATH)/include + +EUREKA_ROOT = $(PROJECTS_ROOT)/eureka/eureka +CHROME_ROOT = $(EUREKA_ROOT)/src/chromium/src +# +# build outputs should go into Chrome repository, such as ../chromium/src/out +# or some local equivalent. +# WARNING: splitting outputs from CHROME_ROOT can lead to build errors +ifndef CHROME_ROOT +CHROME_ROOT = $(CDM_ROOT)/out +endif + +# TARGET_PLATFORM from {x86,eureka} +ifndef TARGET_PLATFORM +TARGET_PLATFORM = x86 +endif + +# TARGET_BUILD from {debug,release} +ifndef TARGET_BUILD +TARGET_BUILD = debug +endif + +ifeq ($(TARGET_PLATFORM),x86) + BUILDPLATFORM = out_x86_linux +else ifeq ($(TARGET_PLATFORM),eureka) + BUILDPLATFORM = out_arm_eureka +else + BUILDPLATFORM = UNKNOWN +endif + +ifeq ($(TARGET_BUILD),debug) + BUILDTYPE = Debug +else ifeq ($(TARGET_BUILD),release) + BUILDTYPE = Release +else + BUILDTYPE = UNKNOWN +endif + +BUILDPATH = $(CHROME_ROOT)/$(BUILDPLATFORM)/$(BUILDTYPE) +OBJPATH = $(BUILDPATH)/obj + +CHROME_THIRD_PARTY_LIBS = $(BUILDPATH)/obj/third_party + +# target image file name +TARGET_TEST_EXE = oemcrypto_unittests + +TARGET_OBJECTS = oemcrypto_test.o + +OBJECTDIR = $(OBJPATH)/oemcrypto_unittests + +INSTALLDIR = $(BUILDPATH) + +LIBGTEST_INCLUDE = $(CDM_SRC_PATH)/prebuilt/gtest/include +LIBGTEST_LIBS = $(CDM_SRC_PATH)/prebuilt/gtest/$(BUILDPLATFORM)/$(BUILDTYPE)/lib +LIBGTEST_LIBNAME = gtest + +INCLUDES = \ + -I$(LIBGTEST_INCLUDE) \ + -I$(CDM_BASE_INCLUDE_PATH) + +LIBDIRS = \ + -L$(INSTALLDIR) \ + -L$(LIBGTEST_LIBS) + +OBJECTS := $(patsubst %.o,$(OBJECTDIR)/%.o,$(TARGET_OBJECTS)) + +CXXFLAGS = -m64 -fPIC -W -Wall -g -DCDM_TEST +LINK = $(CXX) +MKDIR = mkdir -p + +$(INSTALLDIR)/$(TARGET_TEST_EXE): $(OBJECTDIR) $(INSTALLDIR) $(OBJECTS) + $(CXX) -v -fPIC -m64 $(OBJECTS) $(LIBDIRS) -loemcrypto_mock \ + -lcrypto -ldl -lrt -lpthread -l$(LIBGTEST_LIBNAME) -o $@ + @echo "[Unit test image: " $(INSTALLDIR)/$(TARGET_TEST_EXE) "]" + +$(OBJECTDIR)/%.o: %.cpp + $(CXX) -c $(CXXFLAGS) $(INCLUDES) $< -o $@ + +$(OBJECTDIR)/%.o: %.cc + $(CXX) -c $(CXXFLAGS) $(INCLUDES) $< -o $@ + +clean: + $(RM) -rf $(OBJECTDIR) + $(RM) -rf $(INSTALLDIR)/$(TARGET_TEST_EXE) + +$(OBJECTDIR): + @$(MKDIR) $@ + +$(INSTALLDIR): + @$(MKDIR) $@ + +.PHONY: $(OBJECTDIR) + +.PHONY: $(INSTALLDIR) + +.PHONY: clean + +.PHONY: test diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_keybox_test.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_keybox_test.cpp new file mode 100644 index 00000000..e0db67bd --- /dev/null +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_keybox_test.cpp @@ -0,0 +1,170 @@ +// Copyright 2013 Google Inc. All Rights Reserved. + +// +// OEMCrypto unit tests +// +#include +#include +#include +#include +#include + +#include "OEMCryptoCENC.h" +#include "string_conversions.h" +#include "wv_cdm_constants.h" +#include "wv_keybox.h" + +using namespace std; + +namespace wvoec { + +static wvoec_mock::WidevineKeybox kValidKeybox02 = { + // Sample keybox used for test vectors + { + // deviceID + 0x54, 0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x30, // TestKey02 + 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + }, { + // key + 0x76, 0x5d, 0xce, 0x01, 0x04, 0x89, 0xb3, 0xd0, + 0xdf, 0xce, 0x54, 0x8a, 0x49, 0xda, 0xdc, 0xb6, + }, { + // data + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x10, 0x19, + 0x92, 0x27, 0x0b, 0x1f, 0x1a, 0xd5, 0xc6, 0x93, + 0x19, 0x3f, 0xaa, 0x74, 0x1f, 0xdd, 0x5f, 0xb4, + 0xe9, 0x40, 0x2f, 0x34, 0xa4, 0x92, 0xf4, 0xae, + 0x9a, 0x52, 0x39, 0xbc, 0xb7, 0x24, 0x38, 0x13, + 0xab, 0xf4, 0x92, 0x96, 0xc4, 0x81, 0x60, 0x33, + 0xd8, 0xb8, 0x09, 0xc7, 0x55, 0x0e, 0x12, 0xfa, + 0xa8, 0x98, 0x62, 0x8a, 0xec, 0xea, 0x74, 0x8a, + 0x4b, 0xfa, 0x5a, 0x9e, 0xb6, 0x49, 0x0d, 0x80, + }, { + // magic + 0x6b, 0x62, 0x6f, 0x78, + }, { + // Crc + 0x2a, 0x3b, 0x3e, 0xe4, + } +}; + +static wvoec_mock::WidevineKeybox kValidKeybox03 = { + // Sample keybox used for test vectors + { + // deviceID + 0x54, 0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x30, // TestKey03 + 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + }, { + // key + 0x25, 0xe5, 0x2a, 0x02, 0x29, 0x68, 0x04, 0xa2, + 0x92, 0xfd, 0x7c, 0x67, 0x0b, 0x67, 0x1f, 0x31, + }, { + // data + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x10, 0x19, + 0xf4, 0x0a, 0x0e, 0xa2, 0x0a, 0x71, 0xd5, 0x92, + 0xfa, 0xa3, 0x25, 0xc6, 0x4b, 0x76, 0xf1, 0x64, + 0xf4, 0x60, 0xa0, 0x30, 0x72, 0x23, 0xbe, 0x03, + 0xcd, 0xde, 0x7a, 0x06, 0xd4, 0x01, 0xeb, 0xdc, + 0xe0, 0x50, 0xc0, 0x53, 0x0a, 0x50, 0xb0, 0x37, + 0xe5, 0x05, 0x25, 0x0e, 0xa4, 0xc8, 0x5a, 0xff, + 0x46, 0x6e, 0xa5, 0x31, 0xf3, 0xdd, 0x94, 0xb7, + 0xe0, 0xd3, 0xf9, 0x04, 0xb2, 0x54, 0xb1, 0x64, + }, { + // magic + 0x6b, 0x62, 0x6f, 0x78, + }, { + // Crc + 0xa1, 0x99, 0x5f, 0x46, + } +}; + +// Define CAN_INSTALL_KEYBOX if you are compiling with the reference +// implementation of OEMCrypto, or if your version of OEMCrypto supports +// OEMCrypto_InstallKeybox and OEMCrypto_WrapKeybox. +#if defined(CAN_INSTALL_KEYBOX) +// The Below tests are based on a specific keybox which is installed for testing. + +class OEMCryptoKeyboxTest : public ::testing::Test { + + protected: + virtual void SetUp() { + ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Initialize()) + << "OEMCrypto_Initialize failed."; + } + + void install_keybox(wvoec_mock::WidevineKeybox& keybox) { + OEMCryptoResult sts; + uint8_t wrapped[sizeof(wvoec_mock::WidevineKeybox)]; + size_t length = sizeof(wvoec_mock::WidevineKeybox); + sts = OEMCrypto_WrapKeybox(reinterpret_cast(&keybox), + sizeof(keybox), + wrapped, + &length, + NULL, 0); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + sts = OEMCrypto_InstallKeybox(wrapped, sizeof(keybox)); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + } + + virtual void TearDown() { + ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Terminate()) + << "OEMCrypto_Terminate failed."; + } + public: + +}; + +TEST_F(OEMCryptoKeyboxTest, DefaultKeybox) { + OEMCryptoResult sts; + sts = OEMCrypto_IsKeyboxValid(); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); +} + +TEST_F(OEMCryptoKeyboxTest, GoodKeybox) { + wvoec_mock::WidevineKeybox keybox = kValidKeybox02; + OEMCryptoResult sts; + install_keybox(keybox); + sts = OEMCrypto_IsKeyboxValid(); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + + keybox = kValidKeybox03; + install_keybox(keybox); + sts = OEMCrypto_IsKeyboxValid(); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); +} + +TEST_F(OEMCryptoKeyboxTest, BadCRCKeybox) { + wvoec_mock::WidevineKeybox keybox = kValidKeybox02; + keybox.crc_[1] = 42; + OEMCryptoResult sts; + install_keybox(keybox); + sts = OEMCrypto_IsKeyboxValid(); + ASSERT_EQ(OEMCrypto_ERROR_BAD_CRC, sts); +} + +TEST_F(OEMCryptoKeyboxTest, BadMagicKeybox) { + wvoec_mock::WidevineKeybox keybox = kValidKeybox02; + keybox.magic_[1] = 42; + OEMCryptoResult sts; + install_keybox(keybox); + sts = OEMCrypto_IsKeyboxValid(); + ASSERT_EQ(OEMCrypto_ERROR_BAD_MAGIC, sts); +} + + +TEST_F(OEMCryptoKeyboxTest, BadDataKeybox) { + wvoec_mock::WidevineKeybox keybox = kValidKeybox02; + keybox.data_[1] = 42; + OEMCryptoResult sts; + install_keybox(keybox); + sts = OEMCrypto_IsKeyboxValid(); + ASSERT_EQ(OEMCrypto_ERROR_BAD_CRC, sts); +} + +#endif // CAN_INSTALL_KEYBOX + +} // namespace wvoec diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp new file mode 100644 index 00000000..0aa7ebfa --- /dev/null +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp @@ -0,0 +1,1401 @@ +// Copyright 2013 Google Inc. All Rights Reserved. + +// +// OEMCrypto unit tests +// +#include // TODO(fredgc): Add ntoh to wv_cdm_utilities.h +#include +#include +#include +#include +#include +#include +#include + +#include "OEMCryptoCENC.h" +#include "oemcrypto_key_mock.h" +#include "openssl/aes.h" +#include "openssl/cmac.h" +#include "openssl/hmac.h" +#include "openssl/rand.h" +#include "openssl/sha.h" +#include "string_conversions.h" +#include "wv_cdm_constants.h" +#include "wv_keybox.h" + +using namespace std; + +namespace wvoec { + +typedef struct { + uint8_t verification[4]; + uint32_t duration; + uint32_t nonce; + uint32_t control_bits; +} KeyControlBlock; + +const size_t kTestKeyIdLength = 12; // pick a length. any length. +typedef struct { + uint8_t key_id[kTestKeyIdLength]; + uint8_t key_data[wvcdm::KEY_SIZE]; + uint8_t key_iv[wvcdm::KEY_IV_SIZE]; + uint8_t control_iv[wvcdm::KEY_IV_SIZE]; + KeyControlBlock control; +} MessageKeyData; + +template +struct MessageData { + MessageKeyData keys[kNumberKeys]; + uint8_t mac_key_iv[wvcdm::KEY_IV_SIZE]; + uint8_t mac_key[wvcdm::MAC_KEY_SIZE]; +}; + +const wvoec_mock::WidevineKeybox kDefaultKeybox = { + // Sample keybox used for test vectors + { + // deviceID + 0x54, 0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x30, // TestKey01 + 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + }, { + // key + 0xfb, 0xda, 0x04, 0x89, 0xa1, 0x58, 0x16, 0x0e, + 0xa4, 0x02, 0xe9, 0x29, 0xe3, 0xb6, 0x8f, 0x04, + }, { + // data + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x10, 0x19, + 0x07, 0xd9, 0xff, 0xde, 0x13, 0xaa, 0x95, 0xc1, + 0x22, 0x67, 0x80, 0x53, 0x36, 0x21, 0x36, 0xbd, + 0xf8, 0x40, 0x8f, 0x82, 0x76, 0xe4, 0xc2, 0xd8, + 0x7e, 0xc5, 0x2b, 0x61, 0xaa, 0x1b, 0x9f, 0x64, + 0x6e, 0x58, 0x73, 0x49, 0x30, 0xac, 0xeb, 0xe8, + 0x99, 0xb3, 0xe4, 0x64, 0x18, 0x9a, 0x14, 0xa8, + 0x72, 0x02, 0xfb, 0x02, 0x57, 0x4e, 0x70, 0x64, + 0x0b, 0xd2, 0x2e, 0xf4, 0x4b, 0x2d, 0x7e, 0x39, + }, { + // magic + 0x6b, 0x62, 0x6f, 0x78, + }, { + // Crc + 0x0a, 0x7a, 0x2c, 0x35, + } +}; + +class OEMCryptoClientTest : public ::testing::Test { + + protected: + + class Session; + + OEMCryptoClientTest() : alive_(false) {} + + bool init() { + OEMCryptoResult result; + if (!alive_) { + result = OEMCrypto_Initialize(); + alive_ = (OEMCrypto_SUCCESS == result); + } + return alive_; + } + + bool terminate() { + OEMCryptoResult result; + result = OEMCrypto_Terminate(); + if (OEMCrypto_SUCCESS == result) { + alive_ = false; + } + return !alive_; + } + + void testSetUp() { + init(); + } + + void InstallKeybox(const wvoec_mock::WidevineKeybox& keybox) { + OEMCryptoResult sts; + uint8_t wrapped[sizeof(wvoec_mock::WidevineKeybox)]; + size_t length = sizeof(wvoec_mock::WidevineKeybox); + sts = OEMCrypto_WrapKeybox(reinterpret_cast(&keybox), + sizeof(keybox), + wrapped, + &length, + NULL, 0); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + sts = OEMCrypto_InstallKeybox(wrapped, sizeof(keybox)); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + } + + void testTearDown() { + destroySessions(); + terminate(); + } + + bool validateKeybox() { + OEMCryptoResult result; + result = OEMCrypto_IsKeyboxValid(); + return (OEMCrypto_SUCCESS == result); + } + + class Session { + public: + Session() : valid_(false), open_(false) {} + + Session(string sname) : valid_(true), open_(false), sname_(sname), + mac_key_(wvcdm::MAC_KEY_SIZE), + enc_key_(wvcdm::KEY_SIZE) {} + + bool isValid() { return valid_; } + bool isOpen() { return open_; } + bool successStatus() { return (OEMCrypto_SUCCESS == session_status_); } + OEMCryptoResult getStatus() { return session_status_; } + uint32_t getNonce() { return nonce_; } + + uint32_t session_id() { return (uint32_t)session_id_; } + void set_session_id(uint32_t newsession) { + session_id_ = (OEMCrypto_SESSION)newsession; + } + + void open() { + EXPECT_TRUE(valid_ && !open_); + session_status_ = OEMCrypto_OpenSession(&session_id_); + if (OEMCrypto_SUCCESS == session_status_) { + open_ = true; + } + } + + void close() { + EXPECT_TRUE(valid_); + session_status_ = OEMCrypto_CloseSession(session_id_); + if (OEMCrypto_SUCCESS == session_status_) { + open_ = false; + } + } + + bool GenerateNonce(uint32_t* nonce) { + OEMCryptoResult sts; + sts = OEMCrypto_GenerateNonce(session_id(), nonce); + return (OEMCrypto_SUCCESS == sts); + } + + bool GenerateDerivedKeys() { + if( !GenerateNonce(&nonce_) ) { + cout << "Generate Nonce failed." << endl; + return false; + } + vector mac_context = wvcdm::a2b_hex( + "41555448454e5449434154494f4e000a4c08001248000000020000101907d9ff" + "de13aa95c122678053362136bdf8408f8276e4c2d87ec52b61aa1b9f646e5873" + "4930acebe899b3e464189a14a87202fb02574e70640bd22ef44b2d7e3912250a" + "230a14080112100915007caa9b5931b76a3a85f046523e10011a093938373635" + "34333231180120002a0c31383836373837343035000000000100"); + vector enc_context = wvcdm::a2b_hex( + "454e4352595054494f4e000a4c08001248000000020000101907d9ffde13aa95" + "c122678053362136bdf8408f8276e4c2d87ec52b61aa1b9f646e58734930aceb" + "e899b3e464189a14a87202fb02574e70640bd22ef44b2d7e3912250a230a1408" + "0112100915007caa9b5931b76a3a85f046523e10011a09393837363534333231" + "180120002a0c31383836373837343035000000000080"); + OEMCryptoResult sts; + sts = OEMCrypto_GenerateDerivedKeys( + session_id(), + &mac_context[0], mac_context.size(), + &enc_context[0], enc_context.size()); + if (sts != OEMCrypto_SUCCESS) { + cout << "GenerateDerivedKeys: Failed OEMCrypto_GenerateDerivedKeys with " + << static_cast(sts) << endl; + } + mac_key_ = wvcdm::a2b_hex( + "9D41F0A77A76E071841C33B06104D106641421E651FBE55F0AED453CDA7713AC"); + enc_key_ = wvcdm::a2b_hex("D0BFC35DA9E33436E81C4229E78CB9F4"); + return (OEMCrypto_SUCCESS == sts); + } + + bool LoadTestKeys() { + if (!GenerateDerivedKeys()) { + cout << "LoadTestKeys: Failed GenerateDerivedKeys" << endl; + return false; + } + + return CreateAndLoadKeyMessage(); + } + + bool CertificateProvision(vector* wrappedKey) { + vector context = wvcdm::a2b_hex( + "0a4c08001248000000020000101907d9ffde13aa95c122678053362136bdf840" + "8f8276e4c2d87ec52b61aa1b9f646e58734930acebe899b3e464189a14a87202" + "fb02574e70640bd22ef44b2d7e3912250a230a14080112100915007caa9b5931" + "b76a3a85f046523e10011a09393837363534333231180120002a0c3138383637" + "38373430350000"); + + OEMCryptoResult sts; + + // Generate signature + size_t gen_signature_length = 0; + sts = OEMCrypto_GenerateSignature(session_id(), &context[0], + context.size(), NULL, + &gen_signature_length); + uint8_t* gen_signature = new uint8_t[gen_signature_length]; + sts = OEMCrypto_GenerateSignature(session_id(), &context[0], + context.size(), gen_signature, + &gen_signature_length); + + if (sts != OEMCrypto_SUCCESS) { + return false; + } + + // Rewrap Canned Response + + // In the real world, the signature above would just have been used to + // contact the certificate provisioning server to get this response. + // TODO: This is not a valid test vector + vector message = wvcdm::a2b_hex( + "000000001248000000020000101907d9ffde13aa95c122678053362136bdf840" + "8f8276e4c2d87ec52b61aa1b9f646e58734930acebe899b3e464189a14a87202" + "fb02574e70640bd22ef44b2d7e3912250a230a14080112100915007caa9b5931" + "b76a3a85f046523e10011a09393837363534333231180120002a0c3138383637" + "0a4c08001248000000020000101907d9ffde13aa95c122678053362136bdf840" + "8f8276e4c2d87ec52b61aa1b9f646e58734930acebe899b3e464189a14a87202" + "fb02574e70640bd22ef44b2d7e3912250a230a14080112100915007caa9b5931" + "b76a3a85f046523e10011a09393837363534333231180120002a0c3138383637" + "0a4c08001248000000020000101907d9ffde13aa95c122678053362136bdf840" + "8f8276e4c2d87ec52b61aa1b9f646e58734930acebe899b3e464189a14a87202" + "fb02574e70640bd22ef44b2d7e3912250a230a14080112100915007caa9b5931" + "b76a3a85f046523e10011a09393837363534333231180120002a0c3138383637" + "0a4c08001248000000020000101907d9ffde13aa95c122678053362136bdf840" + "8f8276e4c2d87ec52b61aa1b9f646e58734930acebe899b3e464189a14a87202" + "fb02574e70640bd22ef44b2d7e3912250a230a14080112100915007caa9b5931" + "b76a3a85f046523e10011a09393837363534333231180120002a0c3138383637" + "0a4c08001248000000020000101907d9ffde13aa95c122678053362136bdf840" + "8f8276e4c2d87ec52b61aa1b9f646e58734930acebe899b3e464189a14a87202" + "fb02574e70640bd22ef44b2d7e3912250a230a14080112100915007caa9b5931" + "b76a3a85f046523e10011a09393837363534333231180120002a0c3138383637" + "0a4c08001248000000020000101907d9ffde13aa95c122678053362136bdf840" + "8f8276e4c2d87ec52b61aa1b9f646e58734930acebe899b3e464189a14a87202" + "fb02574e70640bd22ef44b2d7e3912250a230a14080112100915007caa9b5931" + "b76a3a85f046523e10011a09393837363534333231180120002a0c3138383637"); + + memcpy(&message[0], &nonce_, sizeof(uint32_t)); + uint32_t* messageNonce = reinterpret_cast(&message[0]); + uint8_t* key = &message[32]; + size_t key_length = 7; + uint8_t* key_iv = &message[64]; + + // TODO: In practice, we need to generate a signature here after inserting + // the right nonce into the message. + vector signature = wvcdm::a2b_hex( + "0a4c08001248000000020000101907d9ffde13aa95c122678053362136bdf840"); + + size_t wrapped_key_length; + + sts = OEMCrypto_RewrapDeviceRSAKey(session_id(), &message[0], + message.size(), &signature[0], + signature.size(), messageNonce, key, + key_length, key_iv, NULL, + &wrapped_key_length); + wrappedKey->clear(); + wrappedKey->resize(wrapped_key_length); + sts = OEMCrypto_RewrapDeviceRSAKey(session_id(), &message[0], + message.size(), &signature[0], + signature.size(), messageNonce, key, + key_length, key_iv, + &(wrappedKey->front()), + &wrapped_key_length); + delete[] gen_signature; + return (sts == OEMCrypto_SUCCESS); + } + + bool CertificateLicense() { + OEMCryptoResult sts; + vector wrappedKey; + + if (!CertificateProvision(&wrappedKey)) { + return false; + } + + // Load the Wrapped Key + sts = OEMCrypto_LoadDeviceRSAKey(session_id(), &wrappedKey[0], + wrappedKey.size()); + if (sts != OEMCrypto_SUCCESS) { + return false; + } + + // Sign a Message + vector licenseRequest = wvcdm::a2b_hex( + "ba711a51e0c4c995440c28057f7f5e2f2e9c3a1edeb7549aca21e6050b059ac8" + "6ad64ec1a528eef17b4f5ce781af488d50fb0e60d04b48c78d55847a4e14243c" + "0023c553b46a2f53995870f351295e3aa2237f153f1415e817ad23e662e547b1" + "4708b303473813f93ee192353ff22bee54dd0f558bbe4b61b75b387bc310e9d6" + "8ff2cb3482689c0688570809b756dba4c2697be3132a2da782aa877ed64d8c7d" + "506525a382bad14d7e797c256c3617c22fa4165482b9742e9b54ffb6c52eda1d"); + size_t signature_length = 0; + + sts = OEMCrypto_GenerateRSASignature(session_id(), &licenseRequest[0], + licenseRequest.size(), NULL, + &signature_length); + uint8_t* signature = new uint8_t[signature_length]; + sts = OEMCrypto_GenerateRSASignature(session_id(), &licenseRequest[0], + licenseRequest.size(), signature, + &signature_length); + if (sts != OEMCrypto_SUCCESS) { + return false; + } + + // Rewrap Canned Response + + // In the real world, the signature above would just have been used to contact + // the license server to get this response. + // TODO: This is not a valid test vector + vector sessionKey = wvcdm::a2b_hex( + "6fa479c731d2770b6a61a5d1420bb9d1"); + vector mac_context = wvcdm::a2b_hex( + "41555448454e5449434154494f4e000a4c08001248000000020000101907d9ff" + "de13aa95c122678053362136bdf8408f8276e4c2d87ec52b61aa1b9f646e5873" + "4930acebe899b3e464189a14a87202fb02574e70640bd22ef44b2d7e3912250a" + "230a14080112100915007caa9b5931b76a3a85f046523e10011a093938373635" + "34333231180120002a0c31383836373837343035000000000100"); + vector enc_context = wvcdm::a2b_hex( + "454e4352595054494f4e000a4c08001248000000020000101907d9ffde13aa95" + "c122678053362136bdf8408f8276e4c2d87ec52b61aa1b9f646e58734930aceb" + "e899b3e464189a14a87202fb02574e70640bd22ef44b2d7e3912250a230a1408" + "0112100915007caa9b5931b76a3a85f046523e10011a09393837363534333231" + "180120002a0c31383836373837343035000000000080"); + + sts = OEMCrypto_DeriveKeysFromSessionKey(session_id(), + &sessionKey[0], + sessionKey.size(), + &mac_context[0], + mac_context.size(), + &enc_context[0], + enc_context.size()); + if (sts != OEMCrypto_SUCCESS) { + return false; + } + + delete[] signature; + return CreateAndLoadKeyMessage(); + } + + template + void fill_simple_message(MessageData* data) { + OEMCrypto_GetRandom(data->mac_key_iv, wvcdm::KEY_IV_SIZE); + OEMCrypto_GetRandom(data->mac_key, wvcdm::KEY_IV_SIZE); + for (unsigned int i = 0; i < kNumberKeys; i++) { + memset(data->keys[i].key_id, i, kTestKeyIdLength); + OEMCrypto_GetRandom(data->keys[i].key_data, wvcdm::KEY_SIZE); + OEMCrypto_GetRandom(data->keys[i].key_iv, wvcdm::KEY_IV_SIZE); + OEMCrypto_GetRandom(data->keys[i].control_iv, wvcdm::KEY_IV_SIZE); + memcpy(data->keys[i].control.verification, "kctl", 4); + data->keys[i].control.duration = htonl(0); + data->keys[i].control.nonce = htonl(nonce_); + data->keys[i].control.control_bits = htonl(0); + } + // For the canned decryption content, The first key is: + vector key = wvcdm::a2b_hex("39AD33E5719656069F9EDE9EBBA7A77D"); + memcpy(data->keys[0].key_data, &key[0], key.size()); + + } + + template + void encrypt_message(const MessageData& data, + MessageData* encrypted) { + *encrypted = data; + + uint8_t iv_buffer[16]; + memcpy(iv_buffer, &data.mac_key_iv[0], wvcdm::KEY_IV_SIZE); + AES_KEY aes_key; + AES_set_encrypt_key(&enc_key_[0], 128, &aes_key); + AES_cbc_encrypt(&data.mac_key[0], &encrypted->mac_key[0], + wvcdm::MAC_KEY_SIZE, &aes_key, iv_buffer, AES_ENCRYPT); + + for (unsigned int i = 0; i < kNumberKeys; i++) { + memcpy(iv_buffer, &data.keys[i].control_iv[0], wvcdm::KEY_IV_SIZE); + AES_set_encrypt_key(&data.keys[i].key_data[0], 128, &aes_key); + AES_cbc_encrypt(reinterpret_cast(&data.keys[i].control), + reinterpret_cast(&encrypted->keys[i].control), + wvcdm::KEY_SIZE, &aes_key, iv_buffer, AES_ENCRYPT); + + memcpy(iv_buffer, &data.keys[i].key_iv[0], wvcdm::KEY_IV_SIZE); + AES_set_encrypt_key(&enc_key_[0], 128, &aes_key); + AES_cbc_encrypt(&data.keys[i].key_data[0], &encrypted->keys[i].key_data[0], + wvcdm::KEY_SIZE, &aes_key, iv_buffer, AES_ENCRYPT); + } + + } + + template + void sign_message(MessageData data, + std::vector* signature) { + signature->resize(SHA256_DIGEST_LENGTH); + unsigned int md_len = SHA256_DIGEST_LENGTH; + HMAC(EVP_sha256(), &mac_key_[0], SHA256_DIGEST_LENGTH, + reinterpret_cast(&data), sizeof(data), + &(signature->front()), &md_len); + } + + template + void fill_key_array(const MessageData& data, + OEMCrypto_KeyObject* key_array) { + for (unsigned int i = 0; i < kNumberKeys; i++) { + key_array[i].key_id = data.keys[i].key_id; + key_array[i].key_id_length = kTestKeyIdLength; + key_array[i].key_data_iv = data.keys[i].key_iv; + key_array[i].key_data = data.keys[i].key_data; + key_array[i].key_control_iv = data.keys[i].control_iv; + key_array[i].key_control + = reinterpret_cast(&data.keys[i].control); + } + } + + private: + bool CreateAndLoadKeyMessage() { + const unsigned int num_keys = 3; + MessageData data; + fill_simple_message(&data); + MessageData encrypted; + encrypt_message(data, &encrypted); + std::vector signature; + sign_message(encrypted, &signature); + OEMCrypto_KeyObject key_array[num_keys]; + + const uint8_t* message_ptr = reinterpret_cast(&encrypted); + fill_key_array(encrypted, key_array); + + OEMCryptoResult sts = OEMCrypto_LoadKeys( + session_id(), + message_ptr, sizeof(encrypted), + &signature[0], signature.size(), + encrypted.mac_key_iv, + encrypted.mac_key, + num_keys, key_array); + if (sts != OEMCrypto_SUCCESS) { + cout << "LoadTestKeys: Failed OEMCrypto_LoadKeys with " + << static_cast(sts) << endl; + } + return sts == OEMCrypto_SUCCESS; + } + + bool valid_; + bool open_; + string sname_; + OEMCrypto_SESSION session_id_; + OEMCryptoResult session_status_; + vector mac_key_; + vector enc_key_; + uint32_t nonce_; + }; + + static Session badSession; + + Session& findSession(string sname) { + map::iterator it = _sessions.find(sname); + if (it != _sessions.end()) { + return it->second; + } + return badSession; + } + + Session& createSession(string sname) { + Session temp(sname); + _sessions.insert(pair(sname, temp)); + return findSession(sname); + } + + bool destroySession(string sname) { + Session& temp = findSession(sname); + if (!temp.isValid()) { + return false; + } + _sessions.erase(sname); + return true; + } + + bool destroySessions() { + _sessions.clear(); + return true; + } + + const uint8_t* find(const vector& message, + const vector& substring) { + vector::const_iterator pos = search(message.begin(), message.end(), + substring.begin(), substring.end()); + if (pos == message.end()) { + return NULL; + } + return &(*pos); + } + + private: + bool alive_; + map _sessions; +}; + +OEMCryptoClientTest::Session OEMCryptoClientTest::badSession; + +/////////////////////////////////////////////////// +// initialization tests +/////////////////////////////////////////////////// + +TEST_F(OEMCryptoClientTest, NormalInitTermination) { + bool success; + success = init(); + EXPECT_TRUE(success); + success = terminate(); + ASSERT_TRUE(success); +} + +/////////////////////////////////////////////////// +// Keybox Tests +/////////////////////////////////////////////////// + +TEST_F(OEMCryptoClientTest, KeyboxValid) { + bool success; + success = init(); + EXPECT_TRUE(success); + success = validateKeybox(); + ASSERT_TRUE(success); + success = terminate(); + ASSERT_TRUE(success); +} + +TEST_F(OEMCryptoClientTest, NormalGetDeviceId) { + testSetUp(); + + OEMCryptoResult sts; + uint8_t dev_id[128]; + size_t dev_id_len = 128; + sts = OEMCrypto_GetDeviceID(dev_id, &dev_id_len); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + // cout << "NormalGetDeviceId: dev_id = " << dev_id + // << " len = " << dev_id_len << endl; + + testTearDown(); +} + +TEST_F(OEMCryptoClientTest, GetDeviceIdShortBuffer) { + testSetUp(); + + OEMCryptoResult sts; + uint8_t dev_id[128]; + uint32_t req_len = 11; + for (int i = 0; i < 128; ++i) { + dev_id[i] = 0x55; + } + dev_id[127] = '\0'; + size_t dev_id_len = req_len; + sts = OEMCrypto_GetDeviceID(dev_id, &dev_id_len); + // cout << "GetDeviceIdShortBuffer: sts = " << (int)sts << " request = " + // << req_len << " required = " << dev_id_len << endl; + + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); + + // On short buffer error, function should return minimum buffer length + ASSERT_TRUE(dev_id_len > req_len); + + // cout << "NormalGetDeviceId: dev_id = " << dev_id + // << " len = " << dev_id_len << endl; + + testTearDown(); +} + +TEST_F(OEMCryptoClientTest, NormalGetKeyData) { + testSetUp(); + + OEMCryptoResult sts; + uint8_t key_data[256]; + uint32_t req_len = 256; + size_t key_data_len = req_len; + sts = OEMCrypto_GetKeyData(key_data, &key_data_len); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + + testTearDown(); +} + +/////////////////////////////////////////////////// +// Session Tests +/////////////////////////////////////////////////// + +TEST_F(OEMCryptoClientTest, NormalSessionOpenClose) { + Session& s = createSession("ONE"); + testSetUp(); + + s.open(); + ASSERT_TRUE(s.successStatus()); + ASSERT_TRUE(s.isOpen()); + + s.close(); + ASSERT_TRUE(s.successStatus()); + ASSERT_FALSE(s.isOpen()); + + testTearDown(); +} + +TEST_F(OEMCryptoClientTest, TwoSessionsOpenClose) { + Session& s1 = createSession("ONE"); + Session& s2 = createSession("TWO"); + testSetUp(); + + s1.open(); + ASSERT_TRUE(s1.successStatus()); + ASSERT_TRUE(s1.isOpen()); + + s2.open(); + ASSERT_TRUE(s2.successStatus()); + ASSERT_TRUE(s2.isOpen()); + + s1.close(); + ASSERT_TRUE(s1.successStatus()); + ASSERT_FALSE(s1.isOpen()); + + s2.close(); + ASSERT_TRUE(s2.successStatus()); + ASSERT_FALSE(s2.isOpen()); + + testTearDown(); +} + +TEST_F(OEMCryptoClientTest, EightSessionsOpenClose) { + Session& s1 = createSession("ONE"); + Session& s2 = createSession("TWO"); + Session& s3 = createSession("THREE"); + Session& s4 = createSession("FOUR"); + Session& s5 = createSession("FIVE"); + Session& s6 = createSession("SIX"); + Session& s7 = createSession("SEVEN"); + Session& s8 = createSession("EIGHT"); + testSetUp(); + + s1.open(); + ASSERT_TRUE(s1.successStatus()); + ASSERT_TRUE(s1.isOpen()); + + s2.open(); + ASSERT_TRUE(s2.successStatus()); + ASSERT_TRUE(s2.isOpen()); + + s3.open(); + ASSERT_TRUE(s3.successStatus()); + ASSERT_TRUE(s3.isOpen()); + + s4.open(); + ASSERT_TRUE(s4.successStatus()); + ASSERT_TRUE(s4.isOpen()); + + s5.open(); + ASSERT_TRUE(s5.successStatus()); + ASSERT_TRUE(s5.isOpen()); + + s6.open(); + ASSERT_TRUE(s6.successStatus()); + ASSERT_TRUE(s6.isOpen()); + + s7.open(); + ASSERT_TRUE(s7.successStatus()); + ASSERT_TRUE(s7.isOpen()); + + s8.open(); + ASSERT_TRUE(s8.successStatus()); + ASSERT_TRUE(s8.isOpen()); + + s1.close(); + ASSERT_TRUE(s1.successStatus()); + ASSERT_FALSE(s1.isOpen()); + + s8.close(); + ASSERT_TRUE(s8.successStatus()); + ASSERT_FALSE(s8.isOpen()); + + s3.close(); + ASSERT_TRUE(s3.successStatus()); + ASSERT_FALSE(s3.isOpen()); + + s6.close(); + ASSERT_TRUE(s6.successStatus()); + ASSERT_FALSE(s6.isOpen()); + + s5.close(); + ASSERT_TRUE(s5.successStatus()); + ASSERT_FALSE(s5.isOpen()); + + s4.close(); + ASSERT_TRUE(s4.successStatus()); + ASSERT_FALSE(s4.isOpen()); + + s7.close(); + ASSERT_TRUE(s7.successStatus()); + ASSERT_FALSE(s7.isOpen()); + + s2.close(); + ASSERT_TRUE(s2.successStatus()); + ASSERT_FALSE(s2.isOpen()); + + testTearDown(); +} + +/////////////////////////////////////////////////// +// AddKey Tests +/////////////////////////////////////////////////// + +TEST_F(OEMCryptoClientTest, GenerateNonce) { + Session& s = createSession("ONE"); + testSetUp(); + s.open(); + uint32_t nonce; + + ASSERT_TRUE(s.GenerateNonce(&nonce)); + std::cout << "GenerateNonce:: nonce=" << nonce << std::endl; + + s.close(); + ASSERT_TRUE(s.successStatus()); + ASSERT_FALSE(s.isOpen()); + testTearDown(); +} + +TEST_F(OEMCryptoClientTest, GenerateTwoNonces) { + Session& s = createSession("ONE"); + testSetUp(); + s.open(); + uint32_t nonce1; + uint32_t nonce2; + + ASSERT_TRUE(s.GenerateNonce(&nonce1)); + ASSERT_TRUE(s.GenerateNonce(&nonce2)); + std::cout << "GenerateNonce:: nonce1=" << nonce1 << std::endl; + std::cout << "GenerateNonce:: nonce2=" << nonce2 << std::endl; + + ASSERT_TRUE(nonce1 != nonce2); + + s.close(); + ASSERT_TRUE(s.successStatus()); + ASSERT_FALSE(s.isOpen()); + testTearDown(); +} + +TEST_F(OEMCryptoClientTest, GenerateDerivedKeys) { + Session& s = createSession("ONE"); + testSetUp(); + s.open(); + + ASSERT_TRUE(s.GenerateDerivedKeys()); + + s.close(); + ASSERT_TRUE(s.successStatus()); + ASSERT_FALSE(s.isOpen()); + testTearDown(); +} + +TEST_F(OEMCryptoClientTest, GenerateSignature) { + Session& s = createSession("ONE"); + testSetUp(); + s.open(); + + ASSERT_TRUE(s.GenerateDerivedKeys()); + + vector context = wvcdm::a2b_hex( + "0a4c08001248000000020000101907d9ffde13aa95c122678053362136bdf840" + "8f8276e4c2d87ec52b61aa1b9f646e58734930acebe899b3e464189a14a87202" + "fb02574e70640bd22ef44b2d7e3912250a230a14080112100915007caa9b5931" + "b76a3a85f046523e10011a09393837363534333231180120002a0c3138383637" + "38373430350000"); + + static const uint32_t SignatureBufferMaxLength = 256; + uint8_t signature[SignatureBufferMaxLength]; + size_t signature_length = SignatureBufferMaxLength; + + OEMCryptoResult sts; + sts = OEMCrypto_GenerateSignature( + s.session_id(), + &context[0], context.size(), signature, &signature_length); + + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + + static const uint32_t SignatureExpectedLength = 32; + ASSERT_EQ(signature_length, SignatureExpectedLength); + + std::string expected_signature = + "281C3ECC8BD1CAFA5786329471043A388E04AF97B3133D9A8F9B102FBD3CAF1E"; + ASSERT_EQ(expected_signature, wvcdm::HexEncode(signature, signature_length)); + + + s.close(); + ASSERT_TRUE(s.successStatus()); + ASSERT_FALSE(s.isOpen()); + testTearDown(); +} + +// Define CAN_INSTALL_KEYBOX if you are compiling with the reference +// implementation of OEMCrypto, or if your version of OEMCrypto supports +// OEMCrypto_InstallKeybox and OEwith a clear keybox. +// The Below tests are based on a specific keybox which is installed for testing. +#if defined(CAN_INSTALL_KEYBOX) + +TEST_F(OEMCryptoClientTest, LoadKeyNoNonce) { + testSetUp(); + InstallKeybox(kDefaultKeybox); + Session& s = createSession("ONE"); + s.open(); + ASSERT_TRUE(s.GenerateDerivedKeys()); + ASSERT_TRUE(s.LoadTestKeys()); + s.close(); + testTearDown(); +} + +TEST_F(OEMCryptoClientTest, LoadKeyWithNonce) { + testSetUp(); + InstallKeybox(kDefaultKeybox); + Session& s = createSession("ONE"); + s.open(); + + ASSERT_TRUE(s.GenerateDerivedKeys()); + + const unsigned int num_keys = 3; + MessageData data; + s.fill_simple_message(&data); + data.keys[0].control.control_bits = wvoec_mock::kControlNonceEnabled; + data.keys[1].control.control_bits = wvoec_mock::kControlNonceEnabled; + data.keys[2].control.control_bits = wvoec_mock::kControlNonceEnabled; + + MessageData encrypted; + s.encrypt_message(data, &encrypted); + std::vector signature; + s.sign_message(encrypted, &signature); + OEMCrypto_KeyObject key_array[num_keys]; + const uint8_t* message_ptr = reinterpret_cast(&encrypted); + s.fill_key_array(encrypted, key_array); + + OEMCryptoResult sts = OEMCrypto_LoadKeys( + s.session_id(), + message_ptr, sizeof(encrypted), + &signature[0], signature.size(), + encrypted.mac_key_iv, + encrypted.mac_key, + num_keys, key_array); + + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + + s.close(); + testTearDown(); +} + +TEST_F(OEMCryptoClientTest, LoadKeyWithBadNonce) { + testSetUp(); + InstallKeybox(kDefaultKeybox); + Session& s = createSession("ONE"); + s.open(); + + ASSERT_TRUE(s.GenerateDerivedKeys()); + + const unsigned int num_keys = 3; + MessageData data; + s.fill_simple_message(&data); + data.keys[0].control.control_bits = wvoec_mock::kControlNonceEnabled; + data.keys[1].control.control_bits = wvoec_mock::kControlNonceEnabled; + data.keys[2].control.control_bits = wvoec_mock::kControlNonceEnabled; + data.keys[1].control.nonce = 42; // This one is bad. + + MessageData encrypted; + s.encrypt_message(data, &encrypted); + std::vector signature; + s.sign_message(encrypted, &signature); + OEMCrypto_KeyObject key_array[num_keys]; + const uint8_t* message_ptr = reinterpret_cast(&encrypted); + s.fill_key_array(encrypted, key_array); + + OEMCryptoResult sts = OEMCrypto_LoadKeys( + s.session_id(), + message_ptr, sizeof(encrypted), + &signature[0], signature.size(), + encrypted.mac_key_iv, + encrypted.mac_key, + num_keys, key_array); + + ASSERT_NE(OEMCrypto_SUCCESS, sts); + + s.close(); + testTearDown(); +} + +TEST_F(OEMCryptoClientTest, LoadKeysBadSignature) { + testSetUp(); + InstallKeybox(kDefaultKeybox); + Session& s = createSession("ONE"); + s.open(); + + ASSERT_TRUE(s.GenerateDerivedKeys()); + + const unsigned int num_keys = 3; + MessageData data; + s.fill_simple_message(&data); + + MessageData encrypted; + s.encrypt_message(data, &encrypted); + std::vector signature; + s.sign_message(encrypted, &signature); + OEMCrypto_KeyObject key_array[num_keys]; + const uint8_t* message_ptr = reinterpret_cast(&encrypted); + s.fill_key_array(encrypted, key_array); + + signature[0] = 42; // Bad signature. + + OEMCryptoResult sts = OEMCrypto_LoadKeys( + s.session_id(), + message_ptr, sizeof(encrypted), + &signature[0], signature.size(), + encrypted.mac_key_iv, + encrypted.mac_key, + num_keys, key_array); + + ASSERT_NE(OEMCrypto_SUCCESS, sts); + + s.close(); + testTearDown(); +} + +TEST_F(OEMCryptoClientTest, LoadKeysWithNoDerivedKeys) { + testSetUp(); + InstallKeybox(kDefaultKeybox); + Session& s = createSession("ONE"); + s.open(); + + // ASSERT_TRUE(s.GenerateDerivedKeys()); + + const unsigned int num_keys = 3; + MessageData data; + s.fill_simple_message(&data); + + MessageData encrypted; + s.encrypt_message(data, &encrypted); + std::vector signature; + s.sign_message(encrypted, &signature); + OEMCrypto_KeyObject key_array[num_keys]; + const uint8_t* message_ptr = reinterpret_cast(&encrypted); + s.fill_key_array(encrypted, key_array); + + OEMCryptoResult sts = OEMCrypto_LoadKeys( + s.session_id(), + message_ptr, sizeof(encrypted), + &signature[0], signature.size(), + encrypted.mac_key_iv, + encrypted.mac_key, + num_keys, key_array); + + ASSERT_NE(OEMCrypto_SUCCESS, sts); + + s.close(); + testTearDown(); +} + +/////////////////////////////////////////////////// +// Decrypt Tests +/////////////////////////////////////////////////// + +TEST_F(OEMCryptoClientTest, Decrypt) { + OEMCryptoResult sts; + testSetUp(); + InstallKeybox(kDefaultKeybox); + Session& s = createSession("ONE"); + s.open(); + + ASSERT_TRUE(s.LoadTestKeys()); + + // Select the key (from fill_simple_message) + vector keyId = wvcdm::a2b_hex("000000000000000000000000"); + sts = OEMCrypto_SelectKey(s.session_id(), &keyId[0], keyId.size()); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + + // Set up our expected input and output + vector encryptedData = wvcdm::a2b_hex( + "ec261c115f9d5cda1d5cc7d33c4e37362d1397c89efdd1da5f0065c4848b0462" + "337ba14693735203c9b4184e362439c0cea5e5d1a628425eddf8a6bf9ba901ca" + "46f5a9fd973cffbbe3c276af9919e2e8f6f3f420538b7a0d6dc41487874d96b8" + "efaedb45a689b91beb8c20d36140ad467d9d620b19a5fc6f223b57e0e6a7f913" + "00fd899e5e1b89963e83067ca0912aa5b79df683e2530b55a9645be341bc5f07" + "cffc724790af635c959e2644e51ba7f23bae710eb55a1f2f4e060c3c1dd1387c" + "74415dc880492dd1d5b9ecf3f01de48a44baeb4d3ea5cc4f8d561d0865afcabb" + "fc14a9ab9647e6e31adabb72d792f0c9ba99dc3e9205657d28fc7771d64e6d4b"); + vector encryptionIv = wvcdm::a2b_hex( + "719dbcb253b2ec702bb8c1b1bc2f3bc6"); + vector unencryptedData = wvcdm::a2b_hex( + "19ef4361e16e6825b336e2012ad8ffc9ce176ab2256e1b98aa15b7877bd8c626" + "fa40b2e88373457cbcf4f1b4b9793434a8ac03a708f85974cff01bddcbdd7a8e" + "e33fd160c1d5573bfd8104efd23237edcf28205c3673920553f8dd5e916604b0" + "1082345181dceeae5ea39d829c7f49e1850c460645de33c288723b7ae3d91a17" + "a3f04195cd1945ba7b0f37fef7e82368be30f04365d877766f6d56f67d22a244" + "ef2596d3053f657c1b5d90b64e11797edf1c198a23a7bfc20e4d44c74ae41280" + "a8317f443255f4020eda850ff0954e308f53a634cbce799ae58911bc59ccd6a5" + "de2ac53ee0fa7ea15fc692cc892acc0090865dc57becacddf362a092dfd3040b"); + + // Describe the output + uint8_t outputBuffer[256]; + OEMCrypto_DestBufferDesc destBuffer; + destBuffer.type = OEMCrypto_BufferType_Clear; + destBuffer.buffer.clear.address = outputBuffer; + destBuffer.buffer.clear.max_length = sizeof(outputBuffer); + + // Decrypt the data + sts = OEMCrypto_DecryptCTR(s.session_id(), &encryptedData[0], + encryptedData.size(), true, &encryptionIv[0], 0, + &destBuffer); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + ASSERT_EQ(0, memcmp(&unencryptedData[0], outputBuffer, + unencryptedData.size())); + + s.close(); + testTearDown(); +} + +TEST_F(OEMCryptoClientTest, DecryptWithOffset) { + OEMCryptoResult sts; + testSetUp(); + InstallKeybox(kDefaultKeybox); + Session& s = createSession("ONE"); + s.open(); + + ASSERT_TRUE(s.LoadTestKeys()); + + // Select the key (from fill_simple_message) + vector keyId = wvcdm::a2b_hex("000000000000000000000000"); + sts = OEMCrypto_SelectKey(s.session_id(), + &keyId[0], + keyId.size()); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + + // Set up our expected input and output + vector encryptedData = wvcdm::a2b_hex( + "c17055d4e3ab8e892b40ca2deed7cd46b406cd41d50f23d5877b36" + "ad351887df2b3774dc413904afd958ba766cc6ab51a3ffd8f845296c5d8326ee" + "39c9d0fec79885515e6b8a12911831d9fb158ca2fd3dfcfcf228741a63734685" + "8dffc30f5871260c5cef8be61cfa08b191c837901f077046664c0c56db81d412" + "98b59e5655cd94871c3c226dc3565144297f1459cddba069d5d2d6206cfd5798" + "eda4b82e01a9966d48984d6ef3fbd326ba0f6fcbe52c95786d478c2f33398c62" + "ae5210c7472d7d8dc7d12f981679f4ea9793736f354747ef14165367b94e07fc" + "4bcc7bd14746304fea100dc6465ab51241355bb19e6c2cfb2bb6bbf709765d13"); + vector encryptionIv = wvcdm::a2b_hex( + "c09454479a280829c946df3c22f25539"); + vector unencryptedData = wvcdm::a2b_hex( + "f344d9cfe336c94cf4e3ea9e3446d1427bc02d2debe6dec5b272b8" + "a4004b696c4b37e01d7418510abf32bb071f9a4bc0d2ad7e874b648e50bd0e4f" + "7085b70bf9ad2c7f37025dd45f93e90304739b1ce098a52e7b99a90f92544a9b" + "dca6f49e0006c80a0cfa018600523ad30e483141fe720d045394815d5c875ad4" + "b4387b8d09b6119bd0943e51b0b9103034496b3a83ba593f79baa188aeb6e08f" + "f6475933e9ce1bb95fbb526424e7966e25830c20da73c65c6fbff110b08e4def" + "eae94f98296770275b0d738207a8217cd6118f6ebc6e393428f2268cfedf800e" + "a7ebc606471b9a9dfccd1589e86d88fde508261eaf190efd20554ce9e14ff3c9"); + + // Describe the output + uint8_t outputBuffer[256]; + OEMCrypto_DestBufferDesc destBuffer; + destBuffer.type = OEMCrypto_BufferType_Clear; + destBuffer.buffer.clear.address = outputBuffer; + destBuffer.buffer.clear.max_length = sizeof(outputBuffer); + + // Decrypt the data + sts = OEMCrypto_DecryptCTR(s.session_id(), &encryptedData[0], + encryptedData.size(), true, &encryptionIv[0], 5, + &destBuffer); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + ASSERT_EQ(0, memcmp(&unencryptedData[0], outputBuffer, + unencryptedData.size())); + + s.close(); + testTearDown(); +} + +TEST_F(OEMCryptoClientTest, DecryptUnencrypted) { + OEMCryptoResult sts; + testSetUp(); + InstallKeybox(kDefaultKeybox); + Session& s = createSession("ONE"); + s.open(); + + ASSERT_TRUE(s.LoadTestKeys()); + + // Select the key (from fill_simple_message) + vector keyId = wvcdm::a2b_hex("000000000000000000000000"); + sts = OEMCrypto_SelectKey(s.session_id(), &keyId[0], keyId.size()); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + + // Set up our expected input and output + vector unencryptedData = wvcdm::a2b_hex( + "1558497b6d994be343ed1c6d6313e0537b843e9a9c0836d1e83fe33154191ce9" + "a14d8d95bebaddc03bd471827170f527c0a166b9068b273d1bc57fbb13975ee4" + "f6b9a31743da6c447acbb712e81b13eddfd4e96c76010ac9b8aa1b6b3152b0fc" + "39ad33e5719656069f9ede9ebba7a77dd2e2074eec5c1b7ffc427a6f1be168f0" + "b5857713a44623862c903284bc53417e23c65602b52c1cb699e8352453eb9698" + "0b31459b90c26c907b549c1ab293725e414d4e45f5b30af7a55f95499a7dc89f" + "7d13ba90b34aef6b49484b0701bf96ea8b660c24bb4e92a2d1c43beb434fa386" + "1071380388799ac31d79285f5817687ed3e2eeb73a30744e77b757686c9ba5ad"); + vector encryptionIv = wvcdm::a2b_hex( + "49fc3efaaf614ed81d595847b928edd0"); + + // Describe the output + uint8_t outputBuffer[256]; + OEMCrypto_DestBufferDesc destBuffer; + destBuffer.type = OEMCrypto_BufferType_Clear; + destBuffer.buffer.clear.address = outputBuffer; + destBuffer.buffer.clear.max_length = sizeof(outputBuffer); + + // Decrypt the data + sts = OEMCrypto_DecryptCTR(s.session_id(), &unencryptedData[0], + unencryptedData.size(), false, &encryptionIv[0], 0, + &destBuffer); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + ASSERT_EQ(0, memcmp(&unencryptedData[0], outputBuffer, + unencryptedData.size())); + + s.close(); + testTearDown(); +} + +/////////////////////////////////////////////////// +// Certificate Root of Trust Tests +/////////////////////////////////////////////////// + +TEST_F(OEMCryptoClientTest, CertificateProvision) { + OEMCryptoResult sts; + testSetUp(); + InstallKeybox(kDefaultKeybox); + Session& s = createSession("ONE"); + s.open(); + + ASSERT_TRUE(s.GenerateDerivedKeys()); + uint32_t nonce = s.getNonce(); + + vector context = wvcdm::a2b_hex( + "0a4c08001248000000020000101907d9ffde13aa95c122678053362136bdf840" + "8f8276e4c2d87ec52b61aa1b9f646e58734930acebe899b3e464189a14a87202" + "fb02574e70640bd22ef44b2d7e3912250a230a14080112100915007caa9b5931" + "b76a3a85f046523e10011a09393837363534333231180120002a0c3138383637" + "38373430350000"); + + // Generate signature + size_t gen_signature_length = 0; + sts = OEMCrypto_GenerateSignature(s.session_id(), &context[0], context.size(), + NULL, &gen_signature_length); + + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); + ASSERT_EQ(static_cast(256), gen_signature_length); + + uint8_t* gen_signature = new uint8_t[gen_signature_length]; + sts = OEMCrypto_GenerateSignature(s.session_id(), &context[0], context.size(), + gen_signature, &gen_signature_length); + + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + + // Rewrap Canned Response + + // In the real world, the signature above would just have been used to + // contact the certificate provisioning server to get this response. + // TODO: This is not a valid test vector + vector message = wvcdm::a2b_hex( + "000000001248000000020000101907d9ffde13aa95c122678053362136bdf840" + "8f8276e4c2d87ec52b61aa1b9f646e58734930acebe899b3e464189a14a87202" + "fb02574e70640bd22ef44b2d7e3912250a230a14080112100915007caa9b5931" + "b76a3a85f046523e10011a09393837363534333231180120002a0c3138383637" + "0a4c08001248000000020000101907d9ffde13aa95c122678053362136bdf840" + "8f8276e4c2d87ec52b61aa1b9f646e58734930acebe899b3e464189a14a87202" + "fb02574e70640bd22ef44b2d7e3912250a230a14080112100915007caa9b5931" + "b76a3a85f046523e10011a09393837363534333231180120002a0c3138383637" + "0a4c08001248000000020000101907d9ffde13aa95c122678053362136bdf840" + "8f8276e4c2d87ec52b61aa1b9f646e58734930acebe899b3e464189a14a87202" + "fb02574e70640bd22ef44b2d7e3912250a230a14080112100915007caa9b5931" + "b76a3a85f046523e10011a09393837363534333231180120002a0c3138383637" + "0a4c08001248000000020000101907d9ffde13aa95c122678053362136bdf840" + "8f8276e4c2d87ec52b61aa1b9f646e58734930acebe899b3e464189a14a87202" + "fb02574e70640bd22ef44b2d7e3912250a230a14080112100915007caa9b5931" + "b76a3a85f046523e10011a09393837363534333231180120002a0c3138383637" + "0a4c08001248000000020000101907d9ffde13aa95c122678053362136bdf840" + "8f8276e4c2d87ec52b61aa1b9f646e58734930acebe899b3e464189a14a87202" + "fb02574e70640bd22ef44b2d7e3912250a230a14080112100915007caa9b5931" + "b76a3a85f046523e10011a09393837363534333231180120002a0c3138383637" + "0a4c08001248000000020000101907d9ffde13aa95c122678053362136bdf840" + "8f8276e4c2d87ec52b61aa1b9f646e58734930acebe899b3e464189a14a87202" + "fb02574e70640bd22ef44b2d7e3912250a230a14080112100915007caa9b5931" + "b76a3a85f046523e10011a09393837363534333231180120002a0c3138383637"); + + memcpy(&message[0], &nonce, sizeof(uint32_t)); + uint32_t* messageNonce = reinterpret_cast(&message[0]); + uint8_t* key = &message[32]; + size_t key_length = 7; + uint8_t* key_iv = &message[64]; + + // TODO: In practice, we need to generate a signature here after inserting + // the right nonce into the message. + vector signature = wvcdm::a2b_hex( + "0a4c08001248000000020000101907d9ffde13aa95c122678053362136bdf840"); + + size_t wrapped_key_length; + + sts = OEMCrypto_RewrapDeviceRSAKey(s.session_id(), &message[0], + message.size(), &signature[0], + signature.size(), messageNonce, key, + key_length, key_iv, NULL, + &wrapped_key_length); + + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); + ASSERT_NE(static_cast(0), wrapped_key_length); + + vector wrapped_key; + wrapped_key.resize(wrapped_key_length); + + sts = OEMCrypto_RewrapDeviceRSAKey(s.session_id(), &message[0], + message.size(), &signature[0], + signature.size(), messageNonce, key, + key_length, key_iv, &wrapped_key[0], + &wrapped_key_length); + + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + + // TODO: This is not a valid test vector + vector clear_key = wvcdm::a2b_hex( + "1558497b6d994be343ed1c6d6313e0537b843e9a9c0836d1e83fe33154191ce9" + "a14d8d95bebaddc03bd471827170f527c0a166b9068b273d1bc57fbb13975ee4" + "f6b9a31743da6c447acbb712e81b13eddfd4e96c76010ac9b8aa1b6b3152b0fc" + "39ad33e5719656069f9ede9ebba7a77dd2e2074eec5c1b7ffc427a6f1be168f0" + "b5857713a44623862c903284bc53417e23c65602b52c1cb699e8352453eb9698" + "0b31459b90c26c907b549c1ab293725e414d4e45f5b30af7a55f95499a7dc89f" + "7d13ba90b34aef6b49484b0701bf96ea8b660c24bb4e92a2d1c43beb434fa386" + "1071380388799ac31d79285f5817687ed3e2eeb73a30744e77b757686c9ba5ad");; + ASSERT_EQ(NULL, find(wrapped_key, clear_key)); + + s.close(); + testTearDown(); + + delete[] gen_signature; +} + +TEST_F(OEMCryptoClientTest, CertificateLicense) { + OEMCryptoResult sts; + InstallKeybox(kDefaultKeybox); + testSetUp(); + Session& s = createSession("ONE"); + s.open(); + + vector wrappedKey; + + ASSERT_TRUE(s.GenerateDerivedKeys()); + ASSERT_TRUE(s.CertificateProvision(&wrappedKey)); + + // Load the Wrapped Key + sts = OEMCrypto_LoadDeviceRSAKey(s.session_id(), &wrappedKey[0], + wrappedKey.size()); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + + // Sign a Message + vector licenseRequest = wvcdm::a2b_hex( + "ba711a51e0c4c995440c28057f7f5e2f2e9c3a1edeb7549aca21e6050b059ac8" + "6ad64ec1a528eef17b4f5ce781af488d50fb0e60d04b48c78d55847a4e14243c" + "0023c553b46a2f53995870f351295e3aa2237f153f1415e817ad23e662e547b1" + "4708b303473813f93ee192353ff22bee54dd0f558bbe4b61b75b387bc310e9d6" + "8ff2cb3482689c0688570809b756dba4c2697be3132a2da782aa877ed64d8c7d" + "506525a382bad14d7e797c256c3617c22fa4165482b9742e9b54ffb6c52eda1d"); + size_t signature_length = 0; + + sts = OEMCrypto_GenerateRSASignature(s.session_id(), &licenseRequest[0], + licenseRequest.size(), NULL, + &signature_length); + + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); + ASSERT_NE(static_cast(0), signature_length); + + uint8_t* signature = new uint8_t[signature_length]; + + sts = OEMCrypto_GenerateRSASignature(s.session_id(), &licenseRequest[0], + licenseRequest.size(), signature, + &signature_length); + + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + // TODO: Validate the signature + + // Rewrap Canned Response + + // In the real world, the signature above would just have been used to contact + // the license server to get this response. + // TODO: This is not a valid test vector + vector sessionKey = wvcdm::a2b_hex( + "6fa479c731d2770b6a61a5d1420bb9d1"); + vector mac_context = wvcdm::a2b_hex( + "41555448454e5449434154494f4e000a4c08001248000000020000101907d9ff" + "de13aa95c122678053362136bdf8408f8276e4c2d87ec52b61aa1b9f646e5873" + "4930acebe899b3e464189a14a87202fb02574e70640bd22ef44b2d7e3912250a" + "230a14080112100915007caa9b5931b76a3a85f046523e10011a093938373635" + "34333231180120002a0c31383836373837343035000000000100"); + vector enc_context = wvcdm::a2b_hex( + "454e4352595054494f4e000a4c08001248000000020000101907d9ffde13aa95" + "c122678053362136bdf8408f8276e4c2d87ec52b61aa1b9f646e58734930aceb" + "e899b3e464189a14a87202fb02574e70640bd22ef44b2d7e3912250a230a1408" + "0112100915007caa9b5931b76a3a85f046523e10011a09393837363534333231" + "180120002a0c31383836373837343035000000000080"); + + sts = OEMCrypto_DeriveKeysFromSessionKey(s.session_id(), &sessionKey[0], + sessionKey.size(), &mac_context[0], + mac_context.size(), &enc_context[0], + enc_context.size()); + + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + + s.close(); + testTearDown(); + + delete[] signature; +} + +TEST_F(OEMCryptoClientTest, CertificateDecrypt) { + OEMCryptoResult sts; + testSetUp(); + InstallKeybox(kDefaultKeybox); + Session& s = createSession("ONE"); + s.open(); + + ASSERT_TRUE(s.GenerateDerivedKeys()); + ASSERT_TRUE(s.CertificateLicense()); + + // Select the key (from fill_simple_message) + vector keyId = wvcdm::a2b_hex("000000000000000000000000"); + sts = OEMCrypto_SelectKey(s.session_id(), &keyId[0], keyId.size()); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + + // Set up our expected input and output + vector encryptedData = wvcdm::a2b_hex( + "ec261c115f9d5cda1d5cc7d33c4e37362d1397c89efdd1da5f0065c4848b0462" + "337ba14693735203c9b4184e362439c0cea5e5d1a628425eddf8a6bf9ba901ca" + "46f5a9fd973cffbbe3c276af9919e2e8f6f3f420538b7a0d6dc41487874d96b8" + "efaedb45a689b91beb8c20d36140ad467d9d620b19a5fc6f223b57e0e6a7f913" + "00fd899e5e1b89963e83067ca0912aa5b79df683e2530b55a9645be341bc5f07" + "cffc724790af635c959e2644e51ba7f23bae710eb55a1f2f4e060c3c1dd1387c" + "74415dc880492dd1d5b9ecf3f01de48a44baeb4d3ea5cc4f8d561d0865afcabb" + "fc14a9ab9647e6e31adabb72d792f0c9ba99dc3e9205657d28fc7771d64e6d4b"); + vector encryptionIv = wvcdm::a2b_hex( + "719dbcb253b2ec702bb8c1b1bc2f3bc6"); + vector unencryptedData = wvcdm::a2b_hex( + "19ef4361e16e6825b336e2012ad8ffc9ce176ab2256e1b98aa15b7877bd8c626" + "fa40b2e88373457cbcf4f1b4b9793434a8ac03a708f85974cff01bddcbdd7a8e" + "e33fd160c1d5573bfd8104efd23237edcf28205c3673920553f8dd5e916604b0" + "1082345181dceeae5ea39d829c7f49e1850c460645de33c288723b7ae3d91a17" + "a3f04195cd1945ba7b0f37fef7e82368be30f04365d877766f6d56f67d22a244" + "ef2596d3053f657c1b5d90b64e11797edf1c198a23a7bfc20e4d44c74ae41280" + "a8317f443255f4020eda850ff0954e308f53a634cbce799ae58911bc59ccd6a5" + "de2ac53ee0fa7ea15fc692cc892acc0090865dc57becacddf362a092dfd3040b"); + + // Describe the output + uint8_t outputBuffer[256]; + OEMCrypto_DestBufferDesc destBuffer; + destBuffer.type = OEMCrypto_BufferType_Clear; + destBuffer.buffer.clear.address = outputBuffer; + destBuffer.buffer.clear.max_length = sizeof(outputBuffer); + + // Decrypt the data + sts = OEMCrypto_DecryptCTR(s.session_id(), &encryptedData[0], + encryptedData.size(), true, &encryptionIv[0], 0, + &destBuffer); + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + ASSERT_EQ(0, memcmp(&unencryptedData[0], outputBuffer, + unencryptedData.size())); + + s.close(); + testTearDown(); +} +#endif // CAN_INSTALL_KEYBOX + +// TODO(kqyang): Add more unit tests + +} // namespace wvoec diff --git a/libwvdrmengine/src/TODO b/libwvdrmengine/src/TODO deleted file mode 100644 index faa1ed87..00000000 --- a/libwvdrmengine/src/TODO +++ /dev/null @@ -1,2 +0,0 @@ -Add make file to create libwvdrmengine.so -Juce and Jerry to fill in and add tests. diff --git a/libwvdrmengine/src/WVCDMSingleton.cpp b/libwvdrmengine/src/WVCDMSingleton.cpp new file mode 100644 index 00000000..c89aacae --- /dev/null +++ b/libwvdrmengine/src/WVCDMSingleton.cpp @@ -0,0 +1,32 @@ +// +// Copyright 2013 Google Inc. All Rights Reserved. +// + +//#define LOG_NDEBUG 0 +#define LOG_TAG "WVCdm" +#include + +#include "WVCDMSingleton.h" + +#include "utils/Mutex.h" + +namespace wvdrm { + +using wvcdm::WvContentDecryptionModule; +using android::Mutex; + +Mutex cdmLock; +WvContentDecryptionModule* cdm = NULL; + +WvContentDecryptionModule* getCDM() { + Mutex::Autolock lock(cdmLock); + + if (cdm == NULL) { + ALOGD("Instantiating CDM."); + cdm = new WvContentDecryptionModule(); + } + + return cdm; +} + +} // namespace wvdrm diff --git a/libwvdrmengine/src/WVCreatePluginFactories.cpp b/libwvdrmengine/src/WVCreatePluginFactories.cpp new file mode 100644 index 00000000..a730990b --- /dev/null +++ b/libwvdrmengine/src/WVCreatePluginFactories.cpp @@ -0,0 +1,20 @@ +// +// Copyright 2013 Google Inc. All Rights Reserved. +// + +#include "WVCreatePluginFactories.h" + +#include "WVCryptoFactory.h" +#include "WVDrmFactory.h" + +extern "C" { + +android::DrmFactory* createDrmFactory() { + return new wvdrm::WVDrmFactory(); +} + +android::CryptoFactory* createCryptoFactory() { + return new wvdrm::WVCryptoFactory(); +} + +} // extern "C" diff --git a/libwvdrmengine/src/WVCryptoFactory.cpp b/libwvdrmengine/src/WVCryptoFactory.cpp new file mode 100644 index 00000000..05de6c07 --- /dev/null +++ b/libwvdrmengine/src/WVCryptoFactory.cpp @@ -0,0 +1,89 @@ +// +// Copyright 2013 Google Inc. All Rights Reserved. +// + +//#define LOG_NDEBUG 0 +#define LOG_TAG "WVCdm" +#include + +#include "WVCryptoFactory.h" + +#include + +#include "utils/Errors.h" +#include "WVCDMSingleton.h" +#include "WVCryptoPlugin.h" +#include "WVUUID.h" + +namespace wvdrm { + +using namespace android; + +WVCryptoFactory::WVCryptoFactory() + : mLegacyLibraryHandle(NULL), + mLegacyFactory(NULL) { + mLegacyLibraryHandle = dlopen("libdrmdecrypt.so", RTLD_NOW); + + if (mLegacyLibraryHandle == NULL) { + ALOGE("Unable to locate libdrmdecrypt.so"); + } else { + typedef CryptoFactory* (*CreateCryptoFactoryFunc)(); + + CreateCryptoFactoryFunc legacyCreateCryptoFactory = + (CreateCryptoFactoryFunc)dlsym(mLegacyLibraryHandle, + "createCryptoFactory"); + + if (legacyCreateCryptoFactory == NULL) { + ALOGE("Unable to find legacy symbol 'createCryptoFactory'."); + dlclose(mLegacyLibraryHandle); + mLegacyLibraryHandle = NULL; + } else { + mLegacyFactory = legacyCreateCryptoFactory(); + if (mLegacyFactory == NULL) { + ALOGE("Legacy createCryptoFactory() failed."); + dlclose(mLegacyLibraryHandle); + mLegacyLibraryHandle = NULL; + } + } + } +} + +WVCryptoFactory::~WVCryptoFactory() { + if (mLegacyFactory != NULL) { + delete mLegacyFactory; + mLegacyFactory = NULL; + } + + if (mLegacyLibraryHandle != NULL) { + dlclose(mLegacyLibraryHandle); + mLegacyLibraryHandle = NULL; + } +} + +bool WVCryptoFactory::isCryptoSchemeSupported(const uint8_t uuid[16]) const { + return isWidevineUUID(uuid); +} + +status_t WVCryptoFactory::createPlugin(const uint8_t uuid[16], const void* data, + size_t size, CryptoPlugin** plugin) { + if (!isCryptoSchemeSupported(uuid)) { + *plugin = NULL; + return BAD_VALUE; + } + + // 0 size means they want an old-style Widevine plugin + if (size == 0) { + if (mLegacyFactory == NULL) { + return -EINVAL; + } + + return mLegacyFactory->createPlugin(uuid, data, size, plugin); + } + + // We received a session ID, so create a CENC plugin. + *plugin = new WVCryptoPlugin(data, size, getCDM()); + + return OK; +} + +} // namespace wvdrm diff --git a/libwvdrmengine/src/WVDrmFactory.cpp b/libwvdrmengine/src/WVDrmFactory.cpp new file mode 100644 index 00000000..6454fe7c --- /dev/null +++ b/libwvdrmengine/src/WVDrmFactory.cpp @@ -0,0 +1,36 @@ +// +// Copyright 2013 Google Inc. All Rights Reserved. +// + +//#define LOG_NDEBUG 0 +#define LOG_TAG "WVCdm" +#include + +#include "WVDrmFactory.h" + +#include "utils/Errors.h" +#include "WVCDMSingleton.h" +#include "WVDrmPlugin.h" +#include "WVUUID.h" + +namespace wvdrm { + +using namespace android; + +bool WVDrmFactory::isCryptoSchemeSupported(const uint8_t uuid[16]) { + return isWidevineUUID(uuid); +} + +status_t WVDrmFactory::createDrmPlugin(const uint8_t uuid[16], + DrmPlugin** plugin) { + if (!isCryptoSchemeSupported(uuid)) { + *plugin = NULL; + return BAD_VALUE; + } + + *plugin = new WVDrmPlugin(getCDM()); + + return OK; +} + +} // namespace wvdrm diff --git a/libwvdrmengine/src/WVUUID.cpp b/libwvdrmengine/src/WVUUID.cpp new file mode 100644 index 00000000..b24a2e3c --- /dev/null +++ b/libwvdrmengine/src/WVUUID.cpp @@ -0,0 +1,26 @@ +// +// Copyright 2013 Google Inc. All Rights Reserved. +// + +#include "WVUUID.h" + +#include + +namespace wvdrm { + +bool isWidevineUUID(const uint8_t uuid[16]) { + static const uint8_t kWidevineUUID[16] = { + 0xED,0xEF,0x8B,0xA9,0x79,0xD6,0x4A,0xCE, + 0xA3,0xC8,0x27,0xDC,0xD5,0x1D,0x21,0xED + }; + + static const uint8_t kOldNetflixWidevineUUID[16] = { + 0x29,0x70,0x1F,0xE4,0x3C,0xC7,0x4A,0x34, + 0x8C,0x5B,0xAE,0x90,0xC7,0x43,0x9A,0x47 + }; + + return !memcmp(uuid, kWidevineUUID, 16) || + !memcmp(uuid, kOldNetflixWidevineUUID, 16); +} + +} // namespace wvdrm diff --git a/libwvdrmengine/test/TODO b/libwvdrmengine/test/TODO deleted file mode 100644 index faa1ed87..00000000 --- a/libwvdrmengine/test/TODO +++ /dev/null @@ -1,2 +0,0 @@ -Add make file to create libwvdrmengine.so -Juce and Jerry to fill in and add tests. diff --git a/libwvdrmengine/test/gmock/Android.mk b/libwvdrmengine/test/gmock/Android.mk new file mode 100644 index 00000000..64e29383 --- /dev/null +++ b/libwvdrmengine/test/gmock/Android.mk @@ -0,0 +1,3 @@ +# Copyright 2013 Google Inc. All Rights Reserved. + +include $(call all-subdir-makefiles) diff --git a/libwvdrmengine/test/gmock/CHANGES b/libwvdrmengine/test/gmock/CHANGES new file mode 100644 index 00000000..90f88a59 --- /dev/null +++ b/libwvdrmengine/test/gmock/CHANGES @@ -0,0 +1,92 @@ +Changes for 1.6.0: + +* Compilation is much faster and uses much less memory, especially + when the constructor and destructor of a mock class are moved out of + the class body. +* New matchers: Pointwise(), Each(). +* New actions: ReturnPointee() and ReturnRefOfCopy(). +* CMake support. +* Project files for Visual Studio 2010. +* AllOf() and AnyOf() can handle up-to 10 arguments now. +* Google Mock doctor understands Clang error messages now. +* SetArgPointee<> now accepts string literals. +* gmock_gen.py handles storage specifier macros and template return + types now. +* Compatibility fixes. +* Bug fixes and implementation clean-ups. +* Potentially incompatible changes: disables the harmful 'make install' + command in autotools. + +Potentially breaking changes: + +* The description string for MATCHER*() changes from Python-style + interpolation to an ordinary C++ string expression. +* SetArgumentPointee is deprecated in favor of SetArgPointee. +* Some non-essential project files for Visual Studio 2005 are removed. + +Changes for 1.5.0: + + * New feature: Google Mock can be safely used in multi-threaded tests + on platforms having pthreads. + * New feature: function for printing a value of arbitrary type. + * New feature: function ExplainMatchResult() for easy definition of + composite matchers. + * The new matcher API lets user-defined matchers generate custom + explanations more directly and efficiently. + * Better failure messages all around. + * NotNull() and IsNull() now work with smart pointers. + * Field() and Property() now work when the matcher argument is a pointer + passed by reference. + * Regular expression matchers on all platforms. + * Added GCC 4.0 support for Google Mock Doctor. + * Added gmock_all_test.cc for compiling most Google Mock tests + in a single file. + * Significantly cleaned up compiler warnings. + * Bug fixes, better test coverage, and implementation clean-ups. + + Potentially breaking changes: + + * Custom matchers defined using MatcherInterface or MakePolymorphicMatcher() + need to be updated after upgrading to Google Mock 1.5.0; matchers defined + using MATCHER or MATCHER_P* aren't affected. + * Dropped support for 'make install'. + +Changes for 1.4.0 (we skipped 1.2.* and 1.3.* to match the version of +Google Test): + + * Works in more environments: Symbian and minGW, Visual C++ 7.1. + * Lighter weight: comes with our own implementation of TR1 tuple (no + more dependency on Boost!). + * New feature: --gmock_catch_leaked_mocks for detecting leaked mocks. + * New feature: ACTION_TEMPLATE for defining templatized actions. + * New feature: the .After() clause for specifying expectation order. + * New feature: the .With() clause for for specifying inter-argument + constraints. + * New feature: actions ReturnArg(), ReturnNew(...), and + DeleteArg(). + * New feature: matchers Key(), Pair(), Args<...>(), AllArgs(), IsNull(), + and Contains(). + * New feature: utility class MockFunction, useful for checkpoints, etc. + * New feature: functions Value(x, m) and SafeMatcherCast(m). + * New feature: copying a mock object is rejected at compile time. + * New feature: a script for fusing all Google Mock and Google Test + source files for easy deployment. + * Improved the Google Mock doctor to diagnose more diseases. + * Improved the Google Mock generator script. + * Compatibility fixes for Mac OS X and gcc. + * Bug fixes and implementation clean-ups. + +Changes for 1.1.0: + + * New feature: ability to use Google Mock with any testing framework. + * New feature: macros for easily defining new matchers + * New feature: macros for easily defining new actions. + * New feature: more container matchers. + * New feature: actions for accessing function arguments and throwing + exceptions. + * Improved the Google Mock doctor script for diagnosing compiler errors. + * Bug fixes and implementation clean-ups. + +Changes for 1.0.0: + + * Initial Open Source release of Google Mock diff --git a/libwvdrmengine/test/gmock/CONTRIBUTORS b/libwvdrmengine/test/gmock/CONTRIBUTORS new file mode 100644 index 00000000..6e9ae362 --- /dev/null +++ b/libwvdrmengine/test/gmock/CONTRIBUTORS @@ -0,0 +1,40 @@ +# This file contains a list of people who've made non-trivial +# contribution to the Google C++ Mocking Framework project. People +# who commit code to the project are encouraged to add their names +# here. Please keep the list sorted by first names. + +Benoit Sigoure +Bogdan Piloca +Chandler Carruth +Dave MacLachlan +David Anderson +Dean Sturtevant +Gene Volovich +Hal Burch +Jeffrey Yasskin +Jim Keller +Joe Walnes +Jon Wray +Keir Mierle +Keith Ray +Kostya Serebryany +Lev Makhlis +Manuel Klimek +Mario Tanev +Mark Paskin +Markus Heule +Matthew Simmons +Mike Bland +Neal Norwitz +Nermin Ozkiranartli +Owen Carlsen +Paneendra Ba +Paul Menage +Piotr Kaminski +Russ Rufer +Sverre Sundsdal +Takeshi Yoshino +Vadim Berman +Vlad Losev +Wolfgang Klier +Zhanyong Wan diff --git a/libwvdrmengine/test/gmock/COPYING b/libwvdrmengine/test/gmock/COPYING new file mode 100644 index 00000000..1941a11f --- /dev/null +++ b/libwvdrmengine/test/gmock/COPYING @@ -0,0 +1,28 @@ +Copyright 2008, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/libwvdrmengine/test/gmock/README b/libwvdrmengine/test/gmock/README new file mode 100644 index 00000000..aa3283d7 --- /dev/null +++ b/libwvdrmengine/test/gmock/README @@ -0,0 +1,354 @@ +Google C++ Mocking Framework +============================ + +http://code.google.com/p/googlemock/ + +Overview +-------- + +Google's framework for writing and using C++ mock classes on a variety +of platforms (Linux, Mac OS X, Windows, Windows CE, Symbian, etc). +Inspired by jMock, EasyMock, and Hamcrest, and designed with C++'s +specifics in mind, it can help you derive better designs of your +system and write better tests. + +Google Mock: + +- provides a declarative syntax for defining mocks, +- can easily define partial (hybrid) mocks, which are a cross of real + and mock objects, +- handles functions of arbitrary types and overloaded functions, +- comes with a rich set of matchers for validating function arguments, +- uses an intuitive syntax for controlling the behavior of a mock, +- does automatic verification of expectations (no record-and-replay + needed), +- allows arbitrary (partial) ordering constraints on + function calls to be expressed, +- lets a user extend it by defining new matchers and actions. +- does not use exceptions, and +- is easy to learn and use. + +Please see the project page above for more information as well as the +mailing list for questions, discussions, and development. There is +also an IRC channel on OFTC (irc.oftc.net) #gtest available. Please +join us! + +Please note that code under scripts/generator/ is from the cppclean +project (http://code.google.com/p/cppclean/) and under the Apache +License, which is different from Google Mock's license. + +Requirements for End Users +-------------------------- + +Google Mock is implemented on top of the Google Test C++ testing +framework (http://code.google.com/p/googletest/), and includes the +latter as part of the SVN repositary and distribution package. You +must use the bundled version of Google Test when using Google Mock, or +you may get compiler/linker errors. + +You can also easily configure Google Mock to work with another testing +framework of your choice; although it will still need Google Test as +an internal dependency. Please read +http://code.google.com/p/googlemock/wiki/ForDummies#Using_Google_Mock_with_Any_Testing_Framework +for how to do it. + +Google Mock depends on advanced C++ features and thus requires a more +modern compiler. The following are needed to use Google Mock: + +### Linux Requirements ### + +These are the base requirements to build and use Google Mock from a source +package (as described below): + + * GNU-compatible Make or "gmake" + * POSIX-standard shell + * POSIX(-2) Regular Expressions (regex.h) + * C++98-standard-compliant compiler (e.g. GCC 3.4 or newer) + +### Windows Requirements ### + + * Microsoft Visual C++ 8.0 SP1 or newer + +### Mac OS X Requirements ### + + * Mac OS X 10.4 Tiger or newer + * Developer Tools Installed + +Requirements for Contributors +----------------------------- + +We welcome patches. If you plan to contribute a patch, you need to +build Google Mock and its own tests from an SVN checkout (described +below), which has further requirements: + + * Automake version 1.9 or newer + * Autoconf version 2.59 or newer + * Libtool / Libtoolize + * Python version 2.3 or newer (for running some of the tests and + re-generating certain source files from templates) + +Getting the Source +------------------ + +There are two primary ways of getting Google Mock's source code: you +can download a stable source release in your preferred archive format, +or directly check out the source from our Subversion (SVN) repositary. +The SVN checkout requires a few extra steps and some extra software +packages on your system, but lets you track development and make +patches much more easily, so we highly encourage it. + +### Source Package ### + +Google Mock is released in versioned source packages which can be +downloaded from the download page [1]. Several different archive +formats are provided, but the only difference is the tools needed to +extract their contents, and the size of the resulting file. Download +whichever you are most comfortable with. + + [1] http://code.google.com/p/googlemock/downloads/list + +Once downloaded expand the archive using whichever tools you prefer +for that type. This will always result in a new directory with the +name "gmock-X.Y.Z" which contains all of the source code. Here are +some examples on Linux: + + tar -xvzf gmock-X.Y.Z.tar.gz + tar -xvjf gmock-X.Y.Z.tar.bz2 + unzip gmock-X.Y.Z.zip + +### SVN Checkout ### + +To check out the main branch (also known as the "trunk") of Google +Mock, run the following Subversion command: + + svn checkout http://googlemock.googlecode.com/svn/trunk/ gmock-svn + +If you are using a *nix system and plan to use the GNU Autotools build +system to build Google Mock (described below), you'll need to +configure it now. Otherwise you are done with getting the source +files. + +To prepare the Autotools build system, enter the target directory of +the checkout command you used ('gmock-svn') and proceed with the +following command: + + autoreconf -fvi + +Once you have completed this step, you are ready to build the library. +Note that you should only need to complete this step once. The +subsequent 'make' invocations will automatically re-generate the bits +of the build system that need to be changed. + +If your system uses older versions of the autotools, the above command +will fail. You may need to explicitly specify a version to use. For +instance, if you have both GNU Automake 1.4 and 1.9 installed and +'automake' would invoke the 1.4, use instead: + + AUTOMAKE=automake-1.9 ACLOCAL=aclocal-1.9 autoreconf -fvi + +Make sure you're using the same version of automake and aclocal. + +Setting up the Build +-------------------- + +To build Google Mock and your tests that use it, you need to tell your +build system where to find its headers and source files. The exact +way to do it depends on which build system you use, and is usually +straightforward. + +### Generic Build Instructions ### + +This section shows how you can integrate Google Mock into your +existing build system. + +Suppose you put Google Mock in directory ${GMOCK_DIR} and Google Test +in ${GTEST_DIR} (the latter is ${GMOCK_DIR}/gtest by default). To +build Google Mock, create a library build target (or a project as +called by Visual Studio and Xcode) to compile + + ${GTEST_DIR}/src/gtest-all.cc and ${GMOCK_DIR}/src/gmock-all.cc + +with + + ${GTEST_DIR}/include, ${GTEST_DIR}, ${GMOCK_DIR}/include, and ${GMOCK_DIR} + +in the header search path. Assuming a Linux-like system and gcc, +something like the following will do: + + g++ -I${GTEST_DIR}/include -I${GTEST_DIR} -I${GMOCK_DIR}/include \ + -I${GMOCK_DIR} -c ${GTEST_DIR}/src/gtest-all.cc + g++ -I${GTEST_DIR}/include -I${GTEST_DIR} -I${GMOCK_DIR}/include \ + -I${GMOCK_DIR} -c ${GMOCK_DIR}/src/gmock-all.cc + ar -rv libgmock.a gtest-all.o gmock-all.o + +Next, you should compile your test source file with +${GTEST_DIR}/include and ${GMOCK_DIR}/include in the header search +path, and link it with gmock and any other necessary libraries: + + g++ -I${GTEST_DIR}/include -I${GMOCK_DIR}/include \ + path/to/your_test.cc libgmock.a -o your_test + +As an example, the make/ directory contains a Makefile that you can +use to build Google Mock on systems where GNU make is available +(e.g. Linux, Mac OS X, and Cygwin). It doesn't try to build Google +Mock's own tests. Instead, it just builds the Google Mock library and +a sample test. You can use it as a starting point for your own build +script. + +If the default settings are correct for your environment, the +following commands should succeed: + + cd ${GMOCK_DIR}/make + make + ./gmock_test + +If you see errors, try to tweak the contents of make/Makefile to make +them go away. There are instructions in make/Makefile on how to do +it. + +### Windows ### + +The msvc/2005 directory contains VC++ 2005 projects and the msvc/2010 +directory contains VC++ 2010 projects for building Google Mock and +selected tests. + +Change to the appropriate directory and run "msbuild gmock.sln" to +build the library and tests (or open the gmock.sln in the MSVC IDE). +If you want to create your own project to use with Google Mock, you'll +have to configure it to use the gmock_config propety sheet. For that: + + * Open the Property Manager window (View | Other Windows | Property Manager) + * Right-click on your project and select "Add Existing Property Sheet..." + * Navigate to gmock_config.vsprops or gmock_config.props and select it. + * In Project Properties | Configuration Properties | General | Additional + Include Directories, type /include. + +Tweaking Google Mock +-------------------- + +Google Mock can be used in diverse environments. The default +configuration may not work (or may not work well) out of the box in +some environments. However, you can easily tweak Google Mock by +defining control macros on the compiler command line. Generally, +these macros are named like GTEST_XYZ and you define them to either 1 +or 0 to enable or disable a certain feature. + +We list the most frequently used macros below. For a complete list, +see file ${GTEST_DIR}/include/gtest/internal/gtest-port.h. + +### Choosing a TR1 Tuple Library ### + +Google Mock uses the C++ Technical Report 1 (TR1) tuple library +heavily. Unfortunately TR1 tuple is not yet widely available with all +compilers. The good news is that Google Test 1.4.0+ implements a +subset of TR1 tuple that's enough for Google Mock's need. Google Mock +will automatically use that implementation when the compiler doesn't +provide TR1 tuple. + +Usually you don't need to care about which tuple library Google Test +and Google Mock use. However, if your project already uses TR1 tuple, +you need to tell Google Test and Google Mock to use the same TR1 tuple +library the rest of your project uses, or the two tuple +implementations will clash. To do that, add + + -DGTEST_USE_OWN_TR1_TUPLE=0 + +to the compiler flags while compiling Google Test, Google Mock, and +your tests. If you want to force Google Test and Google Mock to use +their own tuple library, just add + + -DGTEST_USE_OWN_TR1_TUPLE=1 + +to the compiler flags instead. + +If you want to use Boost's TR1 tuple library with Google Mock, please +refer to the Boost website (http://www.boost.org/) for how to obtain +it and set it up. + +### Tweaking Google Test ### + +Most of Google Test's control macros apply to Google Mock as well. +Please see file ${GTEST_DIR}/README for how to tweak them. + +Upgrading from an Earlier Version +--------------------------------- + +We strive to keep Google Mock releases backward compatible. +Sometimes, though, we have to make some breaking changes for the +users' long-term benefits. This section describes what you'll need to +do if you are upgrading from an earlier version of Google Mock. + +### Upgrading from 1.1.0 or Earlier ### + +You may need to explicitly enable or disable Google Test's own TR1 +tuple library. See the instructions in section "Choosing a TR1 Tuple +Library". + +### Upgrading from 1.4.0 or Earlier ### + +On platforms where the pthread library is available, Google Test and +Google Mock use it in order to be thread-safe. For this to work, you +may need to tweak your compiler and/or linker flags. Please see the +"Multi-threaded Tests" section in file ${GTEST_DIR}/README for what +you may need to do. + +If you have custom matchers defined using MatcherInterface or +MakePolymorphicMatcher(), you'll need to update their definitions to +use the new matcher API [2]. Matchers defined using MATCHER() or +MATCHER_P*() aren't affected. + + [2] http://code.google.com/p/googlemock/wiki/CookBook#Writing_New_Monomorphic_Matchers, + http://code.google.com/p/googlemock/wiki/CookBook#Writing_New_Polymorphic_Matchers + +Developing Google Mock +---------------------- + +This section discusses how to make your own changes to Google Mock. + +### Testing Google Mock Itself ### + +To make sure your changes work as intended and don't break existing +functionality, you'll want to compile and run Google Test's own tests. +For that you'll need Autotools. First, make sure you have followed +the instructions in section "SVN Checkout" to configure Google Mock. +Then, create a build output directory and enter it. Next, + + ${GMOCK_DIR}/configure # Standard GNU configure script, --help for more info + +Once you have successfully configured Google Mock, the build steps are +standard for GNU-style OSS packages. + + make # Standard makefile following GNU conventions + make check # Builds and runs all tests - all should pass. + +Note that when building your project against Google Mock, you are building +against Google Test as well. There is no need to configure Google Test +separately. + +### Regenerating Source Files ### + +Some of Google Mock's source files are generated from templates (not +in the C++ sense) using a script. A template file is named FOO.pump, +where FOO is the name of the file it will generate. For example, the +file include/gmock/gmock-generated-actions.h.pump is used to generate +gmock-generated-actions.h in the same directory. + +Normally you don't need to worry about regenerating the source files, +unless you need to modify them. In that case, you should modify the +corresponding .pump files instead and run the 'pump' script (for Pump +is Useful for Meta Programming) to regenerate them. You can find +pump.py in the ${GTEST_DIR}/scripts/ directory. Read the Pump manual +[3] for how to use it. + + [3] http://code.google.com/p/googletest/wiki/PumpManual. + +### Contributing a Patch ### + +We welcome patches. Please read the Google Mock developer's guide [4] +for how you can contribute. In particular, make sure you have signed +the Contributor License Agreement, or we won't be able to accept the +patch. + + [4] http://code.google.com/p/googlemock/wiki/DevGuide + +Happy testing! diff --git a/libwvdrmengine/test/gmock/README.android b/libwvdrmengine/test/gmock/README.android new file mode 100644 index 00000000..308a2a3d --- /dev/null +++ b/libwvdrmengine/test/gmock/README.android @@ -0,0 +1,34 @@ +URL: https://code.google.com/p/googlemock/downloads/list +Version: 1.6.0 +License: New BSD License + +Description: +Google's framework for writing and using C++ mock classes on a variety of +platforms (Linux, Mac OS X, Windows, Windows CE, Symbian, etc). Inspired by +jMock, EasyMock, and Hamcrest, and designed with C++'s specifics in mind, it can +help you derive better designs of your system and write better tests. + +While gMock supports building with the Android NDK out-of-the-box, it needs some +small modifications to build as part of the Android OS build. Specifically, it +needs a different format of build script, and it needs to build against the OS' +modified version of gTest, rather than the packaged version. + +Local Modifications: +Mon Mar 4, 2013 (juce) + +Added this documentation + +Added Android.mk and src/Android.mk files + +Excluded the following files from being copied to Android repo: + Makefile.in + Makefile.am + aclocal.m4 + CMakeLists.txt + configure + configure.ac + build-aux/ + make/ + msvc/ + fused-src/ + gtest/ \ No newline at end of file diff --git a/libwvdrmengine/test/gmock/include/gmock/gmock-actions.h b/libwvdrmengine/test/gmock/include/gmock/gmock-actions.h new file mode 100644 index 00000000..d6a3e148 --- /dev/null +++ b/libwvdrmengine/test/gmock/include/gmock/gmock-actions.h @@ -0,0 +1,1076 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file implements some commonly used actions. + +#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_ACTIONS_H_ +#define GMOCK_INCLUDE_GMOCK_GMOCK_ACTIONS_H_ + +#include +#include + +#ifndef _WIN32_WCE +# include +#endif + +#include "gmock/internal/gmock-internal-utils.h" +#include "gmock/internal/gmock-port.h" + +namespace testing { + +// To implement an action Foo, define: +// 1. a class FooAction that implements the ActionInterface interface, and +// 2. a factory function that creates an Action object from a +// const FooAction*. +// +// The two-level delegation design follows that of Matcher, providing +// consistency for extension developers. It also eases ownership +// management as Action objects can now be copied like plain values. + +namespace internal { + +template +class ActionAdaptor; + +// BuiltInDefaultValue::Get() returns the "built-in" default +// value for type T, which is NULL when T is a pointer type, 0 when T +// is a numeric type, false when T is bool, or "" when T is string or +// std::string. For any other type T, this value is undefined and the +// function will abort the process. +template +class BuiltInDefaultValue { + public: + // This function returns true iff type T has a built-in default value. + static bool Exists() { return false; } + static T Get() { + Assert(false, __FILE__, __LINE__, + "Default action undefined for the function return type."); + return internal::Invalid(); + // The above statement will never be reached, but is required in + // order for this function to compile. + } +}; + +// This partial specialization says that we use the same built-in +// default value for T and const T. +template +class BuiltInDefaultValue { + public: + static bool Exists() { return BuiltInDefaultValue::Exists(); } + static T Get() { return BuiltInDefaultValue::Get(); } +}; + +// This partial specialization defines the default values for pointer +// types. +template +class BuiltInDefaultValue { + public: + static bool Exists() { return true; } + static T* Get() { return NULL; } +}; + +// The following specializations define the default values for +// specific types we care about. +#define GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(type, value) \ + template <> \ + class BuiltInDefaultValue { \ + public: \ + static bool Exists() { return true; } \ + static type Get() { return value; } \ + } + +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(void, ); // NOLINT +#if GTEST_HAS_GLOBAL_STRING +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(::string, ""); +#endif // GTEST_HAS_GLOBAL_STRING +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(::std::string, ""); +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(bool, false); +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(unsigned char, '\0'); +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(signed char, '\0'); +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(char, '\0'); + +// There's no need for a default action for signed wchar_t, as that +// type is the same as wchar_t for gcc, and invalid for MSVC. +// +// There's also no need for a default action for unsigned wchar_t, as +// that type is the same as unsigned int for gcc, and invalid for +// MSVC. +#if GMOCK_WCHAR_T_IS_NATIVE_ +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(wchar_t, 0U); // NOLINT +#endif + +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(unsigned short, 0U); // NOLINT +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(signed short, 0); // NOLINT +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(unsigned int, 0U); +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(signed int, 0); +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(unsigned long, 0UL); // NOLINT +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(signed long, 0L); // NOLINT +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(UInt64, 0); +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(Int64, 0); +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(float, 0); +GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(double, 0); + +#undef GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_ + +} // namespace internal + +// When an unexpected function call is encountered, Google Mock will +// let it return a default value if the user has specified one for its +// return type, or if the return type has a built-in default value; +// otherwise Google Mock won't know what value to return and will have +// to abort the process. +// +// The DefaultValue class allows a user to specify the +// default value for a type T that is both copyable and publicly +// destructible (i.e. anything that can be used as a function return +// type). The usage is: +// +// // Sets the default value for type T to be foo. +// DefaultValue::Set(foo); +template +class DefaultValue { + public: + // Sets the default value for type T; requires T to be + // copy-constructable and have a public destructor. + static void Set(T x) { + delete value_; + value_ = new T(x); + } + + // Unsets the default value for type T. + static void Clear() { + delete value_; + value_ = NULL; + } + + // Returns true iff the user has set the default value for type T. + static bool IsSet() { return value_ != NULL; } + + // Returns true if T has a default return value set by the user or there + // exists a built-in default value. + static bool Exists() { + return IsSet() || internal::BuiltInDefaultValue::Exists(); + } + + // Returns the default value for type T if the user has set one; + // otherwise returns the built-in default value if there is one; + // otherwise aborts the process. + static T Get() { + return value_ == NULL ? + internal::BuiltInDefaultValue::Get() : *value_; + } + private: + static const T* value_; +}; + +// This partial specialization allows a user to set default values for +// reference types. +template +class DefaultValue { + public: + // Sets the default value for type T&. + static void Set(T& x) { // NOLINT + address_ = &x; + } + + // Unsets the default value for type T&. + static void Clear() { + address_ = NULL; + } + + // Returns true iff the user has set the default value for type T&. + static bool IsSet() { return address_ != NULL; } + + // Returns true if T has a default return value set by the user or there + // exists a built-in default value. + static bool Exists() { + return IsSet() || internal::BuiltInDefaultValue::Exists(); + } + + // Returns the default value for type T& if the user has set one; + // otherwise returns the built-in default value if there is one; + // otherwise aborts the process. + static T& Get() { + return address_ == NULL ? + internal::BuiltInDefaultValue::Get() : *address_; + } + private: + static T* address_; +}; + +// This specialization allows DefaultValue::Get() to +// compile. +template <> +class DefaultValue { + public: + static bool Exists() { return true; } + static void Get() {} +}; + +// Points to the user-set default value for type T. +template +const T* DefaultValue::value_ = NULL; + +// Points to the user-set default value for type T&. +template +T* DefaultValue::address_ = NULL; + +// Implement this interface to define an action for function type F. +template +class ActionInterface { + public: + typedef typename internal::Function::Result Result; + typedef typename internal::Function::ArgumentTuple ArgumentTuple; + + ActionInterface() {} + virtual ~ActionInterface() {} + + // Performs the action. This method is not const, as in general an + // action can have side effects and be stateful. For example, a + // get-the-next-element-from-the-collection action will need to + // remember the current element. + virtual Result Perform(const ArgumentTuple& args) = 0; + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(ActionInterface); +}; + +// An Action is a copyable and IMMUTABLE (except by assignment) +// object that represents an action to be taken when a mock function +// of type F is called. The implementation of Action is just a +// linked_ptr to const ActionInterface, so copying is fairly cheap. +// Don't inherit from Action! +// +// You can view an object implementing ActionInterface as a +// concrete action (including its current state), and an Action +// object as a handle to it. +template +class Action { + public: + typedef typename internal::Function::Result Result; + typedef typename internal::Function::ArgumentTuple ArgumentTuple; + + // Constructs a null Action. Needed for storing Action objects in + // STL containers. + Action() : impl_(NULL) {} + + // Constructs an Action from its implementation. A NULL impl is + // used to represent the "do-default" action. + explicit Action(ActionInterface* impl) : impl_(impl) {} + + // Copy constructor. + Action(const Action& action) : impl_(action.impl_) {} + + // This constructor allows us to turn an Action object into an + // Action, as long as F's arguments can be implicitly converted + // to Func's and Func's return type can be implicitly converted to + // F's. + template + explicit Action(const Action& action); + + // Returns true iff this is the DoDefault() action. + bool IsDoDefault() const { return impl_.get() == NULL; } + + // Performs the action. Note that this method is const even though + // the corresponding method in ActionInterface is not. The reason + // is that a const Action means that it cannot be re-bound to + // another concrete action, not that the concrete action it binds to + // cannot change state. (Think of the difference between a const + // pointer and a pointer to const.) + Result Perform(const ArgumentTuple& args) const { + internal::Assert( + !IsDoDefault(), __FILE__, __LINE__, + "You are using DoDefault() inside a composite action like " + "DoAll() or WithArgs(). This is not supported for technical " + "reasons. Please instead spell out the default action, or " + "assign the default action to an Action variable and use " + "the variable in various places."); + return impl_->Perform(args); + } + + private: + template + friend class internal::ActionAdaptor; + + internal::linked_ptr > impl_; +}; + +// The PolymorphicAction class template makes it easy to implement a +// polymorphic action (i.e. an action that can be used in mock +// functions of than one type, e.g. Return()). +// +// To define a polymorphic action, a user first provides a COPYABLE +// implementation class that has a Perform() method template: +// +// class FooAction { +// public: +// template +// Result Perform(const ArgumentTuple& args) const { +// // Processes the arguments and returns a result, using +// // tr1::get(args) to get the N-th (0-based) argument in the tuple. +// } +// ... +// }; +// +// Then the user creates the polymorphic action using +// MakePolymorphicAction(object) where object has type FooAction. See +// the definition of Return(void) and SetArgumentPointee(value) for +// complete examples. +template +class PolymorphicAction { + public: + explicit PolymorphicAction(const Impl& impl) : impl_(impl) {} + + template + operator Action() const { + return Action(new MonomorphicImpl(impl_)); + } + + private: + template + class MonomorphicImpl : public ActionInterface { + public: + typedef typename internal::Function::Result Result; + typedef typename internal::Function::ArgumentTuple ArgumentTuple; + + explicit MonomorphicImpl(const Impl& impl) : impl_(impl) {} + + virtual Result Perform(const ArgumentTuple& args) { + return impl_.template Perform(args); + } + + private: + Impl impl_; + + GTEST_DISALLOW_ASSIGN_(MonomorphicImpl); + }; + + Impl impl_; + + GTEST_DISALLOW_ASSIGN_(PolymorphicAction); +}; + +// Creates an Action from its implementation and returns it. The +// created Action object owns the implementation. +template +Action MakeAction(ActionInterface* impl) { + return Action(impl); +} + +// Creates a polymorphic action from its implementation. This is +// easier to use than the PolymorphicAction constructor as it +// doesn't require you to explicitly write the template argument, e.g. +// +// MakePolymorphicAction(foo); +// vs +// PolymorphicAction(foo); +template +inline PolymorphicAction MakePolymorphicAction(const Impl& impl) { + return PolymorphicAction(impl); +} + +namespace internal { + +// Allows an Action object to pose as an Action, as long as F2 +// and F1 are compatible. +template +class ActionAdaptor : public ActionInterface { + public: + typedef typename internal::Function::Result Result; + typedef typename internal::Function::ArgumentTuple ArgumentTuple; + + explicit ActionAdaptor(const Action& from) : impl_(from.impl_) {} + + virtual Result Perform(const ArgumentTuple& args) { + return impl_->Perform(args); + } + + private: + const internal::linked_ptr > impl_; + + GTEST_DISALLOW_ASSIGN_(ActionAdaptor); +}; + +// Implements the polymorphic Return(x) action, which can be used in +// any function that returns the type of x, regardless of the argument +// types. +// +// Note: The value passed into Return must be converted into +// Function::Result when this action is cast to Action rather than +// when that action is performed. This is important in scenarios like +// +// MOCK_METHOD1(Method, T(U)); +// ... +// { +// Foo foo; +// X x(&foo); +// EXPECT_CALL(mock, Method(_)).WillOnce(Return(x)); +// } +// +// In the example above the variable x holds reference to foo which leaves +// scope and gets destroyed. If copying X just copies a reference to foo, +// that copy will be left with a hanging reference. If conversion to T +// makes a copy of foo, the above code is safe. To support that scenario, we +// need to make sure that the type conversion happens inside the EXPECT_CALL +// statement, and conversion of the result of Return to Action is a +// good place for that. +// +template +class ReturnAction { + public: + // Constructs a ReturnAction object from the value to be returned. + // 'value' is passed by value instead of by const reference in order + // to allow Return("string literal") to compile. + explicit ReturnAction(R value) : value_(value) {} + + // This template type conversion operator allows Return(x) to be + // used in ANY function that returns x's type. + template + operator Action() const { + // Assert statement belongs here because this is the best place to verify + // conditions on F. It produces the clearest error messages + // in most compilers. + // Impl really belongs in this scope as a local class but can't + // because MSVC produces duplicate symbols in different translation units + // in this case. Until MS fixes that bug we put Impl into the class scope + // and put the typedef both here (for use in assert statement) and + // in the Impl class. But both definitions must be the same. + typedef typename Function::Result Result; + GTEST_COMPILE_ASSERT_( + !internal::is_reference::value, + use_ReturnRef_instead_of_Return_to_return_a_reference); + return Action(new Impl(value_)); + } + + private: + // Implements the Return(x) action for a particular function type F. + template + class Impl : public ActionInterface { + public: + typedef typename Function::Result Result; + typedef typename Function::ArgumentTuple ArgumentTuple; + + // The implicit cast is necessary when Result has more than one + // single-argument constructor (e.g. Result is std::vector) and R + // has a type conversion operator template. In that case, value_(value) + // won't compile as the compiler doesn't known which constructor of + // Result to call. ImplicitCast_ forces the compiler to convert R to + // Result without considering explicit constructors, thus resolving the + // ambiguity. value_ is then initialized using its copy constructor. + explicit Impl(R value) + : value_(::testing::internal::ImplicitCast_(value)) {} + + virtual Result Perform(const ArgumentTuple&) { return value_; } + + private: + GTEST_COMPILE_ASSERT_(!internal::is_reference::value, + Result_cannot_be_a_reference_type); + Result value_; + + GTEST_DISALLOW_ASSIGN_(Impl); + }; + + R value_; + + GTEST_DISALLOW_ASSIGN_(ReturnAction); +}; + +// Implements the ReturnNull() action. +class ReturnNullAction { + public: + // Allows ReturnNull() to be used in any pointer-returning function. + template + static Result Perform(const ArgumentTuple&) { + GTEST_COMPILE_ASSERT_(internal::is_pointer::value, + ReturnNull_can_be_used_to_return_a_pointer_only); + return NULL; + } +}; + +// Implements the Return() action. +class ReturnVoidAction { + public: + // Allows Return() to be used in any void-returning function. + template + static void Perform(const ArgumentTuple&) { + CompileAssertTypesEqual(); + } +}; + +// Implements the polymorphic ReturnRef(x) action, which can be used +// in any function that returns a reference to the type of x, +// regardless of the argument types. +template +class ReturnRefAction { + public: + // Constructs a ReturnRefAction object from the reference to be returned. + explicit ReturnRefAction(T& ref) : ref_(ref) {} // NOLINT + + // This template type conversion operator allows ReturnRef(x) to be + // used in ANY function that returns a reference to x's type. + template + operator Action() const { + typedef typename Function::Result Result; + // Asserts that the function return type is a reference. This + // catches the user error of using ReturnRef(x) when Return(x) + // should be used, and generates some helpful error message. + GTEST_COMPILE_ASSERT_(internal::is_reference::value, + use_Return_instead_of_ReturnRef_to_return_a_value); + return Action(new Impl(ref_)); + } + + private: + // Implements the ReturnRef(x) action for a particular function type F. + template + class Impl : public ActionInterface { + public: + typedef typename Function::Result Result; + typedef typename Function::ArgumentTuple ArgumentTuple; + + explicit Impl(T& ref) : ref_(ref) {} // NOLINT + + virtual Result Perform(const ArgumentTuple&) { + return ref_; + } + + private: + T& ref_; + + GTEST_DISALLOW_ASSIGN_(Impl); + }; + + T& ref_; + + GTEST_DISALLOW_ASSIGN_(ReturnRefAction); +}; + +// Implements the polymorphic ReturnRefOfCopy(x) action, which can be +// used in any function that returns a reference to the type of x, +// regardless of the argument types. +template +class ReturnRefOfCopyAction { + public: + // Constructs a ReturnRefOfCopyAction object from the reference to + // be returned. + explicit ReturnRefOfCopyAction(const T& value) : value_(value) {} // NOLINT + + // This template type conversion operator allows ReturnRefOfCopy(x) to be + // used in ANY function that returns a reference to x's type. + template + operator Action() const { + typedef typename Function::Result Result; + // Asserts that the function return type is a reference. This + // catches the user error of using ReturnRefOfCopy(x) when Return(x) + // should be used, and generates some helpful error message. + GTEST_COMPILE_ASSERT_( + internal::is_reference::value, + use_Return_instead_of_ReturnRefOfCopy_to_return_a_value); + return Action(new Impl(value_)); + } + + private: + // Implements the ReturnRefOfCopy(x) action for a particular function type F. + template + class Impl : public ActionInterface { + public: + typedef typename Function::Result Result; + typedef typename Function::ArgumentTuple ArgumentTuple; + + explicit Impl(const T& value) : value_(value) {} // NOLINT + + virtual Result Perform(const ArgumentTuple&) { + return value_; + } + + private: + T value_; + + GTEST_DISALLOW_ASSIGN_(Impl); + }; + + const T value_; + + GTEST_DISALLOW_ASSIGN_(ReturnRefOfCopyAction); +}; + +// Implements the polymorphic DoDefault() action. +class DoDefaultAction { + public: + // This template type conversion operator allows DoDefault() to be + // used in any function. + template + operator Action() const { return Action(NULL); } +}; + +// Implements the Assign action to set a given pointer referent to a +// particular value. +template +class AssignAction { + public: + AssignAction(T1* ptr, T2 value) : ptr_(ptr), value_(value) {} + + template + void Perform(const ArgumentTuple& /* args */) const { + *ptr_ = value_; + } + + private: + T1* const ptr_; + const T2 value_; + + GTEST_DISALLOW_ASSIGN_(AssignAction); +}; + +#if !GTEST_OS_WINDOWS_MOBILE + +// Implements the SetErrnoAndReturn action to simulate return from +// various system calls and libc functions. +template +class SetErrnoAndReturnAction { + public: + SetErrnoAndReturnAction(int errno_value, T result) + : errno_(errno_value), + result_(result) {} + template + Result Perform(const ArgumentTuple& /* args */) const { + errno = errno_; + return result_; + } + + private: + const int errno_; + const T result_; + + GTEST_DISALLOW_ASSIGN_(SetErrnoAndReturnAction); +}; + +#endif // !GTEST_OS_WINDOWS_MOBILE + +// Implements the SetArgumentPointee(x) action for any function +// whose N-th argument (0-based) is a pointer to x's type. The +// template parameter kIsProto is true iff type A is ProtocolMessage, +// proto2::Message, or a sub-class of those. +template +class SetArgumentPointeeAction { + public: + // Constructs an action that sets the variable pointed to by the + // N-th function argument to 'value'. + explicit SetArgumentPointeeAction(const A& value) : value_(value) {} + + template + void Perform(const ArgumentTuple& args) const { + CompileAssertTypesEqual(); + *::std::tr1::get(args) = value_; + } + + private: + const A value_; + + GTEST_DISALLOW_ASSIGN_(SetArgumentPointeeAction); +}; + +template +class SetArgumentPointeeAction { + public: + // Constructs an action that sets the variable pointed to by the + // N-th function argument to 'proto'. Both ProtocolMessage and + // proto2::Message have the CopyFrom() method, so the same + // implementation works for both. + explicit SetArgumentPointeeAction(const Proto& proto) : proto_(new Proto) { + proto_->CopyFrom(proto); + } + + template + void Perform(const ArgumentTuple& args) const { + CompileAssertTypesEqual(); + ::std::tr1::get(args)->CopyFrom(*proto_); + } + + private: + const internal::linked_ptr proto_; + + GTEST_DISALLOW_ASSIGN_(SetArgumentPointeeAction); +}; + +// Implements the InvokeWithoutArgs(f) action. The template argument +// FunctionImpl is the implementation type of f, which can be either a +// function pointer or a functor. InvokeWithoutArgs(f) can be used as an +// Action as long as f's type is compatible with F (i.e. f can be +// assigned to a tr1::function). +template +class InvokeWithoutArgsAction { + public: + // The c'tor makes a copy of function_impl (either a function + // pointer or a functor). + explicit InvokeWithoutArgsAction(FunctionImpl function_impl) + : function_impl_(function_impl) {} + + // Allows InvokeWithoutArgs(f) to be used as any action whose type is + // compatible with f. + template + Result Perform(const ArgumentTuple&) { return function_impl_(); } + + private: + FunctionImpl function_impl_; + + GTEST_DISALLOW_ASSIGN_(InvokeWithoutArgsAction); +}; + +// Implements the InvokeWithoutArgs(object_ptr, &Class::Method) action. +template +class InvokeMethodWithoutArgsAction { + public: + InvokeMethodWithoutArgsAction(Class* obj_ptr, MethodPtr method_ptr) + : obj_ptr_(obj_ptr), method_ptr_(method_ptr) {} + + template + Result Perform(const ArgumentTuple&) const { + return (obj_ptr_->*method_ptr_)(); + } + + private: + Class* const obj_ptr_; + const MethodPtr method_ptr_; + + GTEST_DISALLOW_ASSIGN_(InvokeMethodWithoutArgsAction); +}; + +// Implements the IgnoreResult(action) action. +template +class IgnoreResultAction { + public: + explicit IgnoreResultAction(const A& action) : action_(action) {} + + template + operator Action() const { + // Assert statement belongs here because this is the best place to verify + // conditions on F. It produces the clearest error messages + // in most compilers. + // Impl really belongs in this scope as a local class but can't + // because MSVC produces duplicate symbols in different translation units + // in this case. Until MS fixes that bug we put Impl into the class scope + // and put the typedef both here (for use in assert statement) and + // in the Impl class. But both definitions must be the same. + typedef typename internal::Function::Result Result; + + // Asserts at compile time that F returns void. + CompileAssertTypesEqual(); + + return Action(new Impl(action_)); + } + + private: + template + class Impl : public ActionInterface { + public: + typedef typename internal::Function::Result Result; + typedef typename internal::Function::ArgumentTuple ArgumentTuple; + + explicit Impl(const A& action) : action_(action) {} + + virtual void Perform(const ArgumentTuple& args) { + // Performs the action and ignores its result. + action_.Perform(args); + } + + private: + // Type OriginalFunction is the same as F except that its return + // type is IgnoredValue. + typedef typename internal::Function::MakeResultIgnoredValue + OriginalFunction; + + const Action action_; + + GTEST_DISALLOW_ASSIGN_(Impl); + }; + + const A action_; + + GTEST_DISALLOW_ASSIGN_(IgnoreResultAction); +}; + +// A ReferenceWrapper object represents a reference to type T, +// which can be either const or not. It can be explicitly converted +// from, and implicitly converted to, a T&. Unlike a reference, +// ReferenceWrapper can be copied and can survive template type +// inference. This is used to support by-reference arguments in the +// InvokeArgument(...) action. The idea was from "reference +// wrappers" in tr1, which we don't have in our source tree yet. +template +class ReferenceWrapper { + public: + // Constructs a ReferenceWrapper object from a T&. + explicit ReferenceWrapper(T& l_value) : pointer_(&l_value) {} // NOLINT + + // Allows a ReferenceWrapper object to be implicitly converted to + // a T&. + operator T&() const { return *pointer_; } + private: + T* pointer_; +}; + +// Allows the expression ByRef(x) to be printed as a reference to x. +template +void PrintTo(const ReferenceWrapper& ref, ::std::ostream* os) { + T& value = ref; + UniversalPrinter::Print(value, os); +} + +// Does two actions sequentially. Used for implementing the DoAll(a1, +// a2, ...) action. +template +class DoBothAction { + public: + DoBothAction(Action1 action1, Action2 action2) + : action1_(action1), action2_(action2) {} + + // This template type conversion operator allows DoAll(a1, ..., a_n) + // to be used in ANY function of compatible type. + template + operator Action() const { + return Action(new Impl(action1_, action2_)); + } + + private: + // Implements the DoAll(...) action for a particular function type F. + template + class Impl : public ActionInterface { + public: + typedef typename Function::Result Result; + typedef typename Function::ArgumentTuple ArgumentTuple; + typedef typename Function::MakeResultVoid VoidResult; + + Impl(const Action& action1, const Action& action2) + : action1_(action1), action2_(action2) {} + + virtual Result Perform(const ArgumentTuple& args) { + action1_.Perform(args); + return action2_.Perform(args); + } + + private: + const Action action1_; + const Action action2_; + + GTEST_DISALLOW_ASSIGN_(Impl); + }; + + Action1 action1_; + Action2 action2_; + + GTEST_DISALLOW_ASSIGN_(DoBothAction); +}; + +} // namespace internal + +// An Unused object can be implicitly constructed from ANY value. +// This is handy when defining actions that ignore some or all of the +// mock function arguments. For example, given +// +// MOCK_METHOD3(Foo, double(const string& label, double x, double y)); +// MOCK_METHOD3(Bar, double(int index, double x, double y)); +// +// instead of +// +// double DistanceToOriginWithLabel(const string& label, double x, double y) { +// return sqrt(x*x + y*y); +// } +// double DistanceToOriginWithIndex(int index, double x, double y) { +// return sqrt(x*x + y*y); +// } +// ... +// EXEPCT_CALL(mock, Foo("abc", _, _)) +// .WillOnce(Invoke(DistanceToOriginWithLabel)); +// EXEPCT_CALL(mock, Bar(5, _, _)) +// .WillOnce(Invoke(DistanceToOriginWithIndex)); +// +// you could write +// +// // We can declare any uninteresting argument as Unused. +// double DistanceToOrigin(Unused, double x, double y) { +// return sqrt(x*x + y*y); +// } +// ... +// EXEPCT_CALL(mock, Foo("abc", _, _)).WillOnce(Invoke(DistanceToOrigin)); +// EXEPCT_CALL(mock, Bar(5, _, _)).WillOnce(Invoke(DistanceToOrigin)); +typedef internal::IgnoredValue Unused; + +// This constructor allows us to turn an Action object into an +// Action, as long as To's arguments can be implicitly converted +// to From's and From's return type cann be implicitly converted to +// To's. +template +template +Action::Action(const Action& from) + : impl_(new internal::ActionAdaptor(from)) {} + +// Creates an action that returns 'value'. 'value' is passed by value +// instead of const reference - otherwise Return("string literal") +// will trigger a compiler error about using array as initializer. +template +internal::ReturnAction Return(R value) { + return internal::ReturnAction(value); +} + +// Creates an action that returns NULL. +inline PolymorphicAction ReturnNull() { + return MakePolymorphicAction(internal::ReturnNullAction()); +} + +// Creates an action that returns from a void function. +inline PolymorphicAction Return() { + return MakePolymorphicAction(internal::ReturnVoidAction()); +} + +// Creates an action that returns the reference to a variable. +template +inline internal::ReturnRefAction ReturnRef(R& x) { // NOLINT + return internal::ReturnRefAction(x); +} + +// Creates an action that returns the reference to a copy of the +// argument. The copy is created when the action is constructed and +// lives as long as the action. +template +inline internal::ReturnRefOfCopyAction ReturnRefOfCopy(const R& x) { + return internal::ReturnRefOfCopyAction(x); +} + +// Creates an action that does the default action for the give mock function. +inline internal::DoDefaultAction DoDefault() { + return internal::DoDefaultAction(); +} + +// Creates an action that sets the variable pointed by the N-th +// (0-based) function argument to 'value'. +template +PolymorphicAction< + internal::SetArgumentPointeeAction< + N, T, internal::IsAProtocolMessage::value> > +SetArgPointee(const T& x) { + return MakePolymorphicAction(internal::SetArgumentPointeeAction< + N, T, internal::IsAProtocolMessage::value>(x)); +} + +#if !((GTEST_GCC_VER_ && GTEST_GCC_VER_ < 40000) || GTEST_OS_SYMBIAN) +// This overload allows SetArgPointee() to accept a string literal. +// GCC prior to the version 4.0 and Symbian C++ compiler cannot distinguish +// this overload from the templated version and emit a compile error. +template +PolymorphicAction< + internal::SetArgumentPointeeAction > +SetArgPointee(const char* p) { + return MakePolymorphicAction(internal::SetArgumentPointeeAction< + N, const char*, false>(p)); +} + +template +PolymorphicAction< + internal::SetArgumentPointeeAction > +SetArgPointee(const wchar_t* p) { + return MakePolymorphicAction(internal::SetArgumentPointeeAction< + N, const wchar_t*, false>(p)); +} +#endif + +// The following version is DEPRECATED. +template +PolymorphicAction< + internal::SetArgumentPointeeAction< + N, T, internal::IsAProtocolMessage::value> > +SetArgumentPointee(const T& x) { + return MakePolymorphicAction(internal::SetArgumentPointeeAction< + N, T, internal::IsAProtocolMessage::value>(x)); +} + +// Creates an action that sets a pointer referent to a given value. +template +PolymorphicAction > Assign(T1* ptr, T2 val) { + return MakePolymorphicAction(internal::AssignAction(ptr, val)); +} + +#if !GTEST_OS_WINDOWS_MOBILE + +// Creates an action that sets errno and returns the appropriate error. +template +PolymorphicAction > +SetErrnoAndReturn(int errval, T result) { + return MakePolymorphicAction( + internal::SetErrnoAndReturnAction(errval, result)); +} + +#endif // !GTEST_OS_WINDOWS_MOBILE + +// Various overloads for InvokeWithoutArgs(). + +// Creates an action that invokes 'function_impl' with no argument. +template +PolymorphicAction > +InvokeWithoutArgs(FunctionImpl function_impl) { + return MakePolymorphicAction( + internal::InvokeWithoutArgsAction(function_impl)); +} + +// Creates an action that invokes the given method on the given object +// with no argument. +template +PolymorphicAction > +InvokeWithoutArgs(Class* obj_ptr, MethodPtr method_ptr) { + return MakePolymorphicAction( + internal::InvokeMethodWithoutArgsAction( + obj_ptr, method_ptr)); +} + +// Creates an action that performs an_action and throws away its +// result. In other words, it changes the return type of an_action to +// void. an_action MUST NOT return void, or the code won't compile. +template +inline internal::IgnoreResultAction IgnoreResult(const A& an_action) { + return internal::IgnoreResultAction(an_action); +} + +// Creates a reference wrapper for the given L-value. If necessary, +// you can explicitly specify the type of the reference. For example, +// suppose 'derived' is an object of type Derived, ByRef(derived) +// would wrap a Derived&. If you want to wrap a const Base& instead, +// where Base is a base class of Derived, just write: +// +// ByRef(derived) +template +inline internal::ReferenceWrapper ByRef(T& l_value) { // NOLINT + return internal::ReferenceWrapper(l_value); +} + +} // namespace testing + +#endif // GMOCK_INCLUDE_GMOCK_GMOCK_ACTIONS_H_ diff --git a/libwvdrmengine/test/gmock/include/gmock/gmock-cardinalities.h b/libwvdrmengine/test/gmock/include/gmock/gmock-cardinalities.h new file mode 100644 index 00000000..954a86ea --- /dev/null +++ b/libwvdrmengine/test/gmock/include/gmock/gmock-cardinalities.h @@ -0,0 +1,146 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file implements some commonly used cardinalities. More +// cardinalities can be defined by the user implementing the +// CardinalityInterface interface if necessary. + +#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_CARDINALITIES_H_ +#define GMOCK_INCLUDE_GMOCK_GMOCK_CARDINALITIES_H_ + +#include +#include // NOLINT +#include "gmock/internal/gmock-port.h" +#include "gtest/gtest.h" + +namespace testing { + +// To implement a cardinality Foo, define: +// 1. a class FooCardinality that implements the +// CardinalityInterface interface, and +// 2. a factory function that creates a Cardinality object from a +// const FooCardinality*. +// +// The two-level delegation design follows that of Matcher, providing +// consistency for extension developers. It also eases ownership +// management as Cardinality objects can now be copied like plain values. + +// The implementation of a cardinality. +class CardinalityInterface { + public: + virtual ~CardinalityInterface() {} + + // Conservative estimate on the lower/upper bound of the number of + // calls allowed. + virtual int ConservativeLowerBound() const { return 0; } + virtual int ConservativeUpperBound() const { return INT_MAX; } + + // Returns true iff call_count calls will satisfy this cardinality. + virtual bool IsSatisfiedByCallCount(int call_count) const = 0; + + // Returns true iff call_count calls will saturate this cardinality. + virtual bool IsSaturatedByCallCount(int call_count) const = 0; + + // Describes self to an ostream. + virtual void DescribeTo(::std::ostream* os) const = 0; +}; + +// A Cardinality is a copyable and IMMUTABLE (except by assignment) +// object that specifies how many times a mock function is expected to +// be called. The implementation of Cardinality is just a linked_ptr +// to const CardinalityInterface, so copying is fairly cheap. +// Don't inherit from Cardinality! +class Cardinality { + public: + // Constructs a null cardinality. Needed for storing Cardinality + // objects in STL containers. + Cardinality() {} + + // Constructs a Cardinality from its implementation. + explicit Cardinality(const CardinalityInterface* impl) : impl_(impl) {} + + // Conservative estimate on the lower/upper bound of the number of + // calls allowed. + int ConservativeLowerBound() const { return impl_->ConservativeLowerBound(); } + int ConservativeUpperBound() const { return impl_->ConservativeUpperBound(); } + + // Returns true iff call_count calls will satisfy this cardinality. + bool IsSatisfiedByCallCount(int call_count) const { + return impl_->IsSatisfiedByCallCount(call_count); + } + + // Returns true iff call_count calls will saturate this cardinality. + bool IsSaturatedByCallCount(int call_count) const { + return impl_->IsSaturatedByCallCount(call_count); + } + + // Returns true iff call_count calls will over-saturate this + // cardinality, i.e. exceed the maximum number of allowed calls. + bool IsOverSaturatedByCallCount(int call_count) const { + return impl_->IsSaturatedByCallCount(call_count) && + !impl_->IsSatisfiedByCallCount(call_count); + } + + // Describes self to an ostream + void DescribeTo(::std::ostream* os) const { impl_->DescribeTo(os); } + + // Describes the given actual call count to an ostream. + static void DescribeActualCallCountTo(int actual_call_count, + ::std::ostream* os); + private: + internal::linked_ptr impl_; +}; + +// Creates a cardinality that allows at least n calls. +Cardinality AtLeast(int n); + +// Creates a cardinality that allows at most n calls. +Cardinality AtMost(int n); + +// Creates a cardinality that allows any number of calls. +Cardinality AnyNumber(); + +// Creates a cardinality that allows between min and max calls. +Cardinality Between(int min, int max); + +// Creates a cardinality that allows exactly n calls. +Cardinality Exactly(int n); + +// Creates a cardinality from its implementation. +inline Cardinality MakeCardinality(const CardinalityInterface* c) { + return Cardinality(c); +} + +} // namespace testing + +#endif // GMOCK_INCLUDE_GMOCK_GMOCK_CARDINALITIES_H_ diff --git a/libwvdrmengine/test/gmock/include/gmock/gmock-generated-actions.h b/libwvdrmengine/test/gmock/include/gmock/gmock-generated-actions.h new file mode 100644 index 00000000..635bb595 --- /dev/null +++ b/libwvdrmengine/test/gmock/include/gmock/gmock-generated-actions.h @@ -0,0 +1,2419 @@ +// This file was GENERATED by a script. DO NOT EDIT BY HAND!!! + +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file implements some commonly used variadic actions. + +#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_ACTIONS_H_ +#define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_ACTIONS_H_ + +#include "gmock/gmock-actions.h" +#include "gmock/internal/gmock-port.h" + +namespace testing { +namespace internal { + +// InvokeHelper knows how to unpack an N-tuple and invoke an N-ary +// function or method with the unpacked values, where F is a function +// type that takes N arguments. +template +class InvokeHelper; + +template +class InvokeHelper > { + public: + template + static R Invoke(Function function, const ::std::tr1::tuple<>&) { + return function(); + } + + template + static R InvokeMethod(Class* obj_ptr, + MethodPtr method_ptr, + const ::std::tr1::tuple<>&) { + return (obj_ptr->*method_ptr)(); + } +}; + +template +class InvokeHelper > { + public: + template + static R Invoke(Function function, const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return function(get<0>(args)); + } + + template + static R InvokeMethod(Class* obj_ptr, + MethodPtr method_ptr, + const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return (obj_ptr->*method_ptr)(get<0>(args)); + } +}; + +template +class InvokeHelper > { + public: + template + static R Invoke(Function function, const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return function(get<0>(args), get<1>(args)); + } + + template + static R InvokeMethod(Class* obj_ptr, + MethodPtr method_ptr, + const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args)); + } +}; + +template +class InvokeHelper > { + public: + template + static R Invoke(Function function, const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return function(get<0>(args), get<1>(args), get<2>(args)); + } + + template + static R InvokeMethod(Class* obj_ptr, + MethodPtr method_ptr, + const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args), get<2>(args)); + } +}; + +template +class InvokeHelper > { + public: + template + static R Invoke(Function function, const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return function(get<0>(args), get<1>(args), get<2>(args), get<3>(args)); + } + + template + static R InvokeMethod(Class* obj_ptr, + MethodPtr method_ptr, + const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args), get<2>(args), + get<3>(args)); + } +}; + +template +class InvokeHelper > { + public: + template + static R Invoke(Function function, const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return function(get<0>(args), get<1>(args), get<2>(args), get<3>(args), + get<4>(args)); + } + + template + static R InvokeMethod(Class* obj_ptr, + MethodPtr method_ptr, + const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args), get<2>(args), + get<3>(args), get<4>(args)); + } +}; + +template +class InvokeHelper > { + public: + template + static R Invoke(Function function, const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return function(get<0>(args), get<1>(args), get<2>(args), get<3>(args), + get<4>(args), get<5>(args)); + } + + template + static R InvokeMethod(Class* obj_ptr, + MethodPtr method_ptr, + const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args), get<2>(args), + get<3>(args), get<4>(args), get<5>(args)); + } +}; + +template +class InvokeHelper > { + public: + template + static R Invoke(Function function, const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return function(get<0>(args), get<1>(args), get<2>(args), get<3>(args), + get<4>(args), get<5>(args), get<6>(args)); + } + + template + static R InvokeMethod(Class* obj_ptr, + MethodPtr method_ptr, + const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args), get<2>(args), + get<3>(args), get<4>(args), get<5>(args), get<6>(args)); + } +}; + +template +class InvokeHelper > { + public: + template + static R Invoke(Function function, const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return function(get<0>(args), get<1>(args), get<2>(args), get<3>(args), + get<4>(args), get<5>(args), get<6>(args), get<7>(args)); + } + + template + static R InvokeMethod(Class* obj_ptr, + MethodPtr method_ptr, + const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args), get<2>(args), + get<3>(args), get<4>(args), get<5>(args), get<6>(args), get<7>(args)); + } +}; + +template +class InvokeHelper > { + public: + template + static R Invoke(Function function, const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return function(get<0>(args), get<1>(args), get<2>(args), get<3>(args), + get<4>(args), get<5>(args), get<6>(args), get<7>(args), get<8>(args)); + } + + template + static R InvokeMethod(Class* obj_ptr, + MethodPtr method_ptr, + const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args), get<2>(args), + get<3>(args), get<4>(args), get<5>(args), get<6>(args), get<7>(args), + get<8>(args)); + } +}; + +template +class InvokeHelper > { + public: + template + static R Invoke(Function function, const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return function(get<0>(args), get<1>(args), get<2>(args), get<3>(args), + get<4>(args), get<5>(args), get<6>(args), get<7>(args), get<8>(args), + get<9>(args)); + } + + template + static R InvokeMethod(Class* obj_ptr, + MethodPtr method_ptr, + const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args), get<2>(args), + get<3>(args), get<4>(args), get<5>(args), get<6>(args), get<7>(args), + get<8>(args), get<9>(args)); + } +}; + +// CallableHelper has static methods for invoking "callables", +// i.e. function pointers and functors. It uses overloading to +// provide a uniform interface for invoking different kinds of +// callables. In particular, you can use: +// +// CallableHelper::Call(callable, a1, a2, ..., an) +// +// to invoke an n-ary callable, where R is its return type. If an +// argument, say a2, needs to be passed by reference, you should write +// ByRef(a2) instead of a2 in the above expression. +template +class CallableHelper { + public: + // Calls a nullary callable. + template + static R Call(Function function) { return function(); } + + // Calls a unary callable. + + // We deliberately pass a1 by value instead of const reference here + // in case it is a C-string literal. If we had declared the + // parameter as 'const A1& a1' and write Call(function, "Hi"), the + // compiler would've thought A1 is 'char[3]', which causes trouble + // when you need to copy a value of type A1. By declaring the + // parameter as 'A1 a1', the compiler will correctly infer that A1 + // is 'const char*' when it sees Call(function, "Hi"). + // + // Since this function is defined inline, the compiler can get rid + // of the copying of the arguments. Therefore the performance won't + // be hurt. + template + static R Call(Function function, A1 a1) { return function(a1); } + + // Calls a binary callable. + template + static R Call(Function function, A1 a1, A2 a2) { + return function(a1, a2); + } + + // Calls a ternary callable. + template + static R Call(Function function, A1 a1, A2 a2, A3 a3) { + return function(a1, a2, a3); + } + + // Calls a 4-ary callable. + template + static R Call(Function function, A1 a1, A2 a2, A3 a3, A4 a4) { + return function(a1, a2, a3, a4); + } + + // Calls a 5-ary callable. + template + static R Call(Function function, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { + return function(a1, a2, a3, a4, a5); + } + + // Calls a 6-ary callable. + template + static R Call(Function function, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { + return function(a1, a2, a3, a4, a5, a6); + } + + // Calls a 7-ary callable. + template + static R Call(Function function, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, + A7 a7) { + return function(a1, a2, a3, a4, a5, a6, a7); + } + + // Calls a 8-ary callable. + template + static R Call(Function function, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, + A7 a7, A8 a8) { + return function(a1, a2, a3, a4, a5, a6, a7, a8); + } + + // Calls a 9-ary callable. + template + static R Call(Function function, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, + A7 a7, A8 a8, A9 a9) { + return function(a1, a2, a3, a4, a5, a6, a7, a8, a9); + } + + // Calls a 10-ary callable. + template + static R Call(Function function, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, + A7 a7, A8 a8, A9 a9, A10 a10) { + return function(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); + } + +}; // class CallableHelper + +// An INTERNAL macro for extracting the type of a tuple field. It's +// subject to change without notice - DO NOT USE IN USER CODE! +#define GMOCK_FIELD_(Tuple, N) \ + typename ::std::tr1::tuple_element::type + +// SelectArgs::type is the +// type of an n-ary function whose i-th (1-based) argument type is the +// k{i}-th (0-based) field of ArgumentTuple, which must be a tuple +// type, and whose return type is Result. For example, +// SelectArgs, 0, 3>::type +// is int(bool, long). +// +// SelectArgs::Select(args) +// returns the selected fields (k1, k2, ..., k_n) of args as a tuple. +// For example, +// SelectArgs, 2, 0>::Select( +// ::std::tr1::make_tuple(true, 'a', 2.5)) +// returns ::std::tr1::tuple (2.5, true). +// +// The numbers in list k1, k2, ..., k_n must be >= 0, where n can be +// in the range [0, 10]. Duplicates are allowed and they don't have +// to be in an ascending or descending order. + +template +class SelectArgs { + public: + typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1), + GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3), + GMOCK_FIELD_(ArgumentTuple, k4), GMOCK_FIELD_(ArgumentTuple, k5), + GMOCK_FIELD_(ArgumentTuple, k6), GMOCK_FIELD_(ArgumentTuple, k7), + GMOCK_FIELD_(ArgumentTuple, k8), GMOCK_FIELD_(ArgumentTuple, k9), + GMOCK_FIELD_(ArgumentTuple, k10)); + typedef typename Function::ArgumentTuple SelectedArgs; + static SelectedArgs Select(const ArgumentTuple& args) { + using ::std::tr1::get; + return SelectedArgs(get(args), get(args), get(args), + get(args), get(args), get(args), get(args), + get(args), get(args), get(args)); + } +}; + +template +class SelectArgs { + public: + typedef Result type(); + typedef typename Function::ArgumentTuple SelectedArgs; + static SelectedArgs Select(const ArgumentTuple& /* args */) { + using ::std::tr1::get; + return SelectedArgs(); + } +}; + +template +class SelectArgs { + public: + typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1)); + typedef typename Function::ArgumentTuple SelectedArgs; + static SelectedArgs Select(const ArgumentTuple& args) { + using ::std::tr1::get; + return SelectedArgs(get(args)); + } +}; + +template +class SelectArgs { + public: + typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1), + GMOCK_FIELD_(ArgumentTuple, k2)); + typedef typename Function::ArgumentTuple SelectedArgs; + static SelectedArgs Select(const ArgumentTuple& args) { + using ::std::tr1::get; + return SelectedArgs(get(args), get(args)); + } +}; + +template +class SelectArgs { + public: + typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1), + GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3)); + typedef typename Function::ArgumentTuple SelectedArgs; + static SelectedArgs Select(const ArgumentTuple& args) { + using ::std::tr1::get; + return SelectedArgs(get(args), get(args), get(args)); + } +}; + +template +class SelectArgs { + public: + typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1), + GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3), + GMOCK_FIELD_(ArgumentTuple, k4)); + typedef typename Function::ArgumentTuple SelectedArgs; + static SelectedArgs Select(const ArgumentTuple& args) { + using ::std::tr1::get; + return SelectedArgs(get(args), get(args), get(args), + get(args)); + } +}; + +template +class SelectArgs { + public: + typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1), + GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3), + GMOCK_FIELD_(ArgumentTuple, k4), GMOCK_FIELD_(ArgumentTuple, k5)); + typedef typename Function::ArgumentTuple SelectedArgs; + static SelectedArgs Select(const ArgumentTuple& args) { + using ::std::tr1::get; + return SelectedArgs(get(args), get(args), get(args), + get(args), get(args)); + } +}; + +template +class SelectArgs { + public: + typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1), + GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3), + GMOCK_FIELD_(ArgumentTuple, k4), GMOCK_FIELD_(ArgumentTuple, k5), + GMOCK_FIELD_(ArgumentTuple, k6)); + typedef typename Function::ArgumentTuple SelectedArgs; + static SelectedArgs Select(const ArgumentTuple& args) { + using ::std::tr1::get; + return SelectedArgs(get(args), get(args), get(args), + get(args), get(args), get(args)); + } +}; + +template +class SelectArgs { + public: + typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1), + GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3), + GMOCK_FIELD_(ArgumentTuple, k4), GMOCK_FIELD_(ArgumentTuple, k5), + GMOCK_FIELD_(ArgumentTuple, k6), GMOCK_FIELD_(ArgumentTuple, k7)); + typedef typename Function::ArgumentTuple SelectedArgs; + static SelectedArgs Select(const ArgumentTuple& args) { + using ::std::tr1::get; + return SelectedArgs(get(args), get(args), get(args), + get(args), get(args), get(args), get(args)); + } +}; + +template +class SelectArgs { + public: + typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1), + GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3), + GMOCK_FIELD_(ArgumentTuple, k4), GMOCK_FIELD_(ArgumentTuple, k5), + GMOCK_FIELD_(ArgumentTuple, k6), GMOCK_FIELD_(ArgumentTuple, k7), + GMOCK_FIELD_(ArgumentTuple, k8)); + typedef typename Function::ArgumentTuple SelectedArgs; + static SelectedArgs Select(const ArgumentTuple& args) { + using ::std::tr1::get; + return SelectedArgs(get(args), get(args), get(args), + get(args), get(args), get(args), get(args), + get(args)); + } +}; + +template +class SelectArgs { + public: + typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1), + GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3), + GMOCK_FIELD_(ArgumentTuple, k4), GMOCK_FIELD_(ArgumentTuple, k5), + GMOCK_FIELD_(ArgumentTuple, k6), GMOCK_FIELD_(ArgumentTuple, k7), + GMOCK_FIELD_(ArgumentTuple, k8), GMOCK_FIELD_(ArgumentTuple, k9)); + typedef typename Function::ArgumentTuple SelectedArgs; + static SelectedArgs Select(const ArgumentTuple& args) { + using ::std::tr1::get; + return SelectedArgs(get(args), get(args), get(args), + get(args), get(args), get(args), get(args), + get(args), get(args)); + } +}; + +#undef GMOCK_FIELD_ + +// Implements the WithArgs action. +template +class WithArgsAction { + public: + explicit WithArgsAction(const InnerAction& action) : action_(action) {} + + template + operator Action() const { return MakeAction(new Impl(action_)); } + + private: + template + class Impl : public ActionInterface { + public: + typedef typename Function::Result Result; + typedef typename Function::ArgumentTuple ArgumentTuple; + + explicit Impl(const InnerAction& action) : action_(action) {} + + virtual Result Perform(const ArgumentTuple& args) { + return action_.Perform(SelectArgs::Select(args)); + } + + private: + typedef typename SelectArgs::type InnerFunctionType; + + Action action_; + }; + + const InnerAction action_; + + GTEST_DISALLOW_ASSIGN_(WithArgsAction); +}; + +// A macro from the ACTION* family (defined later in this file) +// defines an action that can be used in a mock function. Typically, +// these actions only care about a subset of the arguments of the mock +// function. For example, if such an action only uses the second +// argument, it can be used in any mock function that takes >= 2 +// arguments where the type of the second argument is compatible. +// +// Therefore, the action implementation must be prepared to take more +// arguments than it needs. The ExcessiveArg type is used to +// represent those excessive arguments. In order to keep the compiler +// error messages tractable, we define it in the testing namespace +// instead of testing::internal. However, this is an INTERNAL TYPE +// and subject to change without notice, so a user MUST NOT USE THIS +// TYPE DIRECTLY. +struct ExcessiveArg {}; + +// A helper class needed for implementing the ACTION* macros. +template +class ActionHelper { + public: + static Result Perform(Impl* impl, const ::std::tr1::tuple<>& args) { + using ::std::tr1::get; + return impl->template gmock_PerformImpl<>(args, ExcessiveArg(), + ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg()); + } + + template + static Result Perform(Impl* impl, const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return impl->template gmock_PerformImpl(args, get<0>(args), + ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg()); + } + + template + static Result Perform(Impl* impl, const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return impl->template gmock_PerformImpl(args, get<0>(args), + get<1>(args), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg()); + } + + template + static Result Perform(Impl* impl, const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return impl->template gmock_PerformImpl(args, get<0>(args), + get<1>(args), get<2>(args), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg()); + } + + template + static Result Perform(Impl* impl, const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return impl->template gmock_PerformImpl(args, get<0>(args), + get<1>(args), get<2>(args), get<3>(args), ExcessiveArg(), + ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg()); + } + + template + static Result Perform(Impl* impl, const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return impl->template gmock_PerformImpl(args, + get<0>(args), get<1>(args), get<2>(args), get<3>(args), get<4>(args), + ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg()); + } + + template + static Result Perform(Impl* impl, const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return impl->template gmock_PerformImpl(args, + get<0>(args), get<1>(args), get<2>(args), get<3>(args), get<4>(args), + get<5>(args), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg()); + } + + template + static Result Perform(Impl* impl, const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return impl->template gmock_PerformImpl(args, + get<0>(args), get<1>(args), get<2>(args), get<3>(args), get<4>(args), + get<5>(args), get<6>(args), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg()); + } + + template + static Result Perform(Impl* impl, const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return impl->template gmock_PerformImpl(args, get<0>(args), get<1>(args), get<2>(args), get<3>(args), + get<4>(args), get<5>(args), get<6>(args), get<7>(args), ExcessiveArg(), + ExcessiveArg()); + } + + template + static Result Perform(Impl* impl, const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return impl->template gmock_PerformImpl(args, get<0>(args), get<1>(args), get<2>(args), get<3>(args), + get<4>(args), get<5>(args), get<6>(args), get<7>(args), get<8>(args), + ExcessiveArg()); + } + + template + static Result Perform(Impl* impl, const ::std::tr1::tuple& args) { + using ::std::tr1::get; + return impl->template gmock_PerformImpl(args, get<0>(args), get<1>(args), get<2>(args), get<3>(args), + get<4>(args), get<5>(args), get<6>(args), get<7>(args), get<8>(args), + get<9>(args)); + } +}; + +} // namespace internal + +// Various overloads for Invoke(). + +// WithArgs(an_action) creates an action that passes +// the selected arguments of the mock function to an_action and +// performs it. It serves as an adaptor between actions with +// different argument lists. C++ doesn't support default arguments for +// function templates, so we have to overload it. +template +inline internal::WithArgsAction +WithArgs(const InnerAction& action) { + return internal::WithArgsAction(action); +} + +template +inline internal::WithArgsAction +WithArgs(const InnerAction& action) { + return internal::WithArgsAction(action); +} + +template +inline internal::WithArgsAction +WithArgs(const InnerAction& action) { + return internal::WithArgsAction(action); +} + +template +inline internal::WithArgsAction +WithArgs(const InnerAction& action) { + return internal::WithArgsAction(action); +} + +template +inline internal::WithArgsAction +WithArgs(const InnerAction& action) { + return internal::WithArgsAction(action); +} + +template +inline internal::WithArgsAction +WithArgs(const InnerAction& action) { + return internal::WithArgsAction(action); +} + +template +inline internal::WithArgsAction +WithArgs(const InnerAction& action) { + return internal::WithArgsAction(action); +} + +template +inline internal::WithArgsAction +WithArgs(const InnerAction& action) { + return internal::WithArgsAction(action); +} + +template +inline internal::WithArgsAction +WithArgs(const InnerAction& action) { + return internal::WithArgsAction(action); +} + +template +inline internal::WithArgsAction +WithArgs(const InnerAction& action) { + return internal::WithArgsAction(action); +} + +// Creates an action that does actions a1, a2, ..., sequentially in +// each invocation. +template +inline internal::DoBothAction +DoAll(Action1 a1, Action2 a2) { + return internal::DoBothAction(a1, a2); +} + +template +inline internal::DoBothAction > +DoAll(Action1 a1, Action2 a2, Action3 a3) { + return DoAll(a1, DoAll(a2, a3)); +} + +template +inline internal::DoBothAction > > +DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4) { + return DoAll(a1, DoAll(a2, a3, a4)); +} + +template +inline internal::DoBothAction > > > +DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5) { + return DoAll(a1, DoAll(a2, a3, a4, a5)); +} + +template +inline internal::DoBothAction > > > > +DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6) { + return DoAll(a1, DoAll(a2, a3, a4, a5, a6)); +} + +template +inline internal::DoBothAction > > > > > +DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6, + Action7 a7) { + return DoAll(a1, DoAll(a2, a3, a4, a5, a6, a7)); +} + +template +inline internal::DoBothAction > > > > > > +DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6, + Action7 a7, Action8 a8) { + return DoAll(a1, DoAll(a2, a3, a4, a5, a6, a7, a8)); +} + +template +inline internal::DoBothAction > > > > > > > +DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6, + Action7 a7, Action8 a8, Action9 a9) { + return DoAll(a1, DoAll(a2, a3, a4, a5, a6, a7, a8, a9)); +} + +template +inline internal::DoBothAction > > > > > > > > +DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6, + Action7 a7, Action8 a8, Action9 a9, Action10 a10) { + return DoAll(a1, DoAll(a2, a3, a4, a5, a6, a7, a8, a9, a10)); +} + +} // namespace testing + +// The ACTION* family of macros can be used in a namespace scope to +// define custom actions easily. The syntax: +// +// ACTION(name) { statements; } +// +// will define an action with the given name that executes the +// statements. The value returned by the statements will be used as +// the return value of the action. Inside the statements, you can +// refer to the K-th (0-based) argument of the mock function by +// 'argK', and refer to its type by 'argK_type'. For example: +// +// ACTION(IncrementArg1) { +// arg1_type temp = arg1; +// return ++(*temp); +// } +// +// allows you to write +// +// ...WillOnce(IncrementArg1()); +// +// You can also refer to the entire argument tuple and its type by +// 'args' and 'args_type', and refer to the mock function type and its +// return type by 'function_type' and 'return_type'. +// +// Note that you don't need to specify the types of the mock function +// arguments. However rest assured that your code is still type-safe: +// you'll get a compiler error if *arg1 doesn't support the ++ +// operator, or if the type of ++(*arg1) isn't compatible with the +// mock function's return type, for example. +// +// Sometimes you'll want to parameterize the action. For that you can use +// another macro: +// +// ACTION_P(name, param_name) { statements; } +// +// For example: +// +// ACTION_P(Add, n) { return arg0 + n; } +// +// will allow you to write: +// +// ...WillOnce(Add(5)); +// +// Note that you don't need to provide the type of the parameter +// either. If you need to reference the type of a parameter named +// 'foo', you can write 'foo_type'. For example, in the body of +// ACTION_P(Add, n) above, you can write 'n_type' to refer to the type +// of 'n'. +// +// We also provide ACTION_P2, ACTION_P3, ..., up to ACTION_P10 to support +// multi-parameter actions. +// +// For the purpose of typing, you can view +// +// ACTION_Pk(Foo, p1, ..., pk) { ... } +// +// as shorthand for +// +// template +// FooActionPk Foo(p1_type p1, ..., pk_type pk) { ... } +// +// In particular, you can provide the template type arguments +// explicitly when invoking Foo(), as in Foo(5, false); +// although usually you can rely on the compiler to infer the types +// for you automatically. You can assign the result of expression +// Foo(p1, ..., pk) to a variable of type FooActionPk. This can be useful when composing actions. +// +// You can also overload actions with different numbers of parameters: +// +// ACTION_P(Plus, a) { ... } +// ACTION_P2(Plus, a, b) { ... } +// +// While it's tempting to always use the ACTION* macros when defining +// a new action, you should also consider implementing ActionInterface +// or using MakePolymorphicAction() instead, especially if you need to +// use the action a lot. While these approaches require more work, +// they give you more control on the types of the mock function +// arguments and the action parameters, which in general leads to +// better compiler error messages that pay off in the long run. They +// also allow overloading actions based on parameter types (as opposed +// to just based on the number of parameters). +// +// CAVEAT: +// +// ACTION*() can only be used in a namespace scope. The reason is +// that C++ doesn't yet allow function-local types to be used to +// instantiate templates. The up-coming C++0x standard will fix this. +// Once that's done, we'll consider supporting using ACTION*() inside +// a function. +// +// MORE INFORMATION: +// +// To learn more about using these macros, please search for 'ACTION' +// on http://code.google.com/p/googlemock/wiki/CookBook. + +// An internal macro needed for implementing ACTION*(). +#define GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_\ + const args_type& args GTEST_ATTRIBUTE_UNUSED_,\ + arg0_type arg0 GTEST_ATTRIBUTE_UNUSED_,\ + arg1_type arg1 GTEST_ATTRIBUTE_UNUSED_,\ + arg2_type arg2 GTEST_ATTRIBUTE_UNUSED_,\ + arg3_type arg3 GTEST_ATTRIBUTE_UNUSED_,\ + arg4_type arg4 GTEST_ATTRIBUTE_UNUSED_,\ + arg5_type arg5 GTEST_ATTRIBUTE_UNUSED_,\ + arg6_type arg6 GTEST_ATTRIBUTE_UNUSED_,\ + arg7_type arg7 GTEST_ATTRIBUTE_UNUSED_,\ + arg8_type arg8 GTEST_ATTRIBUTE_UNUSED_,\ + arg9_type arg9 GTEST_ATTRIBUTE_UNUSED_ + +// Sometimes you want to give an action explicit template parameters +// that cannot be inferred from its value parameters. ACTION() and +// ACTION_P*() don't support that. ACTION_TEMPLATE() remedies that +// and can be viewed as an extension to ACTION() and ACTION_P*(). +// +// The syntax: +// +// ACTION_TEMPLATE(ActionName, +// HAS_m_TEMPLATE_PARAMS(kind1, name1, ..., kind_m, name_m), +// AND_n_VALUE_PARAMS(p1, ..., p_n)) { statements; } +// +// defines an action template that takes m explicit template +// parameters and n value parameters. name_i is the name of the i-th +// template parameter, and kind_i specifies whether it's a typename, +// an integral constant, or a template. p_i is the name of the i-th +// value parameter. +// +// Example: +// +// // DuplicateArg(output) converts the k-th argument of the mock +// // function to type T and copies it to *output. +// ACTION_TEMPLATE(DuplicateArg, +// HAS_2_TEMPLATE_PARAMS(int, k, typename, T), +// AND_1_VALUE_PARAMS(output)) { +// *output = T(std::tr1::get(args)); +// } +// ... +// int n; +// EXPECT_CALL(mock, Foo(_, _)) +// .WillOnce(DuplicateArg<1, unsigned char>(&n)); +// +// To create an instance of an action template, write: +// +// ActionName(v1, ..., v_n) +// +// where the ts are the template arguments and the vs are the value +// arguments. The value argument types are inferred by the compiler. +// If you want to explicitly specify the value argument types, you can +// provide additional template arguments: +// +// ActionName(v1, ..., v_n) +// +// where u_i is the desired type of v_i. +// +// ACTION_TEMPLATE and ACTION/ACTION_P* can be overloaded on the +// number of value parameters, but not on the number of template +// parameters. Without the restriction, the meaning of the following +// is unclear: +// +// OverloadedAction(x); +// +// Are we using a single-template-parameter action where 'bool' refers +// to the type of x, or are we using a two-template-parameter action +// where the compiler is asked to infer the type of x? +// +// Implementation notes: +// +// GMOCK_INTERNAL_*_HAS_m_TEMPLATE_PARAMS and +// GMOCK_INTERNAL_*_AND_n_VALUE_PARAMS are internal macros for +// implementing ACTION_TEMPLATE. The main trick we use is to create +// new macro invocations when expanding a macro. For example, we have +// +// #define ACTION_TEMPLATE(name, template_params, value_params) +// ... GMOCK_INTERNAL_DECL_##template_params ... +// +// which causes ACTION_TEMPLATE(..., HAS_1_TEMPLATE_PARAMS(typename, T), ...) +// to expand to +// +// ... GMOCK_INTERNAL_DECL_HAS_1_TEMPLATE_PARAMS(typename, T) ... +// +// Since GMOCK_INTERNAL_DECL_HAS_1_TEMPLATE_PARAMS is a macro, the +// preprocessor will continue to expand it to +// +// ... typename T ... +// +// This technique conforms to the C++ standard and is portable. It +// allows us to implement action templates using O(N) code, where N is +// the maximum number of template/value parameters supported. Without +// using it, we'd have to devote O(N^2) amount of code to implement all +// combinations of m and n. + +// Declares the template parameters. +#define GMOCK_INTERNAL_DECL_HAS_1_TEMPLATE_PARAMS(kind0, name0) kind0 name0 +#define GMOCK_INTERNAL_DECL_HAS_2_TEMPLATE_PARAMS(kind0, name0, kind1, \ + name1) kind0 name0, kind1 name1 +#define GMOCK_INTERNAL_DECL_HAS_3_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2) kind0 name0, kind1 name1, kind2 name2 +#define GMOCK_INTERNAL_DECL_HAS_4_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3) kind0 name0, kind1 name1, kind2 name2, \ + kind3 name3 +#define GMOCK_INTERNAL_DECL_HAS_5_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4) kind0 name0, kind1 name1, \ + kind2 name2, kind3 name3, kind4 name4 +#define GMOCK_INTERNAL_DECL_HAS_6_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4, kind5, name5) kind0 name0, \ + kind1 name1, kind2 name2, kind3 name3, kind4 name4, kind5 name5 +#define GMOCK_INTERNAL_DECL_HAS_7_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, \ + name6) kind0 name0, kind1 name1, kind2 name2, kind3 name3, kind4 name4, \ + kind5 name5, kind6 name6 +#define GMOCK_INTERNAL_DECL_HAS_8_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, name6, \ + kind7, name7) kind0 name0, kind1 name1, kind2 name2, kind3 name3, \ + kind4 name4, kind5 name5, kind6 name6, kind7 name7 +#define GMOCK_INTERNAL_DECL_HAS_9_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, name6, \ + kind7, name7, kind8, name8) kind0 name0, kind1 name1, kind2 name2, \ + kind3 name3, kind4 name4, kind5 name5, kind6 name6, kind7 name7, \ + kind8 name8 +#define GMOCK_INTERNAL_DECL_HAS_10_TEMPLATE_PARAMS(kind0, name0, kind1, \ + name1, kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, \ + name6, kind7, name7, kind8, name8, kind9, name9) kind0 name0, \ + kind1 name1, kind2 name2, kind3 name3, kind4 name4, kind5 name5, \ + kind6 name6, kind7 name7, kind8 name8, kind9 name9 + +// Lists the template parameters. +#define GMOCK_INTERNAL_LIST_HAS_1_TEMPLATE_PARAMS(kind0, name0) name0 +#define GMOCK_INTERNAL_LIST_HAS_2_TEMPLATE_PARAMS(kind0, name0, kind1, \ + name1) name0, name1 +#define GMOCK_INTERNAL_LIST_HAS_3_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2) name0, name1, name2 +#define GMOCK_INTERNAL_LIST_HAS_4_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3) name0, name1, name2, name3 +#define GMOCK_INTERNAL_LIST_HAS_5_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4) name0, name1, name2, name3, \ + name4 +#define GMOCK_INTERNAL_LIST_HAS_6_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4, kind5, name5) name0, name1, \ + name2, name3, name4, name5 +#define GMOCK_INTERNAL_LIST_HAS_7_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, \ + name6) name0, name1, name2, name3, name4, name5, name6 +#define GMOCK_INTERNAL_LIST_HAS_8_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, name6, \ + kind7, name7) name0, name1, name2, name3, name4, name5, name6, name7 +#define GMOCK_INTERNAL_LIST_HAS_9_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \ + kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, name6, \ + kind7, name7, kind8, name8) name0, name1, name2, name3, name4, name5, \ + name6, name7, name8 +#define GMOCK_INTERNAL_LIST_HAS_10_TEMPLATE_PARAMS(kind0, name0, kind1, \ + name1, kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, \ + name6, kind7, name7, kind8, name8, kind9, name9) name0, name1, name2, \ + name3, name4, name5, name6, name7, name8, name9 + +// Declares the types of value parameters. +#define GMOCK_INTERNAL_DECL_TYPE_AND_0_VALUE_PARAMS() +#define GMOCK_INTERNAL_DECL_TYPE_AND_1_VALUE_PARAMS(p0) , typename p0##_type +#define GMOCK_INTERNAL_DECL_TYPE_AND_2_VALUE_PARAMS(p0, p1) , \ + typename p0##_type, typename p1##_type +#define GMOCK_INTERNAL_DECL_TYPE_AND_3_VALUE_PARAMS(p0, p1, p2) , \ + typename p0##_type, typename p1##_type, typename p2##_type +#define GMOCK_INTERNAL_DECL_TYPE_AND_4_VALUE_PARAMS(p0, p1, p2, p3) , \ + typename p0##_type, typename p1##_type, typename p2##_type, \ + typename p3##_type +#define GMOCK_INTERNAL_DECL_TYPE_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4) , \ + typename p0##_type, typename p1##_type, typename p2##_type, \ + typename p3##_type, typename p4##_type +#define GMOCK_INTERNAL_DECL_TYPE_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5) , \ + typename p0##_type, typename p1##_type, typename p2##_type, \ + typename p3##_type, typename p4##_type, typename p5##_type +#define GMOCK_INTERNAL_DECL_TYPE_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6) , typename p0##_type, typename p1##_type, typename p2##_type, \ + typename p3##_type, typename p4##_type, typename p5##_type, \ + typename p6##_type +#define GMOCK_INTERNAL_DECL_TYPE_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6, p7) , typename p0##_type, typename p1##_type, typename p2##_type, \ + typename p3##_type, typename p4##_type, typename p5##_type, \ + typename p6##_type, typename p7##_type +#define GMOCK_INTERNAL_DECL_TYPE_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6, p7, p8) , typename p0##_type, typename p1##_type, typename p2##_type, \ + typename p3##_type, typename p4##_type, typename p5##_type, \ + typename p6##_type, typename p7##_type, typename p8##_type +#define GMOCK_INTERNAL_DECL_TYPE_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6, p7, p8, p9) , typename p0##_type, typename p1##_type, \ + typename p2##_type, typename p3##_type, typename p4##_type, \ + typename p5##_type, typename p6##_type, typename p7##_type, \ + typename p8##_type, typename p9##_type + +// Initializes the value parameters. +#define GMOCK_INTERNAL_INIT_AND_0_VALUE_PARAMS()\ + () +#define GMOCK_INTERNAL_INIT_AND_1_VALUE_PARAMS(p0)\ + (p0##_type gmock_p0) : p0(gmock_p0) +#define GMOCK_INTERNAL_INIT_AND_2_VALUE_PARAMS(p0, p1)\ + (p0##_type gmock_p0, p1##_type gmock_p1) : p0(gmock_p0), p1(gmock_p1) +#define GMOCK_INTERNAL_INIT_AND_3_VALUE_PARAMS(p0, p1, p2)\ + (p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2) +#define GMOCK_INTERNAL_INIT_AND_4_VALUE_PARAMS(p0, p1, p2, p3)\ + (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3) +#define GMOCK_INTERNAL_INIT_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4)\ + (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4) : p0(gmock_p0), p1(gmock_p1), \ + p2(gmock_p2), p3(gmock_p3), p4(gmock_p4) +#define GMOCK_INTERNAL_INIT_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5)\ + (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, \ + p5##_type gmock_p5) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3), p4(gmock_p4), p5(gmock_p5) +#define GMOCK_INTERNAL_INIT_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6)\ + (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6) +#define GMOCK_INTERNAL_INIT_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7)\ + (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6, p7##_type gmock_p7) : p0(gmock_p0), p1(gmock_p1), \ + p2(gmock_p2), p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), \ + p7(gmock_p7) +#define GMOCK_INTERNAL_INIT_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8)\ + (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6, p7##_type gmock_p7, \ + p8##_type gmock_p8) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), p7(gmock_p7), \ + p8(gmock_p8) +#define GMOCK_INTERNAL_INIT_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8, p9)\ + (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6, p7##_type gmock_p7, p8##_type gmock_p8, \ + p9##_type gmock_p9) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), p7(gmock_p7), \ + p8(gmock_p8), p9(gmock_p9) + +// Declares the fields for storing the value parameters. +#define GMOCK_INTERNAL_DEFN_AND_0_VALUE_PARAMS() +#define GMOCK_INTERNAL_DEFN_AND_1_VALUE_PARAMS(p0) p0##_type p0; +#define GMOCK_INTERNAL_DEFN_AND_2_VALUE_PARAMS(p0, p1) p0##_type p0; \ + p1##_type p1; +#define GMOCK_INTERNAL_DEFN_AND_3_VALUE_PARAMS(p0, p1, p2) p0##_type p0; \ + p1##_type p1; p2##_type p2; +#define GMOCK_INTERNAL_DEFN_AND_4_VALUE_PARAMS(p0, p1, p2, p3) p0##_type p0; \ + p1##_type p1; p2##_type p2; p3##_type p3; +#define GMOCK_INTERNAL_DEFN_AND_5_VALUE_PARAMS(p0, p1, p2, p3, \ + p4) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; p4##_type p4; +#define GMOCK_INTERNAL_DEFN_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, \ + p5) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; p4##_type p4; \ + p5##_type p5; +#define GMOCK_INTERNAL_DEFN_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; p4##_type p4; \ + p5##_type p5; p6##_type p6; +#define GMOCK_INTERNAL_DEFN_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; p4##_type p4; \ + p5##_type p5; p6##_type p6; p7##_type p7; +#define GMOCK_INTERNAL_DEFN_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; \ + p4##_type p4; p5##_type p5; p6##_type p6; p7##_type p7; p8##_type p8; +#define GMOCK_INTERNAL_DEFN_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8, p9) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; \ + p4##_type p4; p5##_type p5; p6##_type p6; p7##_type p7; p8##_type p8; \ + p9##_type p9; + +// Lists the value parameters. +#define GMOCK_INTERNAL_LIST_AND_0_VALUE_PARAMS() +#define GMOCK_INTERNAL_LIST_AND_1_VALUE_PARAMS(p0) p0 +#define GMOCK_INTERNAL_LIST_AND_2_VALUE_PARAMS(p0, p1) p0, p1 +#define GMOCK_INTERNAL_LIST_AND_3_VALUE_PARAMS(p0, p1, p2) p0, p1, p2 +#define GMOCK_INTERNAL_LIST_AND_4_VALUE_PARAMS(p0, p1, p2, p3) p0, p1, p2, p3 +#define GMOCK_INTERNAL_LIST_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4) p0, p1, \ + p2, p3, p4 +#define GMOCK_INTERNAL_LIST_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5) p0, \ + p1, p2, p3, p4, p5 +#define GMOCK_INTERNAL_LIST_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6) p0, p1, p2, p3, p4, p5, p6 +#define GMOCK_INTERNAL_LIST_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7) p0, p1, p2, p3, p4, p5, p6, p7 +#define GMOCK_INTERNAL_LIST_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8) p0, p1, p2, p3, p4, p5, p6, p7, p8 +#define GMOCK_INTERNAL_LIST_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8, p9) p0, p1, p2, p3, p4, p5, p6, p7, p8, p9 + +// Lists the value parameter types. +#define GMOCK_INTERNAL_LIST_TYPE_AND_0_VALUE_PARAMS() +#define GMOCK_INTERNAL_LIST_TYPE_AND_1_VALUE_PARAMS(p0) , p0##_type +#define GMOCK_INTERNAL_LIST_TYPE_AND_2_VALUE_PARAMS(p0, p1) , p0##_type, \ + p1##_type +#define GMOCK_INTERNAL_LIST_TYPE_AND_3_VALUE_PARAMS(p0, p1, p2) , p0##_type, \ + p1##_type, p2##_type +#define GMOCK_INTERNAL_LIST_TYPE_AND_4_VALUE_PARAMS(p0, p1, p2, p3) , \ + p0##_type, p1##_type, p2##_type, p3##_type +#define GMOCK_INTERNAL_LIST_TYPE_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4) , \ + p0##_type, p1##_type, p2##_type, p3##_type, p4##_type +#define GMOCK_INTERNAL_LIST_TYPE_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5) , \ + p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, p5##_type +#define GMOCK_INTERNAL_LIST_TYPE_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6) , p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, p5##_type, \ + p6##_type +#define GMOCK_INTERNAL_LIST_TYPE_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6, p7) , p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \ + p5##_type, p6##_type, p7##_type +#define GMOCK_INTERNAL_LIST_TYPE_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6, p7, p8) , p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \ + p5##_type, p6##_type, p7##_type, p8##_type +#define GMOCK_INTERNAL_LIST_TYPE_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6, p7, p8, p9) , p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \ + p5##_type, p6##_type, p7##_type, p8##_type, p9##_type + +// Declares the value parameters. +#define GMOCK_INTERNAL_DECL_AND_0_VALUE_PARAMS() +#define GMOCK_INTERNAL_DECL_AND_1_VALUE_PARAMS(p0) p0##_type p0 +#define GMOCK_INTERNAL_DECL_AND_2_VALUE_PARAMS(p0, p1) p0##_type p0, \ + p1##_type p1 +#define GMOCK_INTERNAL_DECL_AND_3_VALUE_PARAMS(p0, p1, p2) p0##_type p0, \ + p1##_type p1, p2##_type p2 +#define GMOCK_INTERNAL_DECL_AND_4_VALUE_PARAMS(p0, p1, p2, p3) p0##_type p0, \ + p1##_type p1, p2##_type p2, p3##_type p3 +#define GMOCK_INTERNAL_DECL_AND_5_VALUE_PARAMS(p0, p1, p2, p3, \ + p4) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4 +#define GMOCK_INTERNAL_DECL_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, \ + p5) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, \ + p5##_type p5 +#define GMOCK_INTERNAL_DECL_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \ + p6) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, \ + p5##_type p5, p6##_type p6 +#define GMOCK_INTERNAL_DECL_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, \ + p5##_type p5, p6##_type p6, p7##_type p7 +#define GMOCK_INTERNAL_DECL_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \ + p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, p8##_type p8 +#define GMOCK_INTERNAL_DECL_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8, p9) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \ + p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, p8##_type p8, \ + p9##_type p9 + +// The suffix of the class template implementing the action template. +#define GMOCK_INTERNAL_COUNT_AND_0_VALUE_PARAMS() +#define GMOCK_INTERNAL_COUNT_AND_1_VALUE_PARAMS(p0) P +#define GMOCK_INTERNAL_COUNT_AND_2_VALUE_PARAMS(p0, p1) P2 +#define GMOCK_INTERNAL_COUNT_AND_3_VALUE_PARAMS(p0, p1, p2) P3 +#define GMOCK_INTERNAL_COUNT_AND_4_VALUE_PARAMS(p0, p1, p2, p3) P4 +#define GMOCK_INTERNAL_COUNT_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4) P5 +#define GMOCK_INTERNAL_COUNT_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5) P6 +#define GMOCK_INTERNAL_COUNT_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6) P7 +#define GMOCK_INTERNAL_COUNT_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7) P8 +#define GMOCK_INTERNAL_COUNT_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8) P9 +#define GMOCK_INTERNAL_COUNT_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \ + p7, p8, p9) P10 + +// The name of the class template implementing the action template. +#define GMOCK_ACTION_CLASS_(name, value_params)\ + GTEST_CONCAT_TOKEN_(name##Action, GMOCK_INTERNAL_COUNT_##value_params) + +#define ACTION_TEMPLATE(name, template_params, value_params)\ + template \ + class GMOCK_ACTION_CLASS_(name, value_params) {\ + public:\ + GMOCK_ACTION_CLASS_(name, value_params)\ + GMOCK_INTERNAL_INIT_##value_params {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + explicit gmock_Impl GMOCK_INTERNAL_INIT_##value_params {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ + arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ + arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ + arg9_type arg9) const;\ + GMOCK_INTERNAL_DEFN_##value_params\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(\ + new gmock_Impl(GMOCK_INTERNAL_LIST_##value_params));\ + }\ + GMOCK_INTERNAL_DEFN_##value_params\ + private:\ + GTEST_DISALLOW_ASSIGN_(GMOCK_ACTION_CLASS_(name, value_params));\ + };\ + template \ + inline GMOCK_ACTION_CLASS_(name, value_params)<\ + GMOCK_INTERNAL_LIST_##template_params\ + GMOCK_INTERNAL_LIST_TYPE_##value_params> name(\ + GMOCK_INTERNAL_DECL_##value_params) {\ + return GMOCK_ACTION_CLASS_(name, value_params)<\ + GMOCK_INTERNAL_LIST_##template_params\ + GMOCK_INTERNAL_LIST_TYPE_##value_params>(\ + GMOCK_INTERNAL_LIST_##value_params);\ + }\ + template \ + template \ + template \ + typename ::testing::internal::Function::Result\ + GMOCK_ACTION_CLASS_(name, value_params)<\ + GMOCK_INTERNAL_LIST_##template_params\ + GMOCK_INTERNAL_LIST_TYPE_##value_params>::gmock_Impl::\ + gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +#define ACTION(name)\ + class name##Action {\ + public:\ + name##Action() {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + gmock_Impl() {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ + arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ + arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ + arg9_type arg9) const;\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(new gmock_Impl());\ + }\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##Action);\ + };\ + inline name##Action name() {\ + return name##Action();\ + }\ + template \ + template \ + typename ::testing::internal::Function::Result\ + name##Action::gmock_Impl::gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +#define ACTION_P(name, p0)\ + template \ + class name##ActionP {\ + public:\ + name##ActionP(p0##_type gmock_p0) : p0(gmock_p0) {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + explicit gmock_Impl(p0##_type gmock_p0) : p0(gmock_p0) {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ + arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ + arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ + arg9_type arg9) const;\ + p0##_type p0;\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(new gmock_Impl(p0));\ + }\ + p0##_type p0;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##ActionP);\ + };\ + template \ + inline name##ActionP name(p0##_type p0) {\ + return name##ActionP(p0);\ + }\ + template \ + template \ + template \ + typename ::testing::internal::Function::Result\ + name##ActionP::gmock_Impl::gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +#define ACTION_P2(name, p0, p1)\ + template \ + class name##ActionP2 {\ + public:\ + name##ActionP2(p0##_type gmock_p0, p1##_type gmock_p1) : p0(gmock_p0), \ + p1(gmock_p1) {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1) : p0(gmock_p0), \ + p1(gmock_p1) {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ + arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ + arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ + arg9_type arg9) const;\ + p0##_type p0;\ + p1##_type p1;\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(new gmock_Impl(p0, p1));\ + }\ + p0##_type p0;\ + p1##_type p1;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##ActionP2);\ + };\ + template \ + inline name##ActionP2 name(p0##_type p0, \ + p1##_type p1) {\ + return name##ActionP2(p0, p1);\ + }\ + template \ + template \ + template \ + typename ::testing::internal::Function::Result\ + name##ActionP2::gmock_Impl::gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +#define ACTION_P3(name, p0, p1, p2)\ + template \ + class name##ActionP3 {\ + public:\ + name##ActionP3(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2) {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2) {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ + arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ + arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ + arg9_type arg9) const;\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(new gmock_Impl(p0, p1, p2));\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##ActionP3);\ + };\ + template \ + inline name##ActionP3 name(p0##_type p0, \ + p1##_type p1, p2##_type p2) {\ + return name##ActionP3(p0, p1, p2);\ + }\ + template \ + template \ + template \ + typename ::testing::internal::Function::Result\ + name##ActionP3::gmock_Impl::gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +#define ACTION_P4(name, p0, p1, p2, p3)\ + template \ + class name##ActionP4 {\ + public:\ + name##ActionP4(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3) : p0(gmock_p0), p1(gmock_p1), \ + p2(gmock_p2), p3(gmock_p3) {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3) {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ + arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ + arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ + arg9_type arg9) const;\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(new gmock_Impl(p0, p1, p2, p3));\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##ActionP4);\ + };\ + template \ + inline name##ActionP4 name(p0##_type p0, p1##_type p1, p2##_type p2, \ + p3##_type p3) {\ + return name##ActionP4(p0, p1, \ + p2, p3);\ + }\ + template \ + template \ + template \ + typename ::testing::internal::Function::Result\ + name##ActionP4::gmock_Impl::gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +#define ACTION_P5(name, p0, p1, p2, p3, p4)\ + template \ + class name##ActionP5 {\ + public:\ + name##ActionP5(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3, \ + p4##_type gmock_p4) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3), p4(gmock_p4) {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4) : p0(gmock_p0), \ + p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), p4(gmock_p4) {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ + arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ + arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ + arg9_type arg9) const;\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(new gmock_Impl(p0, p1, p2, p3, p4));\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##ActionP5);\ + };\ + template \ + inline name##ActionP5 name(p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \ + p4##_type p4) {\ + return name##ActionP5(p0, p1, p2, p3, p4);\ + }\ + template \ + template \ + template \ + typename ::testing::internal::Function::Result\ + name##ActionP5::gmock_Impl::gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +#define ACTION_P6(name, p0, p1, p2, p3, p4, p5)\ + template \ + class name##ActionP6 {\ + public:\ + name##ActionP6(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \ + p5##_type gmock_p5) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3), p4(gmock_p4), p5(gmock_p5) {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, \ + p5##_type gmock_p5) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3), p4(gmock_p4), p5(gmock_p5) {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ + arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ + arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ + arg9_type arg9) const;\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(new gmock_Impl(p0, p1, p2, p3, p4, p5));\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##ActionP6);\ + };\ + template \ + inline name##ActionP6 name(p0##_type p0, p1##_type p1, p2##_type p2, \ + p3##_type p3, p4##_type p4, p5##_type p5) {\ + return name##ActionP6(p0, p1, p2, p3, p4, p5);\ + }\ + template \ + template \ + template \ + typename ::testing::internal::Function::Result\ + name##ActionP6::gmock_Impl::gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +#define ACTION_P7(name, p0, p1, p2, p3, p4, p5, p6)\ + template \ + class name##ActionP7 {\ + public:\ + name##ActionP7(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \ + p5##_type gmock_p5, p6##_type gmock_p6) : p0(gmock_p0), p1(gmock_p1), \ + p2(gmock_p2), p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), \ + p6(gmock_p6) {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6) {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ + arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ + arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ + arg9_type arg9) const;\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(new gmock_Impl(p0, p1, p2, p3, p4, p5, \ + p6));\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##ActionP7);\ + };\ + template \ + inline name##ActionP7 name(p0##_type p0, p1##_type p1, \ + p2##_type p2, p3##_type p3, p4##_type p4, p5##_type p5, \ + p6##_type p6) {\ + return name##ActionP7(p0, p1, p2, p3, p4, p5, p6);\ + }\ + template \ + template \ + template \ + typename ::testing::internal::Function::Result\ + name##ActionP7::gmock_Impl::gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +#define ACTION_P8(name, p0, p1, p2, p3, p4, p5, p6, p7)\ + template \ + class name##ActionP8 {\ + public:\ + name##ActionP8(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \ + p5##_type gmock_p5, p6##_type gmock_p6, \ + p7##_type gmock_p7) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), \ + p7(gmock_p7) {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6, p7##_type gmock_p7) : p0(gmock_p0), \ + p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), p4(gmock_p4), \ + p5(gmock_p5), p6(gmock_p6), p7(gmock_p7) {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ + arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ + arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ + arg9_type arg9) const;\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + p7##_type p7;\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(new gmock_Impl(p0, p1, p2, p3, p4, p5, \ + p6, p7));\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + p7##_type p7;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##ActionP8);\ + };\ + template \ + inline name##ActionP8 name(p0##_type p0, \ + p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, p5##_type p5, \ + p6##_type p6, p7##_type p7) {\ + return name##ActionP8(p0, p1, p2, p3, p4, p5, \ + p6, p7);\ + }\ + template \ + template \ + template \ + typename ::testing::internal::Function::Result\ + name##ActionP8::gmock_Impl::gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +#define ACTION_P9(name, p0, p1, p2, p3, p4, p5, p6, p7, p8)\ + template \ + class name##ActionP9 {\ + public:\ + name##ActionP9(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \ + p5##_type gmock_p5, p6##_type gmock_p6, p7##_type gmock_p7, \ + p8##_type gmock_p8) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), p7(gmock_p7), \ + p8(gmock_p8) {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6, p7##_type gmock_p7, \ + p8##_type gmock_p8) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), \ + p7(gmock_p7), p8(gmock_p8) {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ + arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ + arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ + arg9_type arg9) const;\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + p7##_type p7;\ + p8##_type p8;\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(new gmock_Impl(p0, p1, p2, p3, p4, p5, \ + p6, p7, p8));\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + p7##_type p7;\ + p8##_type p8;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##ActionP9);\ + };\ + template \ + inline name##ActionP9 name(p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \ + p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, \ + p8##_type p8) {\ + return name##ActionP9(p0, p1, p2, \ + p3, p4, p5, p6, p7, p8);\ + }\ + template \ + template \ + template \ + typename ::testing::internal::Function::Result\ + name##ActionP9::gmock_Impl::gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +#define ACTION_P10(name, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9)\ + template \ + class name##ActionP10 {\ + public:\ + name##ActionP10(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \ + p5##_type gmock_p5, p6##_type gmock_p6, p7##_type gmock_p7, \ + p8##_type gmock_p8, p9##_type gmock_p9) : p0(gmock_p0), p1(gmock_p1), \ + p2(gmock_p2), p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), \ + p7(gmock_p7), p8(gmock_p8), p9(gmock_p9) {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6, p7##_type gmock_p7, p8##_type gmock_p8, \ + p9##_type gmock_p9) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), \ + p7(gmock_p7), p8(gmock_p8), p9(gmock_p9) {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template \ + return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \ + arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \ + arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \ + arg9_type arg9) const;\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + p7##_type p7;\ + p8##_type p8;\ + p9##_type p9;\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(new gmock_Impl(p0, p1, p2, p3, p4, p5, \ + p6, p7, p8, p9));\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + p7##_type p7;\ + p8##_type p8;\ + p9##_type p9;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##ActionP10);\ + };\ + template \ + inline name##ActionP10 name(p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \ + p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, p8##_type p8, \ + p9##_type p9) {\ + return name##ActionP10(p0, \ + p1, p2, p3, p4, p5, p6, p7, p8, p9);\ + }\ + template \ + template \ + template \ + typename ::testing::internal::Function::Result\ + name##ActionP10::gmock_Impl::gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +// TODO(wan@google.com): move the following to a different .h file +// such that we don't have to run 'pump' every time the code is +// updated. +namespace testing { + +// The ACTION*() macros trigger warning C4100 (unreferenced formal +// parameter) in MSVC with -W4. Unfortunately they cannot be fixed in +// the macro definition, as the warnings are generated when the macro +// is expanded and macro expansion cannot contain #pragma. Therefore +// we suppress them here. +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable:4100) +#endif + +// Various overloads for InvokeArgument(). +// +// The InvokeArgument(a1, a2, ..., a_k) action invokes the N-th +// (0-based) argument, which must be a k-ary callable, of the mock +// function, with arguments a1, a2, ..., a_k. +// +// Notes: +// +// 1. The arguments are passed by value by default. If you need to +// pass an argument by reference, wrap it inside ByRef(). For +// example, +// +// InvokeArgument<1>(5, string("Hello"), ByRef(foo)) +// +// passes 5 and string("Hello") by value, and passes foo by +// reference. +// +// 2. If the callable takes an argument by reference but ByRef() is +// not used, it will receive the reference to a copy of the value, +// instead of the original value. For example, when the 0-th +// argument of the mock function takes a const string&, the action +// +// InvokeArgument<0>(string("Hello")) +// +// makes a copy of the temporary string("Hello") object and passes a +// reference of the copy, instead of the original temporary object, +// to the callable. This makes it easy for a user to define an +// InvokeArgument action from temporary values and have it performed +// later. + +ACTION_TEMPLATE(InvokeArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_0_VALUE_PARAMS()) { + return internal::CallableHelper::Call( + ::std::tr1::get(args)); +} + +ACTION_TEMPLATE(InvokeArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_1_VALUE_PARAMS(p0)) { + return internal::CallableHelper::Call( + ::std::tr1::get(args), p0); +} + +ACTION_TEMPLATE(InvokeArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_2_VALUE_PARAMS(p0, p1)) { + return internal::CallableHelper::Call( + ::std::tr1::get(args), p0, p1); +} + +ACTION_TEMPLATE(InvokeArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_3_VALUE_PARAMS(p0, p1, p2)) { + return internal::CallableHelper::Call( + ::std::tr1::get(args), p0, p1, p2); +} + +ACTION_TEMPLATE(InvokeArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_4_VALUE_PARAMS(p0, p1, p2, p3)) { + return internal::CallableHelper::Call( + ::std::tr1::get(args), p0, p1, p2, p3); +} + +ACTION_TEMPLATE(InvokeArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4)) { + return internal::CallableHelper::Call( + ::std::tr1::get(args), p0, p1, p2, p3, p4); +} + +ACTION_TEMPLATE(InvokeArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5)) { + return internal::CallableHelper::Call( + ::std::tr1::get(args), p0, p1, p2, p3, p4, p5); +} + +ACTION_TEMPLATE(InvokeArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6)) { + return internal::CallableHelper::Call( + ::std::tr1::get(args), p0, p1, p2, p3, p4, p5, p6); +} + +ACTION_TEMPLATE(InvokeArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7)) { + return internal::CallableHelper::Call( + ::std::tr1::get(args), p0, p1, p2, p3, p4, p5, p6, p7); +} + +ACTION_TEMPLATE(InvokeArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7, p8)) { + return internal::CallableHelper::Call( + ::std::tr1::get(args), p0, p1, p2, p3, p4, p5, p6, p7, p8); +} + +ACTION_TEMPLATE(InvokeArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9)) { + return internal::CallableHelper::Call( + ::std::tr1::get(args), p0, p1, p2, p3, p4, p5, p6, p7, p8, p9); +} + +// Various overloads for ReturnNew(). +// +// The ReturnNew(a1, a2, ..., a_k) action returns a pointer to a new +// instance of type T, constructed on the heap with constructor arguments +// a1, a2, ..., and a_k. The caller assumes ownership of the returned value. +ACTION_TEMPLATE(ReturnNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_0_VALUE_PARAMS()) { + return new T(); +} + +ACTION_TEMPLATE(ReturnNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_1_VALUE_PARAMS(p0)) { + return new T(p0); +} + +ACTION_TEMPLATE(ReturnNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_2_VALUE_PARAMS(p0, p1)) { + return new T(p0, p1); +} + +ACTION_TEMPLATE(ReturnNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_3_VALUE_PARAMS(p0, p1, p2)) { + return new T(p0, p1, p2); +} + +ACTION_TEMPLATE(ReturnNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_4_VALUE_PARAMS(p0, p1, p2, p3)) { + return new T(p0, p1, p2, p3); +} + +ACTION_TEMPLATE(ReturnNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4)) { + return new T(p0, p1, p2, p3, p4); +} + +ACTION_TEMPLATE(ReturnNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5)) { + return new T(p0, p1, p2, p3, p4, p5); +} + +ACTION_TEMPLATE(ReturnNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6)) { + return new T(p0, p1, p2, p3, p4, p5, p6); +} + +ACTION_TEMPLATE(ReturnNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7)) { + return new T(p0, p1, p2, p3, p4, p5, p6, p7); +} + +ACTION_TEMPLATE(ReturnNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7, p8)) { + return new T(p0, p1, p2, p3, p4, p5, p6, p7, p8); +} + +ACTION_TEMPLATE(ReturnNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9)) { + return new T(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9); +} + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + +} // namespace testing + +#endif // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_ACTIONS_H_ diff --git a/libwvdrmengine/test/gmock/include/gmock/gmock-generated-actions.h.pump b/libwvdrmengine/test/gmock/include/gmock/gmock-generated-actions.h.pump new file mode 100644 index 00000000..001fd7d0 --- /dev/null +++ b/libwvdrmengine/test/gmock/include/gmock/gmock-generated-actions.h.pump @@ -0,0 +1,825 @@ +$$ -*- mode: c++; -*- +$$ This is a Pump source file. Please use Pump to convert it to +$$ gmock-generated-actions.h. +$$ +$var n = 10 $$ The maximum arity we support. +$$}} This meta comment fixes auto-indentation in editors. +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file implements some commonly used variadic actions. + +#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_ACTIONS_H_ +#define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_ACTIONS_H_ + +#include "gmock/gmock-actions.h" +#include "gmock/internal/gmock-port.h" + +namespace testing { +namespace internal { + +// InvokeHelper knows how to unpack an N-tuple and invoke an N-ary +// function or method with the unpacked values, where F is a function +// type that takes N arguments. +template +class InvokeHelper; + + +$range i 0..n +$for i [[ +$range j 1..i +$var types = [[$for j [[, typename A$j]]]] +$var as = [[$for j, [[A$j]]]] +$var args = [[$if i==0 [[]] $else [[ args]]]] +$var import = [[$if i==0 [[]] $else [[ + using ::std::tr1::get; + +]]]] +$var gets = [[$for j, [[get<$(j - 1)>(args)]]]] +template +class InvokeHelper > { + public: + template + static R Invoke(Function function, const ::std::tr1::tuple<$as>&$args) { +$import return function($gets); + } + + template + static R InvokeMethod(Class* obj_ptr, + MethodPtr method_ptr, + const ::std::tr1::tuple<$as>&$args) { +$import return (obj_ptr->*method_ptr)($gets); + } +}; + + +]] +// CallableHelper has static methods for invoking "callables", +// i.e. function pointers and functors. It uses overloading to +// provide a uniform interface for invoking different kinds of +// callables. In particular, you can use: +// +// CallableHelper::Call(callable, a1, a2, ..., an) +// +// to invoke an n-ary callable, where R is its return type. If an +// argument, say a2, needs to be passed by reference, you should write +// ByRef(a2) instead of a2 in the above expression. +template +class CallableHelper { + public: + // Calls a nullary callable. + template + static R Call(Function function) { return function(); } + + // Calls a unary callable. + + // We deliberately pass a1 by value instead of const reference here + // in case it is a C-string literal. If we had declared the + // parameter as 'const A1& a1' and write Call(function, "Hi"), the + // compiler would've thought A1 is 'char[3]', which causes trouble + // when you need to copy a value of type A1. By declaring the + // parameter as 'A1 a1', the compiler will correctly infer that A1 + // is 'const char*' when it sees Call(function, "Hi"). + // + // Since this function is defined inline, the compiler can get rid + // of the copying of the arguments. Therefore the performance won't + // be hurt. + template + static R Call(Function function, A1 a1) { return function(a1); } + +$range i 2..n +$for i +[[ +$var arity = [[$if i==2 [[binary]] $elif i==3 [[ternary]] $else [[$i-ary]]]] + + // Calls a $arity callable. + +$range j 1..i +$var typename_As = [[$for j, [[typename A$j]]]] +$var Aas = [[$for j, [[A$j a$j]]]] +$var as = [[$for j, [[a$j]]]] +$var typename_Ts = [[$for j, [[typename T$j]]]] +$var Ts = [[$for j, [[T$j]]]] + template + static R Call(Function function, $Aas) { + return function($as); + } + +]] + +}; // class CallableHelper + +// An INTERNAL macro for extracting the type of a tuple field. It's +// subject to change without notice - DO NOT USE IN USER CODE! +#define GMOCK_FIELD_(Tuple, N) \ + typename ::std::tr1::tuple_element::type + +$range i 1..n + +// SelectArgs::type is the +// type of an n-ary function whose i-th (1-based) argument type is the +// k{i}-th (0-based) field of ArgumentTuple, which must be a tuple +// type, and whose return type is Result. For example, +// SelectArgs, 0, 3>::type +// is int(bool, long). +// +// SelectArgs::Select(args) +// returns the selected fields (k1, k2, ..., k_n) of args as a tuple. +// For example, +// SelectArgs, 2, 0>::Select( +// ::std::tr1::make_tuple(true, 'a', 2.5)) +// returns ::std::tr1::tuple (2.5, true). +// +// The numbers in list k1, k2, ..., k_n must be >= 0, where n can be +// in the range [0, $n]. Duplicates are allowed and they don't have +// to be in an ascending or descending order. + +template +class SelectArgs { + public: + typedef Result type($for i, [[GMOCK_FIELD_(ArgumentTuple, k$i)]]); + typedef typename Function::ArgumentTuple SelectedArgs; + static SelectedArgs Select(const ArgumentTuple& args) { + using ::std::tr1::get; + return SelectedArgs($for i, [[get(args)]]); + } +}; + + +$for i [[ +$range j 1..n +$range j1 1..i-1 +template +class SelectArgs { + public: + typedef Result type($for j1, [[GMOCK_FIELD_(ArgumentTuple, k$j1)]]); + typedef typename Function::ArgumentTuple SelectedArgs; + static SelectedArgs Select(const ArgumentTuple& [[]] +$if i == 1 [[/* args */]] $else [[args]]) { + using ::std::tr1::get; + return SelectedArgs($for j1, [[get(args)]]); + } +}; + + +]] +#undef GMOCK_FIELD_ + +$var ks = [[$for i, [[k$i]]]] + +// Implements the WithArgs action. +template +class WithArgsAction { + public: + explicit WithArgsAction(const InnerAction& action) : action_(action) {} + + template + operator Action() const { return MakeAction(new Impl(action_)); } + + private: + template + class Impl : public ActionInterface { + public: + typedef typename Function::Result Result; + typedef typename Function::ArgumentTuple ArgumentTuple; + + explicit Impl(const InnerAction& action) : action_(action) {} + + virtual Result Perform(const ArgumentTuple& args) { + return action_.Perform(SelectArgs::Select(args)); + } + + private: + typedef typename SelectArgs::type InnerFunctionType; + + Action action_; + }; + + const InnerAction action_; + + GTEST_DISALLOW_ASSIGN_(WithArgsAction); +}; + +// A macro from the ACTION* family (defined later in this file) +// defines an action that can be used in a mock function. Typically, +// these actions only care about a subset of the arguments of the mock +// function. For example, if such an action only uses the second +// argument, it can be used in any mock function that takes >= 2 +// arguments where the type of the second argument is compatible. +// +// Therefore, the action implementation must be prepared to take more +// arguments than it needs. The ExcessiveArg type is used to +// represent those excessive arguments. In order to keep the compiler +// error messages tractable, we define it in the testing namespace +// instead of testing::internal. However, this is an INTERNAL TYPE +// and subject to change without notice, so a user MUST NOT USE THIS +// TYPE DIRECTLY. +struct ExcessiveArg {}; + +// A helper class needed for implementing the ACTION* macros. +template +class ActionHelper { + public: +$range i 0..n +$for i + +[[ +$var template = [[$if i==0 [[]] $else [[ +$range j 0..i-1 + template <$for j, [[typename A$j]]> +]]]] +$range j 0..i-1 +$var As = [[$for j, [[A$j]]]] +$var as = [[$for j, [[get<$j>(args)]]]] +$range k 1..n-i +$var eas = [[$for k, [[ExcessiveArg()]]]] +$var arg_list = [[$if (i==0) | (i==n) [[$as$eas]] $else [[$as, $eas]]]] +$template + static Result Perform(Impl* impl, const ::std::tr1::tuple<$As>& args) { + using ::std::tr1::get; + return impl->template gmock_PerformImpl<$As>(args, $arg_list); + } + +]] +}; + +} // namespace internal + +// Various overloads for Invoke(). + +// WithArgs(an_action) creates an action that passes +// the selected arguments of the mock function to an_action and +// performs it. It serves as an adaptor between actions with +// different argument lists. C++ doesn't support default arguments for +// function templates, so we have to overload it. + +$range i 1..n +$for i [[ +$range j 1..i +template <$for j [[int k$j, ]]typename InnerAction> +inline internal::WithArgsAction +WithArgs(const InnerAction& action) { + return internal::WithArgsAction(action); +} + + +]] +// Creates an action that does actions a1, a2, ..., sequentially in +// each invocation. +$range i 2..n +$for i [[ +$range j 2..i +$var types = [[$for j, [[typename Action$j]]]] +$var Aas = [[$for j [[, Action$j a$j]]]] + +template +$range k 1..i-1 + +inline $for k [[internal::DoBothAction]] + +DoAll(Action1 a1$Aas) { +$if i==2 [[ + + return internal::DoBothAction(a1, a2); +]] $else [[ +$range j2 2..i + + return DoAll(a1, DoAll($for j2, [[a$j2]])); +]] + +} + +]] + +} // namespace testing + +// The ACTION* family of macros can be used in a namespace scope to +// define custom actions easily. The syntax: +// +// ACTION(name) { statements; } +// +// will define an action with the given name that executes the +// statements. The value returned by the statements will be used as +// the return value of the action. Inside the statements, you can +// refer to the K-th (0-based) argument of the mock function by +// 'argK', and refer to its type by 'argK_type'. For example: +// +// ACTION(IncrementArg1) { +// arg1_type temp = arg1; +// return ++(*temp); +// } +// +// allows you to write +// +// ...WillOnce(IncrementArg1()); +// +// You can also refer to the entire argument tuple and its type by +// 'args' and 'args_type', and refer to the mock function type and its +// return type by 'function_type' and 'return_type'. +// +// Note that you don't need to specify the types of the mock function +// arguments. However rest assured that your code is still type-safe: +// you'll get a compiler error if *arg1 doesn't support the ++ +// operator, or if the type of ++(*arg1) isn't compatible with the +// mock function's return type, for example. +// +// Sometimes you'll want to parameterize the action. For that you can use +// another macro: +// +// ACTION_P(name, param_name) { statements; } +// +// For example: +// +// ACTION_P(Add, n) { return arg0 + n; } +// +// will allow you to write: +// +// ...WillOnce(Add(5)); +// +// Note that you don't need to provide the type of the parameter +// either. If you need to reference the type of a parameter named +// 'foo', you can write 'foo_type'. For example, in the body of +// ACTION_P(Add, n) above, you can write 'n_type' to refer to the type +// of 'n'. +// +// We also provide ACTION_P2, ACTION_P3, ..., up to ACTION_P$n to support +// multi-parameter actions. +// +// For the purpose of typing, you can view +// +// ACTION_Pk(Foo, p1, ..., pk) { ... } +// +// as shorthand for +// +// template +// FooActionPk Foo(p1_type p1, ..., pk_type pk) { ... } +// +// In particular, you can provide the template type arguments +// explicitly when invoking Foo(), as in Foo(5, false); +// although usually you can rely on the compiler to infer the types +// for you automatically. You can assign the result of expression +// Foo(p1, ..., pk) to a variable of type FooActionPk. This can be useful when composing actions. +// +// You can also overload actions with different numbers of parameters: +// +// ACTION_P(Plus, a) { ... } +// ACTION_P2(Plus, a, b) { ... } +// +// While it's tempting to always use the ACTION* macros when defining +// a new action, you should also consider implementing ActionInterface +// or using MakePolymorphicAction() instead, especially if you need to +// use the action a lot. While these approaches require more work, +// they give you more control on the types of the mock function +// arguments and the action parameters, which in general leads to +// better compiler error messages that pay off in the long run. They +// also allow overloading actions based on parameter types (as opposed +// to just based on the number of parameters). +// +// CAVEAT: +// +// ACTION*() can only be used in a namespace scope. The reason is +// that C++ doesn't yet allow function-local types to be used to +// instantiate templates. The up-coming C++0x standard will fix this. +// Once that's done, we'll consider supporting using ACTION*() inside +// a function. +// +// MORE INFORMATION: +// +// To learn more about using these macros, please search for 'ACTION' +// on http://code.google.com/p/googlemock/wiki/CookBook. + +$range i 0..n +$range k 0..n-1 + +// An internal macro needed for implementing ACTION*(). +#define GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_\ + const args_type& args GTEST_ATTRIBUTE_UNUSED_ +$for k [[,\ + arg$k[[]]_type arg$k GTEST_ATTRIBUTE_UNUSED_]] + + +// Sometimes you want to give an action explicit template parameters +// that cannot be inferred from its value parameters. ACTION() and +// ACTION_P*() don't support that. ACTION_TEMPLATE() remedies that +// and can be viewed as an extension to ACTION() and ACTION_P*(). +// +// The syntax: +// +// ACTION_TEMPLATE(ActionName, +// HAS_m_TEMPLATE_PARAMS(kind1, name1, ..., kind_m, name_m), +// AND_n_VALUE_PARAMS(p1, ..., p_n)) { statements; } +// +// defines an action template that takes m explicit template +// parameters and n value parameters. name_i is the name of the i-th +// template parameter, and kind_i specifies whether it's a typename, +// an integral constant, or a template. p_i is the name of the i-th +// value parameter. +// +// Example: +// +// // DuplicateArg(output) converts the k-th argument of the mock +// // function to type T and copies it to *output. +// ACTION_TEMPLATE(DuplicateArg, +// HAS_2_TEMPLATE_PARAMS(int, k, typename, T), +// AND_1_VALUE_PARAMS(output)) { +// *output = T(std::tr1::get(args)); +// } +// ... +// int n; +// EXPECT_CALL(mock, Foo(_, _)) +// .WillOnce(DuplicateArg<1, unsigned char>(&n)); +// +// To create an instance of an action template, write: +// +// ActionName(v1, ..., v_n) +// +// where the ts are the template arguments and the vs are the value +// arguments. The value argument types are inferred by the compiler. +// If you want to explicitly specify the value argument types, you can +// provide additional template arguments: +// +// ActionName(v1, ..., v_n) +// +// where u_i is the desired type of v_i. +// +// ACTION_TEMPLATE and ACTION/ACTION_P* can be overloaded on the +// number of value parameters, but not on the number of template +// parameters. Without the restriction, the meaning of the following +// is unclear: +// +// OverloadedAction(x); +// +// Are we using a single-template-parameter action where 'bool' refers +// to the type of x, or are we using a two-template-parameter action +// where the compiler is asked to infer the type of x? +// +// Implementation notes: +// +// GMOCK_INTERNAL_*_HAS_m_TEMPLATE_PARAMS and +// GMOCK_INTERNAL_*_AND_n_VALUE_PARAMS are internal macros for +// implementing ACTION_TEMPLATE. The main trick we use is to create +// new macro invocations when expanding a macro. For example, we have +// +// #define ACTION_TEMPLATE(name, template_params, value_params) +// ... GMOCK_INTERNAL_DECL_##template_params ... +// +// which causes ACTION_TEMPLATE(..., HAS_1_TEMPLATE_PARAMS(typename, T), ...) +// to expand to +// +// ... GMOCK_INTERNAL_DECL_HAS_1_TEMPLATE_PARAMS(typename, T) ... +// +// Since GMOCK_INTERNAL_DECL_HAS_1_TEMPLATE_PARAMS is a macro, the +// preprocessor will continue to expand it to +// +// ... typename T ... +// +// This technique conforms to the C++ standard and is portable. It +// allows us to implement action templates using O(N) code, where N is +// the maximum number of template/value parameters supported. Without +// using it, we'd have to devote O(N^2) amount of code to implement all +// combinations of m and n. + +// Declares the template parameters. + +$range j 1..n +$for j [[ +$range m 0..j-1 +#define GMOCK_INTERNAL_DECL_HAS_$j[[]] +_TEMPLATE_PARAMS($for m, [[kind$m, name$m]]) $for m, [[kind$m name$m]] + + +]] + +// Lists the template parameters. + +$for j [[ +$range m 0..j-1 +#define GMOCK_INTERNAL_LIST_HAS_$j[[]] +_TEMPLATE_PARAMS($for m, [[kind$m, name$m]]) $for m, [[name$m]] + + +]] + +// Declares the types of value parameters. + +$for i [[ +$range j 0..i-1 +#define GMOCK_INTERNAL_DECL_TYPE_AND_$i[[]] +_VALUE_PARAMS($for j, [[p$j]]) $for j [[, typename p$j##_type]] + + +]] + +// Initializes the value parameters. + +$for i [[ +$range j 0..i-1 +#define GMOCK_INTERNAL_INIT_AND_$i[[]]_VALUE_PARAMS($for j, [[p$j]])\ + ($for j, [[p$j##_type gmock_p$j]])$if i>0 [[ : ]]$for j, [[p$j(gmock_p$j)]] + + +]] + +// Declares the fields for storing the value parameters. + +$for i [[ +$range j 0..i-1 +#define GMOCK_INTERNAL_DEFN_AND_$i[[]] +_VALUE_PARAMS($for j, [[p$j]]) $for j [[p$j##_type p$j; ]] + + +]] + +// Lists the value parameters. + +$for i [[ +$range j 0..i-1 +#define GMOCK_INTERNAL_LIST_AND_$i[[]] +_VALUE_PARAMS($for j, [[p$j]]) $for j, [[p$j]] + + +]] + +// Lists the value parameter types. + +$for i [[ +$range j 0..i-1 +#define GMOCK_INTERNAL_LIST_TYPE_AND_$i[[]] +_VALUE_PARAMS($for j, [[p$j]]) $for j [[, p$j##_type]] + + +]] + +// Declares the value parameters. + +$for i [[ +$range j 0..i-1 +#define GMOCK_INTERNAL_DECL_AND_$i[[]]_VALUE_PARAMS($for j, [[p$j]]) [[]] +$for j, [[p$j##_type p$j]] + + +]] + +// The suffix of the class template implementing the action template. +$for i [[ + + +$range j 0..i-1 +#define GMOCK_INTERNAL_COUNT_AND_$i[[]]_VALUE_PARAMS($for j, [[p$j]]) [[]] +$if i==1 [[P]] $elif i>=2 [[P$i]] +]] + + +// The name of the class template implementing the action template. +#define GMOCK_ACTION_CLASS_(name, value_params)\ + GTEST_CONCAT_TOKEN_(name##Action, GMOCK_INTERNAL_COUNT_##value_params) + +$range k 0..n-1 + +#define ACTION_TEMPLATE(name, template_params, value_params)\ + template \ + class GMOCK_ACTION_CLASS_(name, value_params) {\ + public:\ + GMOCK_ACTION_CLASS_(name, value_params)\ + GMOCK_INTERNAL_INIT_##value_params {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + explicit gmock_Impl GMOCK_INTERNAL_INIT_##value_params {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template <$for k, [[typename arg$k[[]]_type]]>\ + return_type gmock_PerformImpl(const args_type& args[[]] +$for k [[, arg$k[[]]_type arg$k]]) const;\ + GMOCK_INTERNAL_DEFN_##value_params\ + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(\ + new gmock_Impl(GMOCK_INTERNAL_LIST_##value_params));\ + }\ + GMOCK_INTERNAL_DEFN_##value_params\ + private:\ + GTEST_DISALLOW_ASSIGN_(GMOCK_ACTION_CLASS_(name, value_params));\ + };\ + template \ + inline GMOCK_ACTION_CLASS_(name, value_params)<\ + GMOCK_INTERNAL_LIST_##template_params\ + GMOCK_INTERNAL_LIST_TYPE_##value_params> name(\ + GMOCK_INTERNAL_DECL_##value_params) {\ + return GMOCK_ACTION_CLASS_(name, value_params)<\ + GMOCK_INTERNAL_LIST_##template_params\ + GMOCK_INTERNAL_LIST_TYPE_##value_params>(\ + GMOCK_INTERNAL_LIST_##value_params);\ + }\ + template \ + template \ + template \ + typename ::testing::internal::Function::Result\ + GMOCK_ACTION_CLASS_(name, value_params)<\ + GMOCK_INTERNAL_LIST_##template_params\ + GMOCK_INTERNAL_LIST_TYPE_##value_params>::gmock_Impl::\ + gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const + +$for i + +[[ +$var template = [[$if i==0 [[]] $else [[ +$range j 0..i-1 + + template <$for j, [[typename p$j##_type]]>\ +]]]] +$var class_name = [[name##Action[[$if i==0 [[]] $elif i==1 [[P]] + $else [[P$i]]]]]] +$range j 0..i-1 +$var ctor_param_list = [[$for j, [[p$j##_type gmock_p$j]]]] +$var param_types_and_names = [[$for j, [[p$j##_type p$j]]]] +$var inits = [[$if i==0 [[]] $else [[ : $for j, [[p$j(gmock_p$j)]]]]]] +$var param_field_decls = [[$for j +[[ + + p$j##_type p$j;\ +]]]] +$var param_field_decls2 = [[$for j +[[ + + p$j##_type p$j;\ +]]]] +$var params = [[$for j, [[p$j]]]] +$var param_types = [[$if i==0 [[]] $else [[<$for j, [[p$j##_type]]>]]]] +$var typename_arg_types = [[$for k, [[typename arg$k[[]]_type]]]] +$var arg_types_and_names = [[$for k, [[arg$k[[]]_type arg$k]]]] +$var macro_name = [[$if i==0 [[ACTION]] $elif i==1 [[ACTION_P]] + $else [[ACTION_P$i]]]] + +#define $macro_name(name$for j [[, p$j]])\$template + class $class_name {\ + public:\ + $class_name($ctor_param_list)$inits {}\ + template \ + class gmock_Impl : public ::testing::ActionInterface {\ + public:\ + typedef F function_type;\ + typedef typename ::testing::internal::Function::Result return_type;\ + typedef typename ::testing::internal::Function::ArgumentTuple\ + args_type;\ + [[$if i==1 [[explicit ]]]]gmock_Impl($ctor_param_list)$inits {}\ + virtual return_type Perform(const args_type& args) {\ + return ::testing::internal::ActionHelper::\ + Perform(this, args);\ + }\ + template <$typename_arg_types>\ + return_type gmock_PerformImpl(const args_type& args, [[]] +$arg_types_and_names) const;\$param_field_decls + private:\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template operator ::testing::Action() const {\ + return ::testing::Action(new gmock_Impl($params));\ + }\$param_field_decls2 + private:\ + GTEST_DISALLOW_ASSIGN_($class_name);\ + };\$template + inline $class_name$param_types name($param_types_and_names) {\ + return $class_name$param_types($params);\ + }\$template + template \ + template <$typename_arg_types>\ + typename ::testing::internal::Function::Result\ + $class_name$param_types::gmock_Impl::gmock_PerformImpl(\ + GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const +]] +$$ } // This meta comment fixes auto-indentation in Emacs. It won't +$$ // show up in the generated code. + + +// TODO(wan@google.com): move the following to a different .h file +// such that we don't have to run 'pump' every time the code is +// updated. +namespace testing { + +// The ACTION*() macros trigger warning C4100 (unreferenced formal +// parameter) in MSVC with -W4. Unfortunately they cannot be fixed in +// the macro definition, as the warnings are generated when the macro +// is expanded and macro expansion cannot contain #pragma. Therefore +// we suppress them here. +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable:4100) +#endif + +// Various overloads for InvokeArgument(). +// +// The InvokeArgument(a1, a2, ..., a_k) action invokes the N-th +// (0-based) argument, which must be a k-ary callable, of the mock +// function, with arguments a1, a2, ..., a_k. +// +// Notes: +// +// 1. The arguments are passed by value by default. If you need to +// pass an argument by reference, wrap it inside ByRef(). For +// example, +// +// InvokeArgument<1>(5, string("Hello"), ByRef(foo)) +// +// passes 5 and string("Hello") by value, and passes foo by +// reference. +// +// 2. If the callable takes an argument by reference but ByRef() is +// not used, it will receive the reference to a copy of the value, +// instead of the original value. For example, when the 0-th +// argument of the mock function takes a const string&, the action +// +// InvokeArgument<0>(string("Hello")) +// +// makes a copy of the temporary string("Hello") object and passes a +// reference of the copy, instead of the original temporary object, +// to the callable. This makes it easy for a user to define an +// InvokeArgument action from temporary values and have it performed +// later. + +$range i 0..n +$for i [[ +$range j 0..i-1 + +ACTION_TEMPLATE(InvokeArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_$i[[]]_VALUE_PARAMS($for j, [[p$j]])) { + return internal::CallableHelper::Call( + ::std::tr1::get(args)$for j [[, p$j]]); +} + +]] + +// Various overloads for ReturnNew(). +// +// The ReturnNew(a1, a2, ..., a_k) action returns a pointer to a new +// instance of type T, constructed on the heap with constructor arguments +// a1, a2, ..., and a_k. The caller assumes ownership of the returned value. +$range i 0..n +$for i [[ +$range j 0..i-1 +$var ps = [[$for j, [[p$j]]]] + +ACTION_TEMPLATE(ReturnNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_$i[[]]_VALUE_PARAMS($ps)) { + return new T($ps); +} + +]] + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + +} // namespace testing + +#endif // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_ACTIONS_H_ diff --git a/libwvdrmengine/test/gmock/include/gmock/gmock-generated-function-mockers.h b/libwvdrmengine/test/gmock/include/gmock/gmock-generated-function-mockers.h new file mode 100644 index 00000000..509d46cb --- /dev/null +++ b/libwvdrmengine/test/gmock/include/gmock/gmock-generated-function-mockers.h @@ -0,0 +1,929 @@ +// This file was GENERATED by command: +// pump.py gmock-generated-function-mockers.h.pump +// DO NOT EDIT BY HAND!!! + +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file implements function mockers of various arities. + +#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_FUNCTION_MOCKERS_H_ +#define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_FUNCTION_MOCKERS_H_ + +#include "gmock/gmock-spec-builders.h" +#include "gmock/internal/gmock-internal-utils.h" + +namespace testing { +namespace internal { + +template +class FunctionMockerBase; + +// Note: class FunctionMocker really belongs to the ::testing +// namespace. However if we define it in ::testing, MSVC will +// complain when classes in ::testing::internal declare it as a +// friend class template. To workaround this compiler bug, we define +// FunctionMocker in ::testing::internal and import it into ::testing. +template +class FunctionMocker; + +template +class FunctionMocker : public + internal::FunctionMockerBase { + public: + typedef R F(); + typedef typename internal::Function::ArgumentTuple ArgumentTuple; + + MockSpec& With() { + return this->current_spec(); + } + + R Invoke() { + // Even though gcc and MSVC don't enforce it, 'this->' is required + // by the C++ standard [14.6.4] here, as the base class type is + // dependent on the template argument (and thus shouldn't be + // looked into when resolving InvokeWith). + return this->InvokeWith(ArgumentTuple()); + } +}; + +template +class FunctionMocker : public + internal::FunctionMockerBase { + public: + typedef R F(A1); + typedef typename internal::Function::ArgumentTuple ArgumentTuple; + + MockSpec& With(const Matcher& m1) { + this->current_spec().SetMatchers(::std::tr1::make_tuple(m1)); + return this->current_spec(); + } + + R Invoke(A1 a1) { + // Even though gcc and MSVC don't enforce it, 'this->' is required + // by the C++ standard [14.6.4] here, as the base class type is + // dependent on the template argument (and thus shouldn't be + // looked into when resolving InvokeWith). + return this->InvokeWith(ArgumentTuple(a1)); + } +}; + +template +class FunctionMocker : public + internal::FunctionMockerBase { + public: + typedef R F(A1, A2); + typedef typename internal::Function::ArgumentTuple ArgumentTuple; + + MockSpec& With(const Matcher& m1, const Matcher& m2) { + this->current_spec().SetMatchers(::std::tr1::make_tuple(m1, m2)); + return this->current_spec(); + } + + R Invoke(A1 a1, A2 a2) { + // Even though gcc and MSVC don't enforce it, 'this->' is required + // by the C++ standard [14.6.4] here, as the base class type is + // dependent on the template argument (and thus shouldn't be + // looked into when resolving InvokeWith). + return this->InvokeWith(ArgumentTuple(a1, a2)); + } +}; + +template +class FunctionMocker : public + internal::FunctionMockerBase { + public: + typedef R F(A1, A2, A3); + typedef typename internal::Function::ArgumentTuple ArgumentTuple; + + MockSpec& With(const Matcher& m1, const Matcher& m2, + const Matcher& m3) { + this->current_spec().SetMatchers(::std::tr1::make_tuple(m1, m2, m3)); + return this->current_spec(); + } + + R Invoke(A1 a1, A2 a2, A3 a3) { + // Even though gcc and MSVC don't enforce it, 'this->' is required + // by the C++ standard [14.6.4] here, as the base class type is + // dependent on the template argument (and thus shouldn't be + // looked into when resolving InvokeWith). + return this->InvokeWith(ArgumentTuple(a1, a2, a3)); + } +}; + +template +class FunctionMocker : public + internal::FunctionMockerBase { + public: + typedef R F(A1, A2, A3, A4); + typedef typename internal::Function::ArgumentTuple ArgumentTuple; + + MockSpec& With(const Matcher& m1, const Matcher& m2, + const Matcher& m3, const Matcher& m4) { + this->current_spec().SetMatchers(::std::tr1::make_tuple(m1, m2, m3, m4)); + return this->current_spec(); + } + + R Invoke(A1 a1, A2 a2, A3 a3, A4 a4) { + // Even though gcc and MSVC don't enforce it, 'this->' is required + // by the C++ standard [14.6.4] here, as the base class type is + // dependent on the template argument (and thus shouldn't be + // looked into when resolving InvokeWith). + return this->InvokeWith(ArgumentTuple(a1, a2, a3, a4)); + } +}; + +template +class FunctionMocker : public + internal::FunctionMockerBase { + public: + typedef R F(A1, A2, A3, A4, A5); + typedef typename internal::Function::ArgumentTuple ArgumentTuple; + + MockSpec& With(const Matcher& m1, const Matcher& m2, + const Matcher& m3, const Matcher& m4, const Matcher& m5) { + this->current_spec().SetMatchers(::std::tr1::make_tuple(m1, m2, m3, m4, + m5)); + return this->current_spec(); + } + + R Invoke(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { + // Even though gcc and MSVC don't enforce it, 'this->' is required + // by the C++ standard [14.6.4] here, as the base class type is + // dependent on the template argument (and thus shouldn't be + // looked into when resolving InvokeWith). + return this->InvokeWith(ArgumentTuple(a1, a2, a3, a4, a5)); + } +}; + +template +class FunctionMocker : public + internal::FunctionMockerBase { + public: + typedef R F(A1, A2, A3, A4, A5, A6); + typedef typename internal::Function::ArgumentTuple ArgumentTuple; + + MockSpec& With(const Matcher& m1, const Matcher& m2, + const Matcher& m3, const Matcher& m4, const Matcher& m5, + const Matcher& m6) { + this->current_spec().SetMatchers(::std::tr1::make_tuple(m1, m2, m3, m4, m5, + m6)); + return this->current_spec(); + } + + R Invoke(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { + // Even though gcc and MSVC don't enforce it, 'this->' is required + // by the C++ standard [14.6.4] here, as the base class type is + // dependent on the template argument (and thus shouldn't be + // looked into when resolving InvokeWith). + return this->InvokeWith(ArgumentTuple(a1, a2, a3, a4, a5, a6)); + } +}; + +template +class FunctionMocker : public + internal::FunctionMockerBase { + public: + typedef R F(A1, A2, A3, A4, A5, A6, A7); + typedef typename internal::Function::ArgumentTuple ArgumentTuple; + + MockSpec& With(const Matcher& m1, const Matcher& m2, + const Matcher& m3, const Matcher& m4, const Matcher& m5, + const Matcher& m6, const Matcher& m7) { + this->current_spec().SetMatchers(::std::tr1::make_tuple(m1, m2, m3, m4, m5, + m6, m7)); + return this->current_spec(); + } + + R Invoke(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) { + // Even though gcc and MSVC don't enforce it, 'this->' is required + // by the C++ standard [14.6.4] here, as the base class type is + // dependent on the template argument (and thus shouldn't be + // looked into when resolving InvokeWith). + return this->InvokeWith(ArgumentTuple(a1, a2, a3, a4, a5, a6, a7)); + } +}; + +template +class FunctionMocker : public + internal::FunctionMockerBase { + public: + typedef R F(A1, A2, A3, A4, A5, A6, A7, A8); + typedef typename internal::Function::ArgumentTuple ArgumentTuple; + + MockSpec& With(const Matcher& m1, const Matcher& m2, + const Matcher& m3, const Matcher& m4, const Matcher& m5, + const Matcher& m6, const Matcher& m7, const Matcher& m8) { + this->current_spec().SetMatchers(::std::tr1::make_tuple(m1, m2, m3, m4, m5, + m6, m7, m8)); + return this->current_spec(); + } + + R Invoke(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) { + // Even though gcc and MSVC don't enforce it, 'this->' is required + // by the C++ standard [14.6.4] here, as the base class type is + // dependent on the template argument (and thus shouldn't be + // looked into when resolving InvokeWith). + return this->InvokeWith(ArgumentTuple(a1, a2, a3, a4, a5, a6, a7, a8)); + } +}; + +template +class FunctionMocker : public + internal::FunctionMockerBase { + public: + typedef R F(A1, A2, A3, A4, A5, A6, A7, A8, A9); + typedef typename internal::Function::ArgumentTuple ArgumentTuple; + + MockSpec& With(const Matcher& m1, const Matcher& m2, + const Matcher& m3, const Matcher& m4, const Matcher& m5, + const Matcher& m6, const Matcher& m7, const Matcher& m8, + const Matcher& m9) { + this->current_spec().SetMatchers(::std::tr1::make_tuple(m1, m2, m3, m4, m5, + m6, m7, m8, m9)); + return this->current_spec(); + } + + R Invoke(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) { + // Even though gcc and MSVC don't enforce it, 'this->' is required + // by the C++ standard [14.6.4] here, as the base class type is + // dependent on the template argument (and thus shouldn't be + // looked into when resolving InvokeWith). + return this->InvokeWith(ArgumentTuple(a1, a2, a3, a4, a5, a6, a7, a8, a9)); + } +}; + +template +class FunctionMocker : public + internal::FunctionMockerBase { + public: + typedef R F(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10); + typedef typename internal::Function::ArgumentTuple ArgumentTuple; + + MockSpec& With(const Matcher& m1, const Matcher& m2, + const Matcher& m3, const Matcher& m4, const Matcher& m5, + const Matcher& m6, const Matcher& m7, const Matcher& m8, + const Matcher& m9, const Matcher& m10) { + this->current_spec().SetMatchers(::std::tr1::make_tuple(m1, m2, m3, m4, m5, + m6, m7, m8, m9, m10)); + return this->current_spec(); + } + + R Invoke(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, + A10 a10) { + // Even though gcc and MSVC don't enforce it, 'this->' is required + // by the C++ standard [14.6.4] here, as the base class type is + // dependent on the template argument (and thus shouldn't be + // looked into when resolving InvokeWith). + return this->InvokeWith(ArgumentTuple(a1, a2, a3, a4, a5, a6, a7, a8, a9, + a10)); + } +}; + +} // namespace internal + +// The style guide prohibits "using" statements in a namespace scope +// inside a header file. However, the FunctionMocker class template +// is meant to be defined in the ::testing namespace. The following +// line is just a trick for working around a bug in MSVC 8.0, which +// cannot handle it if we define FunctionMocker in ::testing. +using internal::FunctionMocker; + +// The result type of function type F. +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_RESULT_(tn, F) tn ::testing::internal::Function::Result + +// The type of argument N of function type F. +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_ARG_(tn, F, N) tn ::testing::internal::Function::Argument##N + +// The matcher type for argument N of function type F. +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_MATCHER_(tn, F, N) const ::testing::Matcher& + +// The variable for mocking the given method. +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_MOCKER_(arity, constness, Method) \ + GTEST_CONCAT_TOKEN_(gmock##constness##arity##_##Method##_, __LINE__) + +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_METHOD0_(tn, constness, ct, Method, F) \ + GMOCK_RESULT_(tn, F) ct Method() constness { \ + GTEST_COMPILE_ASSERT_(::std::tr1::tuple_size< \ + tn ::testing::internal::Function::ArgumentTuple>::value == 0, \ + this_method_does_not_take_0_arguments); \ + GMOCK_MOCKER_(0, constness, Method).SetOwnerAndName(this, #Method); \ + return GMOCK_MOCKER_(0, constness, Method).Invoke(); \ + } \ + ::testing::MockSpec& \ + gmock_##Method() constness { \ + GMOCK_MOCKER_(0, constness, Method).RegisterOwner(this); \ + return GMOCK_MOCKER_(0, constness, Method).With(); \ + } \ + mutable ::testing::FunctionMocker GMOCK_MOCKER_(0, constness, Method) + +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_METHOD1_(tn, constness, ct, Method, F) \ + GMOCK_RESULT_(tn, F) ct Method(GMOCK_ARG_(tn, F, 1) gmock_a1) constness { \ + GTEST_COMPILE_ASSERT_(::std::tr1::tuple_size< \ + tn ::testing::internal::Function::ArgumentTuple>::value == 1, \ + this_method_does_not_take_1_argument); \ + GMOCK_MOCKER_(1, constness, Method).SetOwnerAndName(this, #Method); \ + return GMOCK_MOCKER_(1, constness, Method).Invoke(gmock_a1); \ + } \ + ::testing::MockSpec& \ + gmock_##Method(GMOCK_MATCHER_(tn, F, 1) gmock_a1) constness { \ + GMOCK_MOCKER_(1, constness, Method).RegisterOwner(this); \ + return GMOCK_MOCKER_(1, constness, Method).With(gmock_a1); \ + } \ + mutable ::testing::FunctionMocker GMOCK_MOCKER_(1, constness, Method) + +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_METHOD2_(tn, constness, ct, Method, F) \ + GMOCK_RESULT_(tn, F) ct Method(GMOCK_ARG_(tn, F, 1) gmock_a1, \ + GMOCK_ARG_(tn, F, 2) gmock_a2) constness { \ + GTEST_COMPILE_ASSERT_(::std::tr1::tuple_size< \ + tn ::testing::internal::Function::ArgumentTuple>::value == 2, \ + this_method_does_not_take_2_arguments); \ + GMOCK_MOCKER_(2, constness, Method).SetOwnerAndName(this, #Method); \ + return GMOCK_MOCKER_(2, constness, Method).Invoke(gmock_a1, gmock_a2); \ + } \ + ::testing::MockSpec& \ + gmock_##Method(GMOCK_MATCHER_(tn, F, 1) gmock_a1, \ + GMOCK_MATCHER_(tn, F, 2) gmock_a2) constness { \ + GMOCK_MOCKER_(2, constness, Method).RegisterOwner(this); \ + return GMOCK_MOCKER_(2, constness, Method).With(gmock_a1, gmock_a2); \ + } \ + mutable ::testing::FunctionMocker GMOCK_MOCKER_(2, constness, Method) + +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_METHOD3_(tn, constness, ct, Method, F) \ + GMOCK_RESULT_(tn, F) ct Method(GMOCK_ARG_(tn, F, 1) gmock_a1, \ + GMOCK_ARG_(tn, F, 2) gmock_a2, \ + GMOCK_ARG_(tn, F, 3) gmock_a3) constness { \ + GTEST_COMPILE_ASSERT_(::std::tr1::tuple_size< \ + tn ::testing::internal::Function::ArgumentTuple>::value == 3, \ + this_method_does_not_take_3_arguments); \ + GMOCK_MOCKER_(3, constness, Method).SetOwnerAndName(this, #Method); \ + return GMOCK_MOCKER_(3, constness, Method).Invoke(gmock_a1, gmock_a2, \ + gmock_a3); \ + } \ + ::testing::MockSpec& \ + gmock_##Method(GMOCK_MATCHER_(tn, F, 1) gmock_a1, \ + GMOCK_MATCHER_(tn, F, 2) gmock_a2, \ + GMOCK_MATCHER_(tn, F, 3) gmock_a3) constness { \ + GMOCK_MOCKER_(3, constness, Method).RegisterOwner(this); \ + return GMOCK_MOCKER_(3, constness, Method).With(gmock_a1, gmock_a2, \ + gmock_a3); \ + } \ + mutable ::testing::FunctionMocker GMOCK_MOCKER_(3, constness, Method) + +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_METHOD4_(tn, constness, ct, Method, F) \ + GMOCK_RESULT_(tn, F) ct Method(GMOCK_ARG_(tn, F, 1) gmock_a1, \ + GMOCK_ARG_(tn, F, 2) gmock_a2, \ + GMOCK_ARG_(tn, F, 3) gmock_a3, \ + GMOCK_ARG_(tn, F, 4) gmock_a4) constness { \ + GTEST_COMPILE_ASSERT_(::std::tr1::tuple_size< \ + tn ::testing::internal::Function::ArgumentTuple>::value == 4, \ + this_method_does_not_take_4_arguments); \ + GMOCK_MOCKER_(4, constness, Method).SetOwnerAndName(this, #Method); \ + return GMOCK_MOCKER_(4, constness, Method).Invoke(gmock_a1, gmock_a2, \ + gmock_a3, gmock_a4); \ + } \ + ::testing::MockSpec& \ + gmock_##Method(GMOCK_MATCHER_(tn, F, 1) gmock_a1, \ + GMOCK_MATCHER_(tn, F, 2) gmock_a2, \ + GMOCK_MATCHER_(tn, F, 3) gmock_a3, \ + GMOCK_MATCHER_(tn, F, 4) gmock_a4) constness { \ + GMOCK_MOCKER_(4, constness, Method).RegisterOwner(this); \ + return GMOCK_MOCKER_(4, constness, Method).With(gmock_a1, gmock_a2, \ + gmock_a3, gmock_a4); \ + } \ + mutable ::testing::FunctionMocker GMOCK_MOCKER_(4, constness, Method) + +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_METHOD5_(tn, constness, ct, Method, F) \ + GMOCK_RESULT_(tn, F) ct Method(GMOCK_ARG_(tn, F, 1) gmock_a1, \ + GMOCK_ARG_(tn, F, 2) gmock_a2, \ + GMOCK_ARG_(tn, F, 3) gmock_a3, \ + GMOCK_ARG_(tn, F, 4) gmock_a4, \ + GMOCK_ARG_(tn, F, 5) gmock_a5) constness { \ + GTEST_COMPILE_ASSERT_(::std::tr1::tuple_size< \ + tn ::testing::internal::Function::ArgumentTuple>::value == 5, \ + this_method_does_not_take_5_arguments); \ + GMOCK_MOCKER_(5, constness, Method).SetOwnerAndName(this, #Method); \ + return GMOCK_MOCKER_(5, constness, Method).Invoke(gmock_a1, gmock_a2, \ + gmock_a3, gmock_a4, gmock_a5); \ + } \ + ::testing::MockSpec& \ + gmock_##Method(GMOCK_MATCHER_(tn, F, 1) gmock_a1, \ + GMOCK_MATCHER_(tn, F, 2) gmock_a2, \ + GMOCK_MATCHER_(tn, F, 3) gmock_a3, \ + GMOCK_MATCHER_(tn, F, 4) gmock_a4, \ + GMOCK_MATCHER_(tn, F, 5) gmock_a5) constness { \ + GMOCK_MOCKER_(5, constness, Method).RegisterOwner(this); \ + return GMOCK_MOCKER_(5, constness, Method).With(gmock_a1, gmock_a2, \ + gmock_a3, gmock_a4, gmock_a5); \ + } \ + mutable ::testing::FunctionMocker GMOCK_MOCKER_(5, constness, Method) + +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_METHOD6_(tn, constness, ct, Method, F) \ + GMOCK_RESULT_(tn, F) ct Method(GMOCK_ARG_(tn, F, 1) gmock_a1, \ + GMOCK_ARG_(tn, F, 2) gmock_a2, \ + GMOCK_ARG_(tn, F, 3) gmock_a3, \ + GMOCK_ARG_(tn, F, 4) gmock_a4, \ + GMOCK_ARG_(tn, F, 5) gmock_a5, \ + GMOCK_ARG_(tn, F, 6) gmock_a6) constness { \ + GTEST_COMPILE_ASSERT_(::std::tr1::tuple_size< \ + tn ::testing::internal::Function::ArgumentTuple>::value == 6, \ + this_method_does_not_take_6_arguments); \ + GMOCK_MOCKER_(6, constness, Method).SetOwnerAndName(this, #Method); \ + return GMOCK_MOCKER_(6, constness, Method).Invoke(gmock_a1, gmock_a2, \ + gmock_a3, gmock_a4, gmock_a5, gmock_a6); \ + } \ + ::testing::MockSpec& \ + gmock_##Method(GMOCK_MATCHER_(tn, F, 1) gmock_a1, \ + GMOCK_MATCHER_(tn, F, 2) gmock_a2, \ + GMOCK_MATCHER_(tn, F, 3) gmock_a3, \ + GMOCK_MATCHER_(tn, F, 4) gmock_a4, \ + GMOCK_MATCHER_(tn, F, 5) gmock_a5, \ + GMOCK_MATCHER_(tn, F, 6) gmock_a6) constness { \ + GMOCK_MOCKER_(6, constness, Method).RegisterOwner(this); \ + return GMOCK_MOCKER_(6, constness, Method).With(gmock_a1, gmock_a2, \ + gmock_a3, gmock_a4, gmock_a5, gmock_a6); \ + } \ + mutable ::testing::FunctionMocker GMOCK_MOCKER_(6, constness, Method) + +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_METHOD7_(tn, constness, ct, Method, F) \ + GMOCK_RESULT_(tn, F) ct Method(GMOCK_ARG_(tn, F, 1) gmock_a1, \ + GMOCK_ARG_(tn, F, 2) gmock_a2, \ + GMOCK_ARG_(tn, F, 3) gmock_a3, \ + GMOCK_ARG_(tn, F, 4) gmock_a4, \ + GMOCK_ARG_(tn, F, 5) gmock_a5, \ + GMOCK_ARG_(tn, F, 6) gmock_a6, \ + GMOCK_ARG_(tn, F, 7) gmock_a7) constness { \ + GTEST_COMPILE_ASSERT_(::std::tr1::tuple_size< \ + tn ::testing::internal::Function::ArgumentTuple>::value == 7, \ + this_method_does_not_take_7_arguments); \ + GMOCK_MOCKER_(7, constness, Method).SetOwnerAndName(this, #Method); \ + return GMOCK_MOCKER_(7, constness, Method).Invoke(gmock_a1, gmock_a2, \ + gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7); \ + } \ + ::testing::MockSpec& \ + gmock_##Method(GMOCK_MATCHER_(tn, F, 1) gmock_a1, \ + GMOCK_MATCHER_(tn, F, 2) gmock_a2, \ + GMOCK_MATCHER_(tn, F, 3) gmock_a3, \ + GMOCK_MATCHER_(tn, F, 4) gmock_a4, \ + GMOCK_MATCHER_(tn, F, 5) gmock_a5, \ + GMOCK_MATCHER_(tn, F, 6) gmock_a6, \ + GMOCK_MATCHER_(tn, F, 7) gmock_a7) constness { \ + GMOCK_MOCKER_(7, constness, Method).RegisterOwner(this); \ + return GMOCK_MOCKER_(7, constness, Method).With(gmock_a1, gmock_a2, \ + gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7); \ + } \ + mutable ::testing::FunctionMocker GMOCK_MOCKER_(7, constness, Method) + +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_METHOD8_(tn, constness, ct, Method, F) \ + GMOCK_RESULT_(tn, F) ct Method(GMOCK_ARG_(tn, F, 1) gmock_a1, \ + GMOCK_ARG_(tn, F, 2) gmock_a2, \ + GMOCK_ARG_(tn, F, 3) gmock_a3, \ + GMOCK_ARG_(tn, F, 4) gmock_a4, \ + GMOCK_ARG_(tn, F, 5) gmock_a5, \ + GMOCK_ARG_(tn, F, 6) gmock_a6, \ + GMOCK_ARG_(tn, F, 7) gmock_a7, \ + GMOCK_ARG_(tn, F, 8) gmock_a8) constness { \ + GTEST_COMPILE_ASSERT_(::std::tr1::tuple_size< \ + tn ::testing::internal::Function::ArgumentTuple>::value == 8, \ + this_method_does_not_take_8_arguments); \ + GMOCK_MOCKER_(8, constness, Method).SetOwnerAndName(this, #Method); \ + return GMOCK_MOCKER_(8, constness, Method).Invoke(gmock_a1, gmock_a2, \ + gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7, gmock_a8); \ + } \ + ::testing::MockSpec& \ + gmock_##Method(GMOCK_MATCHER_(tn, F, 1) gmock_a1, \ + GMOCK_MATCHER_(tn, F, 2) gmock_a2, \ + GMOCK_MATCHER_(tn, F, 3) gmock_a3, \ + GMOCK_MATCHER_(tn, F, 4) gmock_a4, \ + GMOCK_MATCHER_(tn, F, 5) gmock_a5, \ + GMOCK_MATCHER_(tn, F, 6) gmock_a6, \ + GMOCK_MATCHER_(tn, F, 7) gmock_a7, \ + GMOCK_MATCHER_(tn, F, 8) gmock_a8) constness { \ + GMOCK_MOCKER_(8, constness, Method).RegisterOwner(this); \ + return GMOCK_MOCKER_(8, constness, Method).With(gmock_a1, gmock_a2, \ + gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7, gmock_a8); \ + } \ + mutable ::testing::FunctionMocker GMOCK_MOCKER_(8, constness, Method) + +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_METHOD9_(tn, constness, ct, Method, F) \ + GMOCK_RESULT_(tn, F) ct Method(GMOCK_ARG_(tn, F, 1) gmock_a1, \ + GMOCK_ARG_(tn, F, 2) gmock_a2, \ + GMOCK_ARG_(tn, F, 3) gmock_a3, \ + GMOCK_ARG_(tn, F, 4) gmock_a4, \ + GMOCK_ARG_(tn, F, 5) gmock_a5, \ + GMOCK_ARG_(tn, F, 6) gmock_a6, \ + GMOCK_ARG_(tn, F, 7) gmock_a7, \ + GMOCK_ARG_(tn, F, 8) gmock_a8, \ + GMOCK_ARG_(tn, F, 9) gmock_a9) constness { \ + GTEST_COMPILE_ASSERT_(::std::tr1::tuple_size< \ + tn ::testing::internal::Function::ArgumentTuple>::value == 9, \ + this_method_does_not_take_9_arguments); \ + GMOCK_MOCKER_(9, constness, Method).SetOwnerAndName(this, #Method); \ + return GMOCK_MOCKER_(9, constness, Method).Invoke(gmock_a1, gmock_a2, \ + gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7, gmock_a8, \ + gmock_a9); \ + } \ + ::testing::MockSpec& \ + gmock_##Method(GMOCK_MATCHER_(tn, F, 1) gmock_a1, \ + GMOCK_MATCHER_(tn, F, 2) gmock_a2, \ + GMOCK_MATCHER_(tn, F, 3) gmock_a3, \ + GMOCK_MATCHER_(tn, F, 4) gmock_a4, \ + GMOCK_MATCHER_(tn, F, 5) gmock_a5, \ + GMOCK_MATCHER_(tn, F, 6) gmock_a6, \ + GMOCK_MATCHER_(tn, F, 7) gmock_a7, \ + GMOCK_MATCHER_(tn, F, 8) gmock_a8, \ + GMOCK_MATCHER_(tn, F, 9) gmock_a9) constness { \ + GMOCK_MOCKER_(9, constness, Method).RegisterOwner(this); \ + return GMOCK_MOCKER_(9, constness, Method).With(gmock_a1, gmock_a2, \ + gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7, gmock_a8, \ + gmock_a9); \ + } \ + mutable ::testing::FunctionMocker GMOCK_MOCKER_(9, constness, Method) + +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_METHOD10_(tn, constness, ct, Method, F) \ + GMOCK_RESULT_(tn, F) ct Method(GMOCK_ARG_(tn, F, 1) gmock_a1, \ + GMOCK_ARG_(tn, F, 2) gmock_a2, \ + GMOCK_ARG_(tn, F, 3) gmock_a3, \ + GMOCK_ARG_(tn, F, 4) gmock_a4, \ + GMOCK_ARG_(tn, F, 5) gmock_a5, \ + GMOCK_ARG_(tn, F, 6) gmock_a6, \ + GMOCK_ARG_(tn, F, 7) gmock_a7, \ + GMOCK_ARG_(tn, F, 8) gmock_a8, \ + GMOCK_ARG_(tn, F, 9) gmock_a9, \ + GMOCK_ARG_(tn, F, 10) gmock_a10) constness { \ + GTEST_COMPILE_ASSERT_(::std::tr1::tuple_size< \ + tn ::testing::internal::Function::ArgumentTuple>::value == 10, \ + this_method_does_not_take_10_arguments); \ + GMOCK_MOCKER_(10, constness, Method).SetOwnerAndName(this, #Method); \ + return GMOCK_MOCKER_(10, constness, Method).Invoke(gmock_a1, gmock_a2, \ + gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7, gmock_a8, gmock_a9, \ + gmock_a10); \ + } \ + ::testing::MockSpec& \ + gmock_##Method(GMOCK_MATCHER_(tn, F, 1) gmock_a1, \ + GMOCK_MATCHER_(tn, F, 2) gmock_a2, \ + GMOCK_MATCHER_(tn, F, 3) gmock_a3, \ + GMOCK_MATCHER_(tn, F, 4) gmock_a4, \ + GMOCK_MATCHER_(tn, F, 5) gmock_a5, \ + GMOCK_MATCHER_(tn, F, 6) gmock_a6, \ + GMOCK_MATCHER_(tn, F, 7) gmock_a7, \ + GMOCK_MATCHER_(tn, F, 8) gmock_a8, \ + GMOCK_MATCHER_(tn, F, 9) gmock_a9, \ + GMOCK_MATCHER_(tn, F, 10) gmock_a10) constness { \ + GMOCK_MOCKER_(10, constness, Method).RegisterOwner(this); \ + return GMOCK_MOCKER_(10, constness, Method).With(gmock_a1, gmock_a2, \ + gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7, gmock_a8, gmock_a9, \ + gmock_a10); \ + } \ + mutable ::testing::FunctionMocker GMOCK_MOCKER_(10, constness, Method) + +#define MOCK_METHOD0(m, F) GMOCK_METHOD0_(, , , m, F) +#define MOCK_METHOD1(m, F) GMOCK_METHOD1_(, , , m, F) +#define MOCK_METHOD2(m, F) GMOCK_METHOD2_(, , , m, F) +#define MOCK_METHOD3(m, F) GMOCK_METHOD3_(, , , m, F) +#define MOCK_METHOD4(m, F) GMOCK_METHOD4_(, , , m, F) +#define MOCK_METHOD5(m, F) GMOCK_METHOD5_(, , , m, F) +#define MOCK_METHOD6(m, F) GMOCK_METHOD6_(, , , m, F) +#define MOCK_METHOD7(m, F) GMOCK_METHOD7_(, , , m, F) +#define MOCK_METHOD8(m, F) GMOCK_METHOD8_(, , , m, F) +#define MOCK_METHOD9(m, F) GMOCK_METHOD9_(, , , m, F) +#define MOCK_METHOD10(m, F) GMOCK_METHOD10_(, , , m, F) + +#define MOCK_CONST_METHOD0(m, F) GMOCK_METHOD0_(, const, , m, F) +#define MOCK_CONST_METHOD1(m, F) GMOCK_METHOD1_(, const, , m, F) +#define MOCK_CONST_METHOD2(m, F) GMOCK_METHOD2_(, const, , m, F) +#define MOCK_CONST_METHOD3(m, F) GMOCK_METHOD3_(, const, , m, F) +#define MOCK_CONST_METHOD4(m, F) GMOCK_METHOD4_(, const, , m, F) +#define MOCK_CONST_METHOD5(m, F) GMOCK_METHOD5_(, const, , m, F) +#define MOCK_CONST_METHOD6(m, F) GMOCK_METHOD6_(, const, , m, F) +#define MOCK_CONST_METHOD7(m, F) GMOCK_METHOD7_(, const, , m, F) +#define MOCK_CONST_METHOD8(m, F) GMOCK_METHOD8_(, const, , m, F) +#define MOCK_CONST_METHOD9(m, F) GMOCK_METHOD9_(, const, , m, F) +#define MOCK_CONST_METHOD10(m, F) GMOCK_METHOD10_(, const, , m, F) + +#define MOCK_METHOD0_T(m, F) GMOCK_METHOD0_(typename, , , m, F) +#define MOCK_METHOD1_T(m, F) GMOCK_METHOD1_(typename, , , m, F) +#define MOCK_METHOD2_T(m, F) GMOCK_METHOD2_(typename, , , m, F) +#define MOCK_METHOD3_T(m, F) GMOCK_METHOD3_(typename, , , m, F) +#define MOCK_METHOD4_T(m, F) GMOCK_METHOD4_(typename, , , m, F) +#define MOCK_METHOD5_T(m, F) GMOCK_METHOD5_(typename, , , m, F) +#define MOCK_METHOD6_T(m, F) GMOCK_METHOD6_(typename, , , m, F) +#define MOCK_METHOD7_T(m, F) GMOCK_METHOD7_(typename, , , m, F) +#define MOCK_METHOD8_T(m, F) GMOCK_METHOD8_(typename, , , m, F) +#define MOCK_METHOD9_T(m, F) GMOCK_METHOD9_(typename, , , m, F) +#define MOCK_METHOD10_T(m, F) GMOCK_METHOD10_(typename, , , m, F) + +#define MOCK_CONST_METHOD0_T(m, F) GMOCK_METHOD0_(typename, const, , m, F) +#define MOCK_CONST_METHOD1_T(m, F) GMOCK_METHOD1_(typename, const, , m, F) +#define MOCK_CONST_METHOD2_T(m, F) GMOCK_METHOD2_(typename, const, , m, F) +#define MOCK_CONST_METHOD3_T(m, F) GMOCK_METHOD3_(typename, const, , m, F) +#define MOCK_CONST_METHOD4_T(m, F) GMOCK_METHOD4_(typename, const, , m, F) +#define MOCK_CONST_METHOD5_T(m, F) GMOCK_METHOD5_(typename, const, , m, F) +#define MOCK_CONST_METHOD6_T(m, F) GMOCK_METHOD6_(typename, const, , m, F) +#define MOCK_CONST_METHOD7_T(m, F) GMOCK_METHOD7_(typename, const, , m, F) +#define MOCK_CONST_METHOD8_T(m, F) GMOCK_METHOD8_(typename, const, , m, F) +#define MOCK_CONST_METHOD9_T(m, F) GMOCK_METHOD9_(typename, const, , m, F) +#define MOCK_CONST_METHOD10_T(m, F) GMOCK_METHOD10_(typename, const, , m, F) + +#define MOCK_METHOD0_WITH_CALLTYPE(ct, m, F) GMOCK_METHOD0_(, , ct, m, F) +#define MOCK_METHOD1_WITH_CALLTYPE(ct, m, F) GMOCK_METHOD1_(, , ct, m, F) +#define MOCK_METHOD2_WITH_CALLTYPE(ct, m, F) GMOCK_METHOD2_(, , ct, m, F) +#define MOCK_METHOD3_WITH_CALLTYPE(ct, m, F) GMOCK_METHOD3_(, , ct, m, F) +#define MOCK_METHOD4_WITH_CALLTYPE(ct, m, F) GMOCK_METHOD4_(, , ct, m, F) +#define MOCK_METHOD5_WITH_CALLTYPE(ct, m, F) GMOCK_METHOD5_(, , ct, m, F) +#define MOCK_METHOD6_WITH_CALLTYPE(ct, m, F) GMOCK_METHOD6_(, , ct, m, F) +#define MOCK_METHOD7_WITH_CALLTYPE(ct, m, F) GMOCK_METHOD7_(, , ct, m, F) +#define MOCK_METHOD8_WITH_CALLTYPE(ct, m, F) GMOCK_METHOD8_(, , ct, m, F) +#define MOCK_METHOD9_WITH_CALLTYPE(ct, m, F) GMOCK_METHOD9_(, , ct, m, F) +#define MOCK_METHOD10_WITH_CALLTYPE(ct, m, F) GMOCK_METHOD10_(, , ct, m, F) + +#define MOCK_CONST_METHOD0_WITH_CALLTYPE(ct, m, F) \ + GMOCK_METHOD0_(, const, ct, m, F) +#define MOCK_CONST_METHOD1_WITH_CALLTYPE(ct, m, F) \ + GMOCK_METHOD1_(, const, ct, m, F) +#define MOCK_CONST_METHOD2_WITH_CALLTYPE(ct, m, F) \ + GMOCK_METHOD2_(, const, ct, m, F) +#define MOCK_CONST_METHOD3_WITH_CALLTYPE(ct, m, F) \ + GMOCK_METHOD3_(, const, ct, m, F) +#define MOCK_CONST_METHOD4_WITH_CALLTYPE(ct, m, F) \ + GMOCK_METHOD4_(, const, ct, m, F) +#define MOCK_CONST_METHOD5_WITH_CALLTYPE(ct, m, F) \ + GMOCK_METHOD5_(, const, ct, m, F) +#define MOCK_CONST_METHOD6_WITH_CALLTYPE(ct, m, F) \ + GMOCK_METHOD6_(, const, ct, m, F) +#define MOCK_CONST_METHOD7_WITH_CALLTYPE(ct, m, F) \ + GMOCK_METHOD7_(, const, ct, m, F) +#define MOCK_CONST_METHOD8_WITH_CALLTYPE(ct, m, F) \ + GMOCK_METHOD8_(, const, ct, m, F) +#define MOCK_CONST_METHOD9_WITH_CALLTYPE(ct, m, F) \ + GMOCK_METHOD9_(, const, ct, m, F) +#define MOCK_CONST_METHOD10_WITH_CALLTYPE(ct, m, F) \ + GMOCK_METHOD10_(, const, ct, m, F) + +#define MOCK_METHOD0_T_WITH_CALLTYPE(ct, m, F) \ + GMOCK_METHOD0_(typename, , ct, m, F) +#define MOCK_METHOD1_T_WITH_CALLTYPE(ct, m, F) \ + GMOCK_METHOD1_(typename, , ct, m, F) +#define MOCK_METHOD2_T_WITH_CALLTYPE(ct, m, F) \ + GMOCK_METHOD2_(typename, , ct, m, F) +#define MOCK_METHOD3_T_WITH_CALLTYPE(ct, m, F) \ + GMOCK_METHOD3_(typename, , ct, m, F) +#define MOCK_METHOD4_T_WITH_CALLTYPE(ct, m, F) \ + GMOCK_METHOD4_(typename, , ct, m, F) +#define MOCK_METHOD5_T_WITH_CALLTYPE(ct, m, F) \ + GMOCK_METHOD5_(typename, , ct, m, F) +#define MOCK_METHOD6_T_WITH_CALLTYPE(ct, m, F) \ + GMOCK_METHOD6_(typename, , ct, m, F) +#define MOCK_METHOD7_T_WITH_CALLTYPE(ct, m, F) \ + GMOCK_METHOD7_(typename, , ct, m, F) +#define MOCK_METHOD8_T_WITH_CALLTYPE(ct, m, F) \ + GMOCK_METHOD8_(typename, , ct, m, F) +#define MOCK_METHOD9_T_WITH_CALLTYPE(ct, m, F) \ + GMOCK_METHOD9_(typename, , ct, m, F) +#define MOCK_METHOD10_T_WITH_CALLTYPE(ct, m, F) \ + GMOCK_METHOD10_(typename, , ct, m, F) + +#define MOCK_CONST_METHOD0_T_WITH_CALLTYPE(ct, m, F) \ + GMOCK_METHOD0_(typename, const, ct, m, F) +#define MOCK_CONST_METHOD1_T_WITH_CALLTYPE(ct, m, F) \ + GMOCK_METHOD1_(typename, const, ct, m, F) +#define MOCK_CONST_METHOD2_T_WITH_CALLTYPE(ct, m, F) \ + GMOCK_METHOD2_(typename, const, ct, m, F) +#define MOCK_CONST_METHOD3_T_WITH_CALLTYPE(ct, m, F) \ + GMOCK_METHOD3_(typename, const, ct, m, F) +#define MOCK_CONST_METHOD4_T_WITH_CALLTYPE(ct, m, F) \ + GMOCK_METHOD4_(typename, const, ct, m, F) +#define MOCK_CONST_METHOD5_T_WITH_CALLTYPE(ct, m, F) \ + GMOCK_METHOD5_(typename, const, ct, m, F) +#define MOCK_CONST_METHOD6_T_WITH_CALLTYPE(ct, m, F) \ + GMOCK_METHOD6_(typename, const, ct, m, F) +#define MOCK_CONST_METHOD7_T_WITH_CALLTYPE(ct, m, F) \ + GMOCK_METHOD7_(typename, const, ct, m, F) +#define MOCK_CONST_METHOD8_T_WITH_CALLTYPE(ct, m, F) \ + GMOCK_METHOD8_(typename, const, ct, m, F) +#define MOCK_CONST_METHOD9_T_WITH_CALLTYPE(ct, m, F) \ + GMOCK_METHOD9_(typename, const, ct, m, F) +#define MOCK_CONST_METHOD10_T_WITH_CALLTYPE(ct, m, F) \ + GMOCK_METHOD10_(typename, const, ct, m, F) + +// A MockFunction class has one mock method whose type is F. It is +// useful when you just want your test code to emit some messages and +// have Google Mock verify the right messages are sent (and perhaps at +// the right times). For example, if you are exercising code: +// +// Foo(1); +// Foo(2); +// Foo(3); +// +// and want to verify that Foo(1) and Foo(3) both invoke +// mock.Bar("a"), but Foo(2) doesn't invoke anything, you can write: +// +// TEST(FooTest, InvokesBarCorrectly) { +// MyMock mock; +// MockFunction check; +// { +// InSequence s; +// +// EXPECT_CALL(mock, Bar("a")); +// EXPECT_CALL(check, Call("1")); +// EXPECT_CALL(check, Call("2")); +// EXPECT_CALL(mock, Bar("a")); +// } +// Foo(1); +// check.Call("1"); +// Foo(2); +// check.Call("2"); +// Foo(3); +// } +// +// The expectation spec says that the first Bar("a") must happen +// before check point "1", the second Bar("a") must happen after check +// point "2", and nothing should happen between the two check +// points. The explicit check points make it easy to tell which +// Bar("a") is called by which call to Foo(). +template +class MockFunction; + +template +class MockFunction { + public: + MockFunction() {} + + MOCK_METHOD0_T(Call, R()); + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction); +}; + +template +class MockFunction { + public: + MockFunction() {} + + MOCK_METHOD1_T(Call, R(A0)); + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction); +}; + +template +class MockFunction { + public: + MockFunction() {} + + MOCK_METHOD2_T(Call, R(A0, A1)); + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction); +}; + +template +class MockFunction { + public: + MockFunction() {} + + MOCK_METHOD3_T(Call, R(A0, A1, A2)); + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction); +}; + +template +class MockFunction { + public: + MockFunction() {} + + MOCK_METHOD4_T(Call, R(A0, A1, A2, A3)); + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction); +}; + +template +class MockFunction { + public: + MockFunction() {} + + MOCK_METHOD5_T(Call, R(A0, A1, A2, A3, A4)); + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction); +}; + +template +class MockFunction { + public: + MockFunction() {} + + MOCK_METHOD6_T(Call, R(A0, A1, A2, A3, A4, A5)); + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction); +}; + +template +class MockFunction { + public: + MockFunction() {} + + MOCK_METHOD7_T(Call, R(A0, A1, A2, A3, A4, A5, A6)); + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction); +}; + +template +class MockFunction { + public: + MockFunction() {} + + MOCK_METHOD8_T(Call, R(A0, A1, A2, A3, A4, A5, A6, A7)); + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction); +}; + +template +class MockFunction { + public: + MockFunction() {} + + MOCK_METHOD9_T(Call, R(A0, A1, A2, A3, A4, A5, A6, A7, A8)); + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction); +}; + +template +class MockFunction { + public: + MockFunction() {} + + MOCK_METHOD10_T(Call, R(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9)); + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction); +}; + +} // namespace testing + +#endif // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_FUNCTION_MOCKERS_H_ diff --git a/libwvdrmengine/test/gmock/include/gmock/gmock-generated-function-mockers.h.pump b/libwvdrmengine/test/gmock/include/gmock/gmock-generated-function-mockers.h.pump new file mode 100644 index 00000000..4f82d622 --- /dev/null +++ b/libwvdrmengine/test/gmock/include/gmock/gmock-generated-function-mockers.h.pump @@ -0,0 +1,258 @@ +$$ -*- mode: c++; -*- +$$ This is a Pump source file. Please use Pump to convert it to +$$ gmock-generated-function-mockers.h. +$$ +$var n = 10 $$ The maximum arity we support. +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file implements function mockers of various arities. + +#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_FUNCTION_MOCKERS_H_ +#define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_FUNCTION_MOCKERS_H_ + +#include "gmock/gmock-spec-builders.h" +#include "gmock/internal/gmock-internal-utils.h" + +namespace testing { +namespace internal { + +template +class FunctionMockerBase; + +// Note: class FunctionMocker really belongs to the ::testing +// namespace. However if we define it in ::testing, MSVC will +// complain when classes in ::testing::internal declare it as a +// friend class template. To workaround this compiler bug, we define +// FunctionMocker in ::testing::internal and import it into ::testing. +template +class FunctionMocker; + + +$range i 0..n +$for i [[ +$range j 1..i +$var typename_As = [[$for j [[, typename A$j]]]] +$var As = [[$for j, [[A$j]]]] +$var as = [[$for j, [[a$j]]]] +$var Aas = [[$for j, [[A$j a$j]]]] +$var ms = [[$for j, [[m$j]]]] +$var matchers = [[$for j, [[const Matcher& m$j]]]] +template +class FunctionMocker : public + internal::FunctionMockerBase { + public: + typedef R F($As); + typedef typename internal::Function::ArgumentTuple ArgumentTuple; + + MockSpec& With($matchers) { + +$if i >= 1 [[ + this->current_spec().SetMatchers(::std::tr1::make_tuple($ms)); + +]] + return this->current_spec(); + } + + R Invoke($Aas) { + // Even though gcc and MSVC don't enforce it, 'this->' is required + // by the C++ standard [14.6.4] here, as the base class type is + // dependent on the template argument (and thus shouldn't be + // looked into when resolving InvokeWith). + return this->InvokeWith(ArgumentTuple($as)); + } +}; + + +]] +} // namespace internal + +// The style guide prohibits "using" statements in a namespace scope +// inside a header file. However, the FunctionMocker class template +// is meant to be defined in the ::testing namespace. The following +// line is just a trick for working around a bug in MSVC 8.0, which +// cannot handle it if we define FunctionMocker in ::testing. +using internal::FunctionMocker; + +// The result type of function type F. +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_RESULT_(tn, F) tn ::testing::internal::Function::Result + +// The type of argument N of function type F. +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_ARG_(tn, F, N) tn ::testing::internal::Function::Argument##N + +// The matcher type for argument N of function type F. +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_MATCHER_(tn, F, N) const ::testing::Matcher& + +// The variable for mocking the given method. +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_MOCKER_(arity, constness, Method) \ + GTEST_CONCAT_TOKEN_(gmock##constness##arity##_##Method##_, __LINE__) + + +$for i [[ +$range j 1..i +$var arg_as = [[$for j, \ + [[GMOCK_ARG_(tn, F, $j) gmock_a$j]]]] +$var as = [[$for j, [[gmock_a$j]]]] +$var matcher_as = [[$for j, \ + [[GMOCK_MATCHER_(tn, F, $j) gmock_a$j]]]] +// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!! +#define GMOCK_METHOD$i[[]]_(tn, constness, ct, Method, F) \ + GMOCK_RESULT_(tn, F) ct Method($arg_as) constness { \ + GTEST_COMPILE_ASSERT_(::std::tr1::tuple_size< \ + tn ::testing::internal::Function::ArgumentTuple>::value == $i, \ + this_method_does_not_take_$i[[]]_argument[[$if i != 1 [[s]]]]); \ + GMOCK_MOCKER_($i, constness, Method).SetOwnerAndName(this, #Method); \ + return GMOCK_MOCKER_($i, constness, Method).Invoke($as); \ + } \ + ::testing::MockSpec& \ + gmock_##Method($matcher_as) constness { \ + GMOCK_MOCKER_($i, constness, Method).RegisterOwner(this); \ + return GMOCK_MOCKER_($i, constness, Method).With($as); \ + } \ + mutable ::testing::FunctionMocker GMOCK_MOCKER_($i, constness, Method) + + +]] +$for i [[ +#define MOCK_METHOD$i(m, F) GMOCK_METHOD$i[[]]_(, , , m, F) + +]] + + +$for i [[ +#define MOCK_CONST_METHOD$i(m, F) GMOCK_METHOD$i[[]]_(, const, , m, F) + +]] + + +$for i [[ +#define MOCK_METHOD$i[[]]_T(m, F) GMOCK_METHOD$i[[]]_(typename, , , m, F) + +]] + + +$for i [[ +#define MOCK_CONST_METHOD$i[[]]_T(m, F) [[]] +GMOCK_METHOD$i[[]]_(typename, const, , m, F) + +]] + + +$for i [[ +#define MOCK_METHOD$i[[]]_WITH_CALLTYPE(ct, m, F) [[]] +GMOCK_METHOD$i[[]]_(, , ct, m, F) + +]] + + +$for i [[ +#define MOCK_CONST_METHOD$i[[]]_WITH_CALLTYPE(ct, m, F) \ + GMOCK_METHOD$i[[]]_(, const, ct, m, F) + +]] + + +$for i [[ +#define MOCK_METHOD$i[[]]_T_WITH_CALLTYPE(ct, m, F) \ + GMOCK_METHOD$i[[]]_(typename, , ct, m, F) + +]] + + +$for i [[ +#define MOCK_CONST_METHOD$i[[]]_T_WITH_CALLTYPE(ct, m, F) \ + GMOCK_METHOD$i[[]]_(typename, const, ct, m, F) + +]] + +// A MockFunction class has one mock method whose type is F. It is +// useful when you just want your test code to emit some messages and +// have Google Mock verify the right messages are sent (and perhaps at +// the right times). For example, if you are exercising code: +// +// Foo(1); +// Foo(2); +// Foo(3); +// +// and want to verify that Foo(1) and Foo(3) both invoke +// mock.Bar("a"), but Foo(2) doesn't invoke anything, you can write: +// +// TEST(FooTest, InvokesBarCorrectly) { +// MyMock mock; +// MockFunction check; +// { +// InSequence s; +// +// EXPECT_CALL(mock, Bar("a")); +// EXPECT_CALL(check, Call("1")); +// EXPECT_CALL(check, Call("2")); +// EXPECT_CALL(mock, Bar("a")); +// } +// Foo(1); +// check.Call("1"); +// Foo(2); +// check.Call("2"); +// Foo(3); +// } +// +// The expectation spec says that the first Bar("a") must happen +// before check point "1", the second Bar("a") must happen after check +// point "2", and nothing should happen between the two check +// points. The explicit check points make it easy to tell which +// Bar("a") is called by which call to Foo(). +template +class MockFunction; + + +$for i [[ +$range j 0..i-1 +template +class MockFunction { + public: + MockFunction() {} + + MOCK_METHOD$i[[]]_T(Call, R($for j, [[A$j]])); + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFunction); +}; + + +]] +} // namespace testing + +#endif // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_FUNCTION_MOCKERS_H_ diff --git a/libwvdrmengine/test/gmock/include/gmock/gmock-generated-matchers.h b/libwvdrmengine/test/gmock/include/gmock/gmock-generated-matchers.h new file mode 100644 index 00000000..6feaf1a2 --- /dev/null +++ b/libwvdrmengine/test/gmock/include/gmock/gmock-generated-matchers.h @@ -0,0 +1,2054 @@ +// This file was GENERATED by command: +// pump.py gmock-generated-matchers.h.pump +// DO NOT EDIT BY HAND!!! + +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Google Mock - a framework for writing C++ mock classes. +// +// This file implements some commonly used variadic matchers. + +#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_MATCHERS_H_ +#define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_MATCHERS_H_ + +#include +#include +#include +#include "gmock/gmock-matchers.h" + +namespace testing { +namespace internal { + +// The type of the i-th (0-based) field of Tuple. +#define GMOCK_FIELD_TYPE_(Tuple, i) \ + typename ::std::tr1::tuple_element::type + +// TupleFields is for selecting fields from a +// tuple of type Tuple. It has two members: +// +// type: a tuple type whose i-th field is the ki-th field of Tuple. +// GetSelectedFields(t): returns fields k0, ..., and kn of t as a tuple. +// +// For example, in class TupleFields, 2, 0>, we have: +// +// type is tuple, and +// GetSelectedFields(make_tuple(true, 'a', 42)) is (42, true). + +template +class TupleFields; + +// This generic version is used when there are 10 selectors. +template +class TupleFields { + public: + typedef ::std::tr1::tuple type; + static type GetSelectedFields(const Tuple& t) { + using ::std::tr1::get; + return type(get(t), get(t), get(t), get(t), get(t), + get(t), get(t), get(t), get(t), get(t)); + } +}; + +// The following specialization is used for 0 ~ 9 selectors. + +template +class TupleFields { + public: + typedef ::std::tr1::tuple<> type; + static type GetSelectedFields(const Tuple& /* t */) { + using ::std::tr1::get; + return type(); + } +}; + +template +class TupleFields { + public: + typedef ::std::tr1::tuple type; + static type GetSelectedFields(const Tuple& t) { + using ::std::tr1::get; + return type(get(t)); + } +}; + +template +class TupleFields { + public: + typedef ::std::tr1::tuple type; + static type GetSelectedFields(const Tuple& t) { + using ::std::tr1::get; + return type(get(t), get(t)); + } +}; + +template +class TupleFields { + public: + typedef ::std::tr1::tuple type; + static type GetSelectedFields(const Tuple& t) { + using ::std::tr1::get; + return type(get(t), get(t), get(t)); + } +}; + +template +class TupleFields { + public: + typedef ::std::tr1::tuple type; + static type GetSelectedFields(const Tuple& t) { + using ::std::tr1::get; + return type(get(t), get(t), get(t), get(t)); + } +}; + +template +class TupleFields { + public: + typedef ::std::tr1::tuple type; + static type GetSelectedFields(const Tuple& t) { + using ::std::tr1::get; + return type(get(t), get(t), get(t), get(t), get(t)); + } +}; + +template +class TupleFields { + public: + typedef ::std::tr1::tuple type; + static type GetSelectedFields(const Tuple& t) { + using ::std::tr1::get; + return type(get(t), get(t), get(t), get(t), get(t), + get(t)); + } +}; + +template +class TupleFields { + public: + typedef ::std::tr1::tuple type; + static type GetSelectedFields(const Tuple& t) { + using ::std::tr1::get; + return type(get(t), get(t), get(t), get(t), get(t), + get(t), get(t)); + } +}; + +template +class TupleFields { + public: + typedef ::std::tr1::tuple type; + static type GetSelectedFields(const Tuple& t) { + using ::std::tr1::get; + return type(get(t), get(t), get(t), get(t), get(t), + get(t), get(t), get(t)); + } +}; + +template +class TupleFields { + public: + typedef ::std::tr1::tuple type; + static type GetSelectedFields(const Tuple& t) { + using ::std::tr1::get; + return type(get(t), get(t), get(t), get(t), get(t), + get(t), get(t), get(t), get(t)); + } +}; + +#undef GMOCK_FIELD_TYPE_ + +// Implements the Args() matcher. +template +class ArgsMatcherImpl : public MatcherInterface { + public: + // ArgsTuple may have top-level const or reference modifiers. + typedef GTEST_REMOVE_REFERENCE_AND_CONST_(ArgsTuple) RawArgsTuple; + typedef typename internal::TupleFields::type SelectedArgs; + typedef Matcher MonomorphicInnerMatcher; + + template + explicit ArgsMatcherImpl(const InnerMatcher& inner_matcher) + : inner_matcher_(SafeMatcherCast(inner_matcher)) {} + + virtual bool MatchAndExplain(ArgsTuple args, + MatchResultListener* listener) const { + const SelectedArgs& selected_args = GetSelectedArgs(args); + if (!listener->IsInterested()) + return inner_matcher_.Matches(selected_args); + + PrintIndices(listener->stream()); + *listener << "are " << PrintToString(selected_args); + + StringMatchResultListener inner_listener; + const bool match = inner_matcher_.MatchAndExplain(selected_args, + &inner_listener); + PrintIfNotEmpty(inner_listener.str(), listener->stream()); + return match; + } + + virtual void DescribeTo(::std::ostream* os) const { + *os << "are a tuple "; + PrintIndices(os); + inner_matcher_.DescribeTo(os); + } + + virtual void DescribeNegationTo(::std::ostream* os) const { + *os << "are a tuple "; + PrintIndices(os); + inner_matcher_.DescribeNegationTo(os); + } + + private: + static SelectedArgs GetSelectedArgs(ArgsTuple args) { + return TupleFields::GetSelectedFields(args); + } + + // Prints the indices of the selected fields. + static void PrintIndices(::std::ostream* os) { + *os << "whose fields ("; + const int indices[10] = { k0, k1, k2, k3, k4, k5, k6, k7, k8, k9 }; + for (int i = 0; i < 10; i++) { + if (indices[i] < 0) + break; + + if (i >= 1) + *os << ", "; + + *os << "#" << indices[i]; + } + *os << ") "; + } + + const MonomorphicInnerMatcher inner_matcher_; + + GTEST_DISALLOW_ASSIGN_(ArgsMatcherImpl); +}; + +template +class ArgsMatcher { + public: + explicit ArgsMatcher(const InnerMatcher& inner_matcher) + : inner_matcher_(inner_matcher) {} + + template + operator Matcher() const { + return MakeMatcher(new ArgsMatcherImpl(inner_matcher_)); + } + + private: + const InnerMatcher inner_matcher_; + + GTEST_DISALLOW_ASSIGN_(ArgsMatcher); +}; + +// Implements ElementsAre() of 1-10 arguments. + +template +class ElementsAreMatcher1 { + public: + explicit ElementsAreMatcher1(const T1& e1) : e1_(e1) {} + + template + operator Matcher() const { + typedef GTEST_REMOVE_REFERENCE_AND_CONST_(Container) RawContainer; + typedef typename internal::StlContainerView::type::value_type + Element; + + // Nokia's Symbian Compiler has a nasty bug where the object put + // in a one-element local array is not destructed when the array + // goes out of scope. This leads to obvious badness as we've + // added the linked_ptr in it to our other linked_ptrs list. + // Hence we implement ElementsAreMatcher1 specially to avoid using + // a local array. + const Matcher matcher = + MatcherCast(e1_); + return MakeMatcher(new ElementsAreMatcherImpl(&matcher, 1)); + } + + private: + const T1& e1_; + + GTEST_DISALLOW_ASSIGN_(ElementsAreMatcher1); +}; + +template +class ElementsAreMatcher2 { + public: + ElementsAreMatcher2(const T1& e1, const T2& e2) : e1_(e1), e2_(e2) {} + + template + operator Matcher() const { + typedef GTEST_REMOVE_REFERENCE_AND_CONST_(Container) RawContainer; + typedef typename internal::StlContainerView::type::value_type + Element; + + const Matcher matchers[] = { + MatcherCast(e1_), + MatcherCast(e2_), + }; + + return MakeMatcher(new ElementsAreMatcherImpl(matchers, 2)); + } + + private: + const T1& e1_; + const T2& e2_; + + GTEST_DISALLOW_ASSIGN_(ElementsAreMatcher2); +}; + +template +class ElementsAreMatcher3 { + public: + ElementsAreMatcher3(const T1& e1, const T2& e2, const T3& e3) : e1_(e1), + e2_(e2), e3_(e3) {} + + template + operator Matcher() const { + typedef GTEST_REMOVE_REFERENCE_AND_CONST_(Container) RawContainer; + typedef typename internal::StlContainerView::type::value_type + Element; + + const Matcher matchers[] = { + MatcherCast(e1_), + MatcherCast(e2_), + MatcherCast(e3_), + }; + + return MakeMatcher(new ElementsAreMatcherImpl(matchers, 3)); + } + + private: + const T1& e1_; + const T2& e2_; + const T3& e3_; + + GTEST_DISALLOW_ASSIGN_(ElementsAreMatcher3); +}; + +template +class ElementsAreMatcher4 { + public: + ElementsAreMatcher4(const T1& e1, const T2& e2, const T3& e3, + const T4& e4) : e1_(e1), e2_(e2), e3_(e3), e4_(e4) {} + + template + operator Matcher() const { + typedef GTEST_REMOVE_REFERENCE_AND_CONST_(Container) RawContainer; + typedef typename internal::StlContainerView::type::value_type + Element; + + const Matcher matchers[] = { + MatcherCast(e1_), + MatcherCast(e2_), + MatcherCast(e3_), + MatcherCast(e4_), + }; + + return MakeMatcher(new ElementsAreMatcherImpl(matchers, 4)); + } + + private: + const T1& e1_; + const T2& e2_; + const T3& e3_; + const T4& e4_; + + GTEST_DISALLOW_ASSIGN_(ElementsAreMatcher4); +}; + +template +class ElementsAreMatcher5 { + public: + ElementsAreMatcher5(const T1& e1, const T2& e2, const T3& e3, const T4& e4, + const T5& e5) : e1_(e1), e2_(e2), e3_(e3), e4_(e4), e5_(e5) {} + + template + operator Matcher() const { + typedef GTEST_REMOVE_REFERENCE_AND_CONST_(Container) RawContainer; + typedef typename internal::StlContainerView::type::value_type + Element; + + const Matcher matchers[] = { + MatcherCast(e1_), + MatcherCast(e2_), + MatcherCast(e3_), + MatcherCast(e4_), + MatcherCast(e5_), + }; + + return MakeMatcher(new ElementsAreMatcherImpl(matchers, 5)); + } + + private: + const T1& e1_; + const T2& e2_; + const T3& e3_; + const T4& e4_; + const T5& e5_; + + GTEST_DISALLOW_ASSIGN_(ElementsAreMatcher5); +}; + +template +class ElementsAreMatcher6 { + public: + ElementsAreMatcher6(const T1& e1, const T2& e2, const T3& e3, const T4& e4, + const T5& e5, const T6& e6) : e1_(e1), e2_(e2), e3_(e3), e4_(e4), + e5_(e5), e6_(e6) {} + + template + operator Matcher() const { + typedef GTEST_REMOVE_REFERENCE_AND_CONST_(Container) RawContainer; + typedef typename internal::StlContainerView::type::value_type + Element; + + const Matcher matchers[] = { + MatcherCast(e1_), + MatcherCast(e2_), + MatcherCast(e3_), + MatcherCast(e4_), + MatcherCast(e5_), + MatcherCast(e6_), + }; + + return MakeMatcher(new ElementsAreMatcherImpl(matchers, 6)); + } + + private: + const T1& e1_; + const T2& e2_; + const T3& e3_; + const T4& e4_; + const T5& e5_; + const T6& e6_; + + GTEST_DISALLOW_ASSIGN_(ElementsAreMatcher6); +}; + +template +class ElementsAreMatcher7 { + public: + ElementsAreMatcher7(const T1& e1, const T2& e2, const T3& e3, const T4& e4, + const T5& e5, const T6& e6, const T7& e7) : e1_(e1), e2_(e2), e3_(e3), + e4_(e4), e5_(e5), e6_(e6), e7_(e7) {} + + template + operator Matcher() const { + typedef GTEST_REMOVE_REFERENCE_AND_CONST_(Container) RawContainer; + typedef typename internal::StlContainerView::type::value_type + Element; + + const Matcher matchers[] = { + MatcherCast(e1_), + MatcherCast(e2_), + MatcherCast(e3_), + MatcherCast(e4_), + MatcherCast(e5_), + MatcherCast(e6_), + MatcherCast(e7_), + }; + + return MakeMatcher(new ElementsAreMatcherImpl(matchers, 7)); + } + + private: + const T1& e1_; + const T2& e2_; + const T3& e3_; + const T4& e4_; + const T5& e5_; + const T6& e6_; + const T7& e7_; + + GTEST_DISALLOW_ASSIGN_(ElementsAreMatcher7); +}; + +template +class ElementsAreMatcher8 { + public: + ElementsAreMatcher8(const T1& e1, const T2& e2, const T3& e3, const T4& e4, + const T5& e5, const T6& e6, const T7& e7, const T8& e8) : e1_(e1), + e2_(e2), e3_(e3), e4_(e4), e5_(e5), e6_(e6), e7_(e7), e8_(e8) {} + + template + operator Matcher() const { + typedef GTEST_REMOVE_REFERENCE_AND_CONST_(Container) RawContainer; + typedef typename internal::StlContainerView::type::value_type + Element; + + const Matcher matchers[] = { + MatcherCast(e1_), + MatcherCast(e2_), + MatcherCast(e3_), + MatcherCast(e4_), + MatcherCast(e5_), + MatcherCast(e6_), + MatcherCast(e7_), + MatcherCast(e8_), + }; + + return MakeMatcher(new ElementsAreMatcherImpl(matchers, 8)); + } + + private: + const T1& e1_; + const T2& e2_; + const T3& e3_; + const T4& e4_; + const T5& e5_; + const T6& e6_; + const T7& e7_; + const T8& e8_; + + GTEST_DISALLOW_ASSIGN_(ElementsAreMatcher8); +}; + +template +class ElementsAreMatcher9 { + public: + ElementsAreMatcher9(const T1& e1, const T2& e2, const T3& e3, const T4& e4, + const T5& e5, const T6& e6, const T7& e7, const T8& e8, + const T9& e9) : e1_(e1), e2_(e2), e3_(e3), e4_(e4), e5_(e5), e6_(e6), + e7_(e7), e8_(e8), e9_(e9) {} + + template + operator Matcher() const { + typedef GTEST_REMOVE_REFERENCE_AND_CONST_(Container) RawContainer; + typedef typename internal::StlContainerView::type::value_type + Element; + + const Matcher matchers[] = { + MatcherCast(e1_), + MatcherCast(e2_), + MatcherCast(e3_), + MatcherCast(e4_), + MatcherCast(e5_), + MatcherCast(e6_), + MatcherCast(e7_), + MatcherCast(e8_), + MatcherCast(e9_), + }; + + return MakeMatcher(new ElementsAreMatcherImpl(matchers, 9)); + } + + private: + const T1& e1_; + const T2& e2_; + const T3& e3_; + const T4& e4_; + const T5& e5_; + const T6& e6_; + const T7& e7_; + const T8& e8_; + const T9& e9_; + + GTEST_DISALLOW_ASSIGN_(ElementsAreMatcher9); +}; + +template +class ElementsAreMatcher10 { + public: + ElementsAreMatcher10(const T1& e1, const T2& e2, const T3& e3, const T4& e4, + const T5& e5, const T6& e6, const T7& e7, const T8& e8, const T9& e9, + const T10& e10) : e1_(e1), e2_(e2), e3_(e3), e4_(e4), e5_(e5), e6_(e6), + e7_(e7), e8_(e8), e9_(e9), e10_(e10) {} + + template + operator Matcher() const { + typedef GTEST_REMOVE_REFERENCE_AND_CONST_(Container) RawContainer; + typedef typename internal::StlContainerView::type::value_type + Element; + + const Matcher matchers[] = { + MatcherCast(e1_), + MatcherCast(e2_), + MatcherCast(e3_), + MatcherCast(e4_), + MatcherCast(e5_), + MatcherCast(e6_), + MatcherCast(e7_), + MatcherCast(e8_), + MatcherCast(e9_), + MatcherCast(e10_), + }; + + return MakeMatcher(new ElementsAreMatcherImpl(matchers, 10)); + } + + private: + const T1& e1_; + const T2& e2_; + const T3& e3_; + const T4& e4_; + const T5& e5_; + const T6& e6_; + const T7& e7_; + const T8& e8_; + const T9& e9_; + const T10& e10_; + + GTEST_DISALLOW_ASSIGN_(ElementsAreMatcher10); +}; + +} // namespace internal + +// Args(a_matcher) matches a tuple if the selected +// fields of it matches a_matcher. C++ doesn't support default +// arguments for function templates, so we have to overload it. +template +inline internal::ArgsMatcher +Args(const InnerMatcher& matcher) { + return internal::ArgsMatcher(matcher); +} + +template +inline internal::ArgsMatcher +Args(const InnerMatcher& matcher) { + return internal::ArgsMatcher(matcher); +} + +template +inline internal::ArgsMatcher +Args(const InnerMatcher& matcher) { + return internal::ArgsMatcher(matcher); +} + +template +inline internal::ArgsMatcher +Args(const InnerMatcher& matcher) { + return internal::ArgsMatcher(matcher); +} + +template +inline internal::ArgsMatcher +Args(const InnerMatcher& matcher) { + return internal::ArgsMatcher(matcher); +} + +template +inline internal::ArgsMatcher +Args(const InnerMatcher& matcher) { + return internal::ArgsMatcher(matcher); +} + +template +inline internal::ArgsMatcher +Args(const InnerMatcher& matcher) { + return internal::ArgsMatcher(matcher); +} + +template +inline internal::ArgsMatcher +Args(const InnerMatcher& matcher) { + return internal::ArgsMatcher(matcher); +} + +template +inline internal::ArgsMatcher +Args(const InnerMatcher& matcher) { + return internal::ArgsMatcher(matcher); +} + +template +inline internal::ArgsMatcher +Args(const InnerMatcher& matcher) { + return internal::ArgsMatcher(matcher); +} + +template +inline internal::ArgsMatcher +Args(const InnerMatcher& matcher) { + return internal::ArgsMatcher(matcher); +} + +// ElementsAre(e0, e1, ..., e_n) matches an STL-style container with +// (n + 1) elements, where the i-th element in the container must +// match the i-th argument in the list. Each argument of +// ElementsAre() can be either a value or a matcher. We support up to +// 10 arguments. +// +// NOTE: Since ElementsAre() cares about the order of the elements, it +// must not be used with containers whose elements's order is +// undefined (e.g. hash_map). + +inline internal::ElementsAreMatcher0 ElementsAre() { + return internal::ElementsAreMatcher0(); +} + +template +inline internal::ElementsAreMatcher1 ElementsAre(const T1& e1) { + return internal::ElementsAreMatcher1(e1); +} + +template +inline internal::ElementsAreMatcher2 ElementsAre(const T1& e1, + const T2& e2) { + return internal::ElementsAreMatcher2(e1, e2); +} + +template +inline internal::ElementsAreMatcher3 ElementsAre(const T1& e1, + const T2& e2, const T3& e3) { + return internal::ElementsAreMatcher3(e1, e2, e3); +} + +template +inline internal::ElementsAreMatcher4 ElementsAre(const T1& e1, + const T2& e2, const T3& e3, const T4& e4) { + return internal::ElementsAreMatcher4(e1, e2, e3, e4); +} + +template +inline internal::ElementsAreMatcher5 ElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4, + const T5& e5) { + return internal::ElementsAreMatcher5(e1, e2, e3, e4, e5); +} + +template +inline internal::ElementsAreMatcher6 ElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4, + const T5& e5, const T6& e6) { + return internal::ElementsAreMatcher6(e1, e2, e3, e4, + e5, e6); +} + +template +inline internal::ElementsAreMatcher7 ElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4, + const T5& e5, const T6& e6, const T7& e7) { + return internal::ElementsAreMatcher7(e1, e2, e3, + e4, e5, e6, e7); +} + +template +inline internal::ElementsAreMatcher8 ElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4, + const T5& e5, const T6& e6, const T7& e7, const T8& e8) { + return internal::ElementsAreMatcher8(e1, e2, + e3, e4, e5, e6, e7, e8); +} + +template +inline internal::ElementsAreMatcher9 ElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4, + const T5& e5, const T6& e6, const T7& e7, const T8& e8, const T9& e9) { + return internal::ElementsAreMatcher9(e1, + e2, e3, e4, e5, e6, e7, e8, e9); +} + +template +inline internal::ElementsAreMatcher10 ElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4, + const T5& e5, const T6& e6, const T7& e7, const T8& e8, const T9& e9, + const T10& e10) { + return internal::ElementsAreMatcher10(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10); +} + +// ElementsAreArray(array) and ElementAreArray(array, count) are like +// ElementsAre(), except that they take an array of values or +// matchers. The former form infers the size of 'array', which must +// be a static C-style array. In the latter form, 'array' can either +// be a static array or a pointer to a dynamically created array. + +template +inline internal::ElementsAreArrayMatcher ElementsAreArray( + const T* first, size_t count) { + return internal::ElementsAreArrayMatcher(first, count); +} + +template +inline internal::ElementsAreArrayMatcher +ElementsAreArray(const T (&array)[N]) { + return internal::ElementsAreArrayMatcher(array, N); +} + +// AllOf(m1, m2, ..., mk) matches any value that matches all of the given +// sub-matchers. AllOf is called fully qualified to prevent ADL from firing. + +template +inline internal::BothOfMatcher +AllOf(Matcher1 m1, Matcher2 m2) { + return internal::BothOfMatcher(m1, m2); +} + +template +inline internal::BothOfMatcher > +AllOf(Matcher1 m1, Matcher2 m2, Matcher3 m3) { + return ::testing::AllOf(m1, ::testing::AllOf(m2, m3)); +} + +template +inline internal::BothOfMatcher > > +AllOf(Matcher1 m1, Matcher2 m2, Matcher3 m3, Matcher4 m4) { + return ::testing::AllOf(m1, ::testing::AllOf(m2, m3, m4)); +} + +template +inline internal::BothOfMatcher > > > +AllOf(Matcher1 m1, Matcher2 m2, Matcher3 m3, Matcher4 m4, Matcher5 m5) { + return ::testing::AllOf(m1, ::testing::AllOf(m2, m3, m4, m5)); +} + +template +inline internal::BothOfMatcher > > > > +AllOf(Matcher1 m1, Matcher2 m2, Matcher3 m3, Matcher4 m4, Matcher5 m5, + Matcher6 m6) { + return ::testing::AllOf(m1, ::testing::AllOf(m2, m3, m4, m5, m6)); +} + +template +inline internal::BothOfMatcher > > > > > +AllOf(Matcher1 m1, Matcher2 m2, Matcher3 m3, Matcher4 m4, Matcher5 m5, + Matcher6 m6, Matcher7 m7) { + return ::testing::AllOf(m1, ::testing::AllOf(m2, m3, m4, m5, m6, m7)); +} + +template +inline internal::BothOfMatcher > > > > > > +AllOf(Matcher1 m1, Matcher2 m2, Matcher3 m3, Matcher4 m4, Matcher5 m5, + Matcher6 m6, Matcher7 m7, Matcher8 m8) { + return ::testing::AllOf(m1, ::testing::AllOf(m2, m3, m4, m5, m6, m7, m8)); +} + +template +inline internal::BothOfMatcher > > > > > > > +AllOf(Matcher1 m1, Matcher2 m2, Matcher3 m3, Matcher4 m4, Matcher5 m5, + Matcher6 m6, Matcher7 m7, Matcher8 m8, Matcher9 m9) { + return ::testing::AllOf(m1, ::testing::AllOf(m2, m3, m4, m5, m6, m7, m8, m9)); +} + +template +inline internal::BothOfMatcher > > > > > > > > +AllOf(Matcher1 m1, Matcher2 m2, Matcher3 m3, Matcher4 m4, Matcher5 m5, + Matcher6 m6, Matcher7 m7, Matcher8 m8, Matcher9 m9, Matcher10 m10) { + return ::testing::AllOf(m1, ::testing::AllOf(m2, m3, m4, m5, m6, m7, m8, m9, + m10)); +} + +// AnyOf(m1, m2, ..., mk) matches any value that matches any of the given +// sub-matchers. AnyOf is called fully qualified to prevent ADL from firing. + +template +inline internal::EitherOfMatcher +AnyOf(Matcher1 m1, Matcher2 m2) { + return internal::EitherOfMatcher(m1, m2); +} + +template +inline internal::EitherOfMatcher > +AnyOf(Matcher1 m1, Matcher2 m2, Matcher3 m3) { + return ::testing::AnyOf(m1, ::testing::AnyOf(m2, m3)); +} + +template +inline internal::EitherOfMatcher > > +AnyOf(Matcher1 m1, Matcher2 m2, Matcher3 m3, Matcher4 m4) { + return ::testing::AnyOf(m1, ::testing::AnyOf(m2, m3, m4)); +} + +template +inline internal::EitherOfMatcher > > > +AnyOf(Matcher1 m1, Matcher2 m2, Matcher3 m3, Matcher4 m4, Matcher5 m5) { + return ::testing::AnyOf(m1, ::testing::AnyOf(m2, m3, m4, m5)); +} + +template +inline internal::EitherOfMatcher > > > > +AnyOf(Matcher1 m1, Matcher2 m2, Matcher3 m3, Matcher4 m4, Matcher5 m5, + Matcher6 m6) { + return ::testing::AnyOf(m1, ::testing::AnyOf(m2, m3, m4, m5, m6)); +} + +template +inline internal::EitherOfMatcher > > > > > +AnyOf(Matcher1 m1, Matcher2 m2, Matcher3 m3, Matcher4 m4, Matcher5 m5, + Matcher6 m6, Matcher7 m7) { + return ::testing::AnyOf(m1, ::testing::AnyOf(m2, m3, m4, m5, m6, m7)); +} + +template +inline internal::EitherOfMatcher > > > > > > +AnyOf(Matcher1 m1, Matcher2 m2, Matcher3 m3, Matcher4 m4, Matcher5 m5, + Matcher6 m6, Matcher7 m7, Matcher8 m8) { + return ::testing::AnyOf(m1, ::testing::AnyOf(m2, m3, m4, m5, m6, m7, m8)); +} + +template +inline internal::EitherOfMatcher > > > > > > > +AnyOf(Matcher1 m1, Matcher2 m2, Matcher3 m3, Matcher4 m4, Matcher5 m5, + Matcher6 m6, Matcher7 m7, Matcher8 m8, Matcher9 m9) { + return ::testing::AnyOf(m1, ::testing::AnyOf(m2, m3, m4, m5, m6, m7, m8, m9)); +} + +template +inline internal::EitherOfMatcher > > > > > > > > +AnyOf(Matcher1 m1, Matcher2 m2, Matcher3 m3, Matcher4 m4, Matcher5 m5, + Matcher6 m6, Matcher7 m7, Matcher8 m8, Matcher9 m9, Matcher10 m10) { + return ::testing::AnyOf(m1, ::testing::AnyOf(m2, m3, m4, m5, m6, m7, m8, m9, + m10)); +} + +} // namespace testing + + +// The MATCHER* family of macros can be used in a namespace scope to +// define custom matchers easily. +// +// Basic Usage +// =========== +// +// The syntax +// +// MATCHER(name, description_string) { statements; } +// +// defines a matcher with the given name that executes the statements, +// which must return a bool to indicate if the match succeeds. Inside +// the statements, you can refer to the value being matched by 'arg', +// and refer to its type by 'arg_type'. +// +// The description string documents what the matcher does, and is used +// to generate the failure message when the match fails. Since a +// MATCHER() is usually defined in a header file shared by multiple +// C++ source files, we require the description to be a C-string +// literal to avoid possible side effects. It can be empty, in which +// case we'll use the sequence of words in the matcher name as the +// description. +// +// For example: +// +// MATCHER(IsEven, "") { return (arg % 2) == 0; } +// +// allows you to write +// +// // Expects mock_foo.Bar(n) to be called where n is even. +// EXPECT_CALL(mock_foo, Bar(IsEven())); +// +// or, +// +// // Verifies that the value of some_expression is even. +// EXPECT_THAT(some_expression, IsEven()); +// +// If the above assertion fails, it will print something like: +// +// Value of: some_expression +// Expected: is even +// Actual: 7 +// +// where the description "is even" is automatically calculated from the +// matcher name IsEven. +// +// Argument Type +// ============= +// +// Note that the type of the value being matched (arg_type) is +// determined by the context in which you use the matcher and is +// supplied to you by the compiler, so you don't need to worry about +// declaring it (nor can you). This allows the matcher to be +// polymorphic. For example, IsEven() can be used to match any type +// where the value of "(arg % 2) == 0" can be implicitly converted to +// a bool. In the "Bar(IsEven())" example above, if method Bar() +// takes an int, 'arg_type' will be int; if it takes an unsigned long, +// 'arg_type' will be unsigned long; and so on. +// +// Parameterizing Matchers +// ======================= +// +// Sometimes you'll want to parameterize the matcher. For that you +// can use another macro: +// +// MATCHER_P(name, param_name, description_string) { statements; } +// +// For example: +// +// MATCHER_P(HasAbsoluteValue, value, "") { return abs(arg) == value; } +// +// will allow you to write: +// +// EXPECT_THAT(Blah("a"), HasAbsoluteValue(n)); +// +// which may lead to this message (assuming n is 10): +// +// Value of: Blah("a") +// Expected: has absolute value 10 +// Actual: -9 +// +// Note that both the matcher description and its parameter are +// printed, making the message human-friendly. +// +// In the matcher definition body, you can write 'foo_type' to +// reference the type of a parameter named 'foo'. For example, in the +// body of MATCHER_P(HasAbsoluteValue, value) above, you can write +// 'value_type' to refer to the type of 'value'. +// +// We also provide MATCHER_P2, MATCHER_P3, ..., up to MATCHER_P10 to +// support multi-parameter matchers. +// +// Describing Parameterized Matchers +// ================================= +// +// The last argument to MATCHER*() is a string-typed expression. The +// expression can reference all of the matcher's parameters and a +// special bool-typed variable named 'negation'. When 'negation' is +// false, the expression should evaluate to the matcher's description; +// otherwise it should evaluate to the description of the negation of +// the matcher. For example, +// +// using testing::PrintToString; +// +// MATCHER_P2(InClosedRange, low, hi, +// string(negation ? "is not" : "is") + " in range [" + +// PrintToString(low) + ", " + PrintToString(hi) + "]") { +// return low <= arg && arg <= hi; +// } +// ... +// EXPECT_THAT(3, InClosedRange(4, 6)); +// EXPECT_THAT(3, Not(InClosedRange(2, 4))); +// +// would generate two failures that contain the text: +// +// Expected: is in range [4, 6] +// ... +// Expected: is not in range [2, 4] +// +// If you specify "" as the description, the failure message will +// contain the sequence of words in the matcher name followed by the +// parameter values printed as a tuple. For example, +// +// MATCHER_P2(InClosedRange, low, hi, "") { ... } +// ... +// EXPECT_THAT(3, InClosedRange(4, 6)); +// EXPECT_THAT(3, Not(InClosedRange(2, 4))); +// +// would generate two failures that contain the text: +// +// Expected: in closed range (4, 6) +// ... +// Expected: not (in closed range (2, 4)) +// +// Types of Matcher Parameters +// =========================== +// +// For the purpose of typing, you can view +// +// MATCHER_Pk(Foo, p1, ..., pk, description_string) { ... } +// +// as shorthand for +// +// template +// FooMatcherPk +// Foo(p1_type p1, ..., pk_type pk) { ... } +// +// When you write Foo(v1, ..., vk), the compiler infers the types of +// the parameters v1, ..., and vk for you. If you are not happy with +// the result of the type inference, you can specify the types by +// explicitly instantiating the template, as in Foo(5, +// false). As said earlier, you don't get to (or need to) specify +// 'arg_type' as that's determined by the context in which the matcher +// is used. You can assign the result of expression Foo(p1, ..., pk) +// to a variable of type FooMatcherPk. This +// can be useful when composing matchers. +// +// While you can instantiate a matcher template with reference types, +// passing the parameters by pointer usually makes your code more +// readable. If, however, you still want to pass a parameter by +// reference, be aware that in the failure message generated by the +// matcher you will see the value of the referenced object but not its +// address. +// +// Explaining Match Results +// ======================== +// +// Sometimes the matcher description alone isn't enough to explain why +// the match has failed or succeeded. For example, when expecting a +// long string, it can be very helpful to also print the diff between +// the expected string and the actual one. To achieve that, you can +// optionally stream additional information to a special variable +// named result_listener, whose type is a pointer to class +// MatchResultListener: +// +// MATCHER_P(EqualsLongString, str, "") { +// if (arg == str) return true; +// +// *result_listener << "the difference: " +/// << DiffStrings(str, arg); +// return false; +// } +// +// Overloading Matchers +// ==================== +// +// You can overload matchers with different numbers of parameters: +// +// MATCHER_P(Blah, a, description_string1) { ... } +// MATCHER_P2(Blah, a, b, description_string2) { ... } +// +// Caveats +// ======= +// +// When defining a new matcher, you should also consider implementing +// MatcherInterface or using MakePolymorphicMatcher(). These +// approaches require more work than the MATCHER* macros, but also +// give you more control on the types of the value being matched and +// the matcher parameters, which may leads to better compiler error +// messages when the matcher is used wrong. They also allow +// overloading matchers based on parameter types (as opposed to just +// based on the number of parameters). +// +// MATCHER*() can only be used in a namespace scope. The reason is +// that C++ doesn't yet allow function-local types to be used to +// instantiate templates. The up-coming C++0x standard will fix this. +// Once that's done, we'll consider supporting using MATCHER*() inside +// a function. +// +// More Information +// ================ +// +// To learn more about using these macros, please search for 'MATCHER' +// on http://code.google.com/p/googlemock/wiki/CookBook. + +#define MATCHER(name, description)\ + class name##Matcher {\ + public:\ + template \ + class gmock_Impl : public ::testing::MatcherInterface {\ + public:\ + gmock_Impl()\ + {}\ + virtual bool MatchAndExplain(\ + arg_type arg, ::testing::MatchResultListener* result_listener) const;\ + virtual void DescribeTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(false);\ + }\ + virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(true);\ + }\ + private:\ + ::testing::internal::string FormatDescription(bool negation) const {\ + const ::testing::internal::string gmock_description = (description);\ + if (!gmock_description.empty())\ + return gmock_description;\ + return ::testing::internal::FormatMatcherDescription(\ + negation, #name,\ + ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ + ::std::tr1::tuple<>()));\ + }\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template \ + operator ::testing::Matcher() const {\ + return ::testing::Matcher(\ + new gmock_Impl());\ + }\ + name##Matcher() {\ + }\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##Matcher);\ + };\ + inline name##Matcher name() {\ + return name##Matcher();\ + }\ + template \ + bool name##Matcher::gmock_Impl::MatchAndExplain(\ + arg_type arg,\ + ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ + const + +#define MATCHER_P(name, p0, description)\ + template \ + class name##MatcherP {\ + public:\ + template \ + class gmock_Impl : public ::testing::MatcherInterface {\ + public:\ + explicit gmock_Impl(p0##_type gmock_p0)\ + : p0(gmock_p0) {}\ + virtual bool MatchAndExplain(\ + arg_type arg, ::testing::MatchResultListener* result_listener) const;\ + virtual void DescribeTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(false);\ + }\ + virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(true);\ + }\ + p0##_type p0;\ + private:\ + ::testing::internal::string FormatDescription(bool negation) const {\ + const ::testing::internal::string gmock_description = (description);\ + if (!gmock_description.empty())\ + return gmock_description;\ + return ::testing::internal::FormatMatcherDescription(\ + negation, #name,\ + ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ + ::std::tr1::tuple(p0)));\ + }\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template \ + operator ::testing::Matcher() const {\ + return ::testing::Matcher(\ + new gmock_Impl(p0));\ + }\ + name##MatcherP(p0##_type gmock_p0) : p0(gmock_p0) {\ + }\ + p0##_type p0;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##MatcherP);\ + };\ + template \ + inline name##MatcherP name(p0##_type p0) {\ + return name##MatcherP(p0);\ + }\ + template \ + template \ + bool name##MatcherP::gmock_Impl::MatchAndExplain(\ + arg_type arg,\ + ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ + const + +#define MATCHER_P2(name, p0, p1, description)\ + template \ + class name##MatcherP2 {\ + public:\ + template \ + class gmock_Impl : public ::testing::MatcherInterface {\ + public:\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1)\ + : p0(gmock_p0), p1(gmock_p1) {}\ + virtual bool MatchAndExplain(\ + arg_type arg, ::testing::MatchResultListener* result_listener) const;\ + virtual void DescribeTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(false);\ + }\ + virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(true);\ + }\ + p0##_type p0;\ + p1##_type p1;\ + private:\ + ::testing::internal::string FormatDescription(bool negation) const {\ + const ::testing::internal::string gmock_description = (description);\ + if (!gmock_description.empty())\ + return gmock_description;\ + return ::testing::internal::FormatMatcherDescription(\ + negation, #name,\ + ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ + ::std::tr1::tuple(p0, p1)));\ + }\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template \ + operator ::testing::Matcher() const {\ + return ::testing::Matcher(\ + new gmock_Impl(p0, p1));\ + }\ + name##MatcherP2(p0##_type gmock_p0, p1##_type gmock_p1) : p0(gmock_p0), \ + p1(gmock_p1) {\ + }\ + p0##_type p0;\ + p1##_type p1;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##MatcherP2);\ + };\ + template \ + inline name##MatcherP2 name(p0##_type p0, \ + p1##_type p1) {\ + return name##MatcherP2(p0, p1);\ + }\ + template \ + template \ + bool name##MatcherP2::gmock_Impl::MatchAndExplain(\ + arg_type arg,\ + ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ + const + +#define MATCHER_P3(name, p0, p1, p2, description)\ + template \ + class name##MatcherP3 {\ + public:\ + template \ + class gmock_Impl : public ::testing::MatcherInterface {\ + public:\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2)\ + : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2) {}\ + virtual bool MatchAndExplain(\ + arg_type arg, ::testing::MatchResultListener* result_listener) const;\ + virtual void DescribeTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(false);\ + }\ + virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(true);\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + private:\ + ::testing::internal::string FormatDescription(bool negation) const {\ + const ::testing::internal::string gmock_description = (description);\ + if (!gmock_description.empty())\ + return gmock_description;\ + return ::testing::internal::FormatMatcherDescription(\ + negation, #name,\ + ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ + ::std::tr1::tuple(p0, p1, \ + p2)));\ + }\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template \ + operator ::testing::Matcher() const {\ + return ::testing::Matcher(\ + new gmock_Impl(p0, p1, p2));\ + }\ + name##MatcherP3(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2) {\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##MatcherP3);\ + };\ + template \ + inline name##MatcherP3 name(p0##_type p0, \ + p1##_type p1, p2##_type p2) {\ + return name##MatcherP3(p0, p1, p2);\ + }\ + template \ + template \ + bool name##MatcherP3::gmock_Impl::MatchAndExplain(\ + arg_type arg,\ + ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ + const + +#define MATCHER_P4(name, p0, p1, p2, p3, description)\ + template \ + class name##MatcherP4 {\ + public:\ + template \ + class gmock_Impl : public ::testing::MatcherInterface {\ + public:\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3)\ + : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), p3(gmock_p3) {}\ + virtual bool MatchAndExplain(\ + arg_type arg, ::testing::MatchResultListener* result_listener) const;\ + virtual void DescribeTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(false);\ + }\ + virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(true);\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + private:\ + ::testing::internal::string FormatDescription(bool negation) const {\ + const ::testing::internal::string gmock_description = (description);\ + if (!gmock_description.empty())\ + return gmock_description;\ + return ::testing::internal::FormatMatcherDescription(\ + negation, #name,\ + ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ + ::std::tr1::tuple(p0, p1, p2, p3)));\ + }\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template \ + operator ::testing::Matcher() const {\ + return ::testing::Matcher(\ + new gmock_Impl(p0, p1, p2, p3));\ + }\ + name##MatcherP4(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3) : p0(gmock_p0), p1(gmock_p1), \ + p2(gmock_p2), p3(gmock_p3) {\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##MatcherP4);\ + };\ + template \ + inline name##MatcherP4 name(p0##_type p0, p1##_type p1, p2##_type p2, \ + p3##_type p3) {\ + return name##MatcherP4(p0, \ + p1, p2, p3);\ + }\ + template \ + template \ + bool name##MatcherP4::gmock_Impl::MatchAndExplain(\ + arg_type arg,\ + ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ + const + +#define MATCHER_P5(name, p0, p1, p2, p3, p4, description)\ + template \ + class name##MatcherP5 {\ + public:\ + template \ + class gmock_Impl : public ::testing::MatcherInterface {\ + public:\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4)\ + : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), \ + p4(gmock_p4) {}\ + virtual bool MatchAndExplain(\ + arg_type arg, ::testing::MatchResultListener* result_listener) const;\ + virtual void DescribeTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(false);\ + }\ + virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(true);\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + private:\ + ::testing::internal::string FormatDescription(bool negation) const {\ + const ::testing::internal::string gmock_description = (description);\ + if (!gmock_description.empty())\ + return gmock_description;\ + return ::testing::internal::FormatMatcherDescription(\ + negation, #name,\ + ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ + ::std::tr1::tuple(p0, p1, p2, p3, p4)));\ + }\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template \ + operator ::testing::Matcher() const {\ + return ::testing::Matcher(\ + new gmock_Impl(p0, p1, p2, p3, p4));\ + }\ + name##MatcherP5(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3, \ + p4##_type gmock_p4) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3), p4(gmock_p4) {\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##MatcherP5);\ + };\ + template \ + inline name##MatcherP5 name(p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \ + p4##_type p4) {\ + return name##MatcherP5(p0, p1, p2, p3, p4);\ + }\ + template \ + template \ + bool name##MatcherP5::gmock_Impl::MatchAndExplain(\ + arg_type arg,\ + ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ + const + +#define MATCHER_P6(name, p0, p1, p2, p3, p4, p5, description)\ + template \ + class name##MatcherP6 {\ + public:\ + template \ + class gmock_Impl : public ::testing::MatcherInterface {\ + public:\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5)\ + : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), \ + p4(gmock_p4), p5(gmock_p5) {}\ + virtual bool MatchAndExplain(\ + arg_type arg, ::testing::MatchResultListener* result_listener) const;\ + virtual void DescribeTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(false);\ + }\ + virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(true);\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + private:\ + ::testing::internal::string FormatDescription(bool negation) const {\ + const ::testing::internal::string gmock_description = (description);\ + if (!gmock_description.empty())\ + return gmock_description;\ + return ::testing::internal::FormatMatcherDescription(\ + negation, #name,\ + ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ + ::std::tr1::tuple(p0, p1, p2, p3, p4, p5)));\ + }\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template \ + operator ::testing::Matcher() const {\ + return ::testing::Matcher(\ + new gmock_Impl(p0, p1, p2, p3, p4, p5));\ + }\ + name##MatcherP6(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \ + p5##_type gmock_p5) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3), p4(gmock_p4), p5(gmock_p5) {\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##MatcherP6);\ + };\ + template \ + inline name##MatcherP6 name(p0##_type p0, p1##_type p1, p2##_type p2, \ + p3##_type p3, p4##_type p4, p5##_type p5) {\ + return name##MatcherP6(p0, p1, p2, p3, p4, p5);\ + }\ + template \ + template \ + bool name##MatcherP6::gmock_Impl::MatchAndExplain(\ + arg_type arg,\ + ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ + const + +#define MATCHER_P7(name, p0, p1, p2, p3, p4, p5, p6, description)\ + template \ + class name##MatcherP7 {\ + public:\ + template \ + class gmock_Impl : public ::testing::MatcherInterface {\ + public:\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6)\ + : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), \ + p4(gmock_p4), p5(gmock_p5), p6(gmock_p6) {}\ + virtual bool MatchAndExplain(\ + arg_type arg, ::testing::MatchResultListener* result_listener) const;\ + virtual void DescribeTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(false);\ + }\ + virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(true);\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + private:\ + ::testing::internal::string FormatDescription(bool negation) const {\ + const ::testing::internal::string gmock_description = (description);\ + if (!gmock_description.empty())\ + return gmock_description;\ + return ::testing::internal::FormatMatcherDescription(\ + negation, #name,\ + ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ + ::std::tr1::tuple(p0, p1, p2, p3, p4, p5, \ + p6)));\ + }\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template \ + operator ::testing::Matcher() const {\ + return ::testing::Matcher(\ + new gmock_Impl(p0, p1, p2, p3, p4, p5, p6));\ + }\ + name##MatcherP7(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \ + p5##_type gmock_p5, p6##_type gmock_p6) : p0(gmock_p0), p1(gmock_p1), \ + p2(gmock_p2), p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), \ + p6(gmock_p6) {\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##MatcherP7);\ + };\ + template \ + inline name##MatcherP7 name(p0##_type p0, p1##_type p1, \ + p2##_type p2, p3##_type p3, p4##_type p4, p5##_type p5, \ + p6##_type p6) {\ + return name##MatcherP7(p0, p1, p2, p3, p4, p5, p6);\ + }\ + template \ + template \ + bool name##MatcherP7::gmock_Impl::MatchAndExplain(\ + arg_type arg,\ + ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ + const + +#define MATCHER_P8(name, p0, p1, p2, p3, p4, p5, p6, p7, description)\ + template \ + class name##MatcherP8 {\ + public:\ + template \ + class gmock_Impl : public ::testing::MatcherInterface {\ + public:\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6, p7##_type gmock_p7)\ + : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), \ + p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), p7(gmock_p7) {}\ + virtual bool MatchAndExplain(\ + arg_type arg, ::testing::MatchResultListener* result_listener) const;\ + virtual void DescribeTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(false);\ + }\ + virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(true);\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + p7##_type p7;\ + private:\ + ::testing::internal::string FormatDescription(bool negation) const {\ + const ::testing::internal::string gmock_description = (description);\ + if (!gmock_description.empty())\ + return gmock_description;\ + return ::testing::internal::FormatMatcherDescription(\ + negation, #name,\ + ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ + ::std::tr1::tuple(p0, p1, p2, \ + p3, p4, p5, p6, p7)));\ + }\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template \ + operator ::testing::Matcher() const {\ + return ::testing::Matcher(\ + new gmock_Impl(p0, p1, p2, p3, p4, p5, p6, p7));\ + }\ + name##MatcherP8(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \ + p5##_type gmock_p5, p6##_type gmock_p6, \ + p7##_type gmock_p7) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), \ + p7(gmock_p7) {\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + p7##_type p7;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##MatcherP8);\ + };\ + template \ + inline name##MatcherP8 name(p0##_type p0, \ + p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, p5##_type p5, \ + p6##_type p6, p7##_type p7) {\ + return name##MatcherP8(p0, p1, p2, p3, p4, p5, \ + p6, p7);\ + }\ + template \ + template \ + bool name##MatcherP8::gmock_Impl::MatchAndExplain(\ + arg_type arg,\ + ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ + const + +#define MATCHER_P9(name, p0, p1, p2, p3, p4, p5, p6, p7, p8, description)\ + template \ + class name##MatcherP9 {\ + public:\ + template \ + class gmock_Impl : public ::testing::MatcherInterface {\ + public:\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6, p7##_type gmock_p7, p8##_type gmock_p8)\ + : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), \ + p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), p7(gmock_p7), \ + p8(gmock_p8) {}\ + virtual bool MatchAndExplain(\ + arg_type arg, ::testing::MatchResultListener* result_listener) const;\ + virtual void DescribeTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(false);\ + }\ + virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(true);\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + p7##_type p7;\ + p8##_type p8;\ + private:\ + ::testing::internal::string FormatDescription(bool negation) const {\ + const ::testing::internal::string gmock_description = (description);\ + if (!gmock_description.empty())\ + return gmock_description;\ + return ::testing::internal::FormatMatcherDescription(\ + negation, #name,\ + ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ + ::std::tr1::tuple(p0, p1, p2, p3, p4, p5, p6, p7, p8)));\ + }\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template \ + operator ::testing::Matcher() const {\ + return ::testing::Matcher(\ + new gmock_Impl(p0, p1, p2, p3, p4, p5, p6, p7, p8));\ + }\ + name##MatcherP9(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \ + p5##_type gmock_p5, p6##_type gmock_p6, p7##_type gmock_p7, \ + p8##_type gmock_p8) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \ + p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), p7(gmock_p7), \ + p8(gmock_p8) {\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + p7##_type p7;\ + p8##_type p8;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##MatcherP9);\ + };\ + template \ + inline name##MatcherP9 name(p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \ + p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, \ + p8##_type p8) {\ + return name##MatcherP9(p0, p1, p2, \ + p3, p4, p5, p6, p7, p8);\ + }\ + template \ + template \ + bool name##MatcherP9::gmock_Impl::MatchAndExplain(\ + arg_type arg,\ + ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ + const + +#define MATCHER_P10(name, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, description)\ + template \ + class name##MatcherP10 {\ + public:\ + template \ + class gmock_Impl : public ::testing::MatcherInterface {\ + public:\ + gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \ + p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \ + p6##_type gmock_p6, p7##_type gmock_p7, p8##_type gmock_p8, \ + p9##_type gmock_p9)\ + : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), \ + p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), p7(gmock_p7), \ + p8(gmock_p8), p9(gmock_p9) {}\ + virtual bool MatchAndExplain(\ + arg_type arg, ::testing::MatchResultListener* result_listener) const;\ + virtual void DescribeTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(false);\ + }\ + virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(true);\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + p7##_type p7;\ + p8##_type p8;\ + p9##_type p9;\ + private:\ + ::testing::internal::string FormatDescription(bool negation) const {\ + const ::testing::internal::string gmock_description = (description);\ + if (!gmock_description.empty())\ + return gmock_description;\ + return ::testing::internal::FormatMatcherDescription(\ + negation, #name,\ + ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ + ::std::tr1::tuple(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9)));\ + }\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template \ + operator ::testing::Matcher() const {\ + return ::testing::Matcher(\ + new gmock_Impl(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9));\ + }\ + name##MatcherP10(p0##_type gmock_p0, p1##_type gmock_p1, \ + p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \ + p5##_type gmock_p5, p6##_type gmock_p6, p7##_type gmock_p7, \ + p8##_type gmock_p8, p9##_type gmock_p9) : p0(gmock_p0), p1(gmock_p1), \ + p2(gmock_p2), p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), \ + p7(gmock_p7), p8(gmock_p8), p9(gmock_p9) {\ + }\ + p0##_type p0;\ + p1##_type p1;\ + p2##_type p2;\ + p3##_type p3;\ + p4##_type p4;\ + p5##_type p5;\ + p6##_type p6;\ + p7##_type p7;\ + p8##_type p8;\ + p9##_type p9;\ + private:\ + GTEST_DISALLOW_ASSIGN_(name##MatcherP10);\ + };\ + template \ + inline name##MatcherP10 name(p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \ + p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, p8##_type p8, \ + p9##_type p9) {\ + return name##MatcherP10(p0, \ + p1, p2, p3, p4, p5, p6, p7, p8, p9);\ + }\ + template \ + template \ + bool name##MatcherP10::gmock_Impl::MatchAndExplain(\ + arg_type arg,\ + ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ + const + +#endif // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_MATCHERS_H_ diff --git a/libwvdrmengine/test/gmock/include/gmock/gmock-generated-matchers.h.pump b/libwvdrmengine/test/gmock/include/gmock/gmock-generated-matchers.h.pump new file mode 100644 index 00000000..8c09444c --- /dev/null +++ b/libwvdrmengine/test/gmock/include/gmock/gmock-generated-matchers.h.pump @@ -0,0 +1,651 @@ +$$ -*- mode: c++; -*- +$$ This is a Pump source file. Please use Pump to convert it to +$$ gmock-generated-actions.h. +$$ +$var n = 10 $$ The maximum arity we support. +$$ }} This line fixes auto-indentation of the following code in Emacs. +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Google Mock - a framework for writing C++ mock classes. +// +// This file implements some commonly used variadic matchers. + +#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_MATCHERS_H_ +#define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_MATCHERS_H_ + +#include +#include +#include +#include "gmock/gmock-matchers.h" + +namespace testing { +namespace internal { + +$range i 0..n-1 + +// The type of the i-th (0-based) field of Tuple. +#define GMOCK_FIELD_TYPE_(Tuple, i) \ + typename ::std::tr1::tuple_element::type + +// TupleFields is for selecting fields from a +// tuple of type Tuple. It has two members: +// +// type: a tuple type whose i-th field is the ki-th field of Tuple. +// GetSelectedFields(t): returns fields k0, ..., and kn of t as a tuple. +// +// For example, in class TupleFields, 2, 0>, we have: +// +// type is tuple, and +// GetSelectedFields(make_tuple(true, 'a', 42)) is (42, true). + +template +class TupleFields; + +// This generic version is used when there are $n selectors. +template +class TupleFields { + public: + typedef ::std::tr1::tuple<$for i, [[GMOCK_FIELD_TYPE_(Tuple, k$i)]]> type; + static type GetSelectedFields(const Tuple& t) { + using ::std::tr1::get; + return type($for i, [[get(t)]]); + } +}; + +// The following specialization is used for 0 ~ $(n-1) selectors. + +$for i [[ +$$ }}} +$range j 0..i-1 +$range k 0..n-1 + +template +class TupleFields { + public: + typedef ::std::tr1::tuple<$for j, [[GMOCK_FIELD_TYPE_(Tuple, k$j)]]> type; + static type GetSelectedFields(const Tuple& $if i==0 [[/* t */]] $else [[t]]) { + using ::std::tr1::get; + return type($for j, [[get(t)]]); + } +}; + +]] + +#undef GMOCK_FIELD_TYPE_ + +// Implements the Args() matcher. + +$var ks = [[$for i, [[k$i]]]] +template +class ArgsMatcherImpl : public MatcherInterface { + public: + // ArgsTuple may have top-level const or reference modifiers. + typedef GTEST_REMOVE_REFERENCE_AND_CONST_(ArgsTuple) RawArgsTuple; + typedef typename internal::TupleFields::type SelectedArgs; + typedef Matcher MonomorphicInnerMatcher; + + template + explicit ArgsMatcherImpl(const InnerMatcher& inner_matcher) + : inner_matcher_(SafeMatcherCast(inner_matcher)) {} + + virtual bool MatchAndExplain(ArgsTuple args, + MatchResultListener* listener) const { + const SelectedArgs& selected_args = GetSelectedArgs(args); + if (!listener->IsInterested()) + return inner_matcher_.Matches(selected_args); + + PrintIndices(listener->stream()); + *listener << "are " << PrintToString(selected_args); + + StringMatchResultListener inner_listener; + const bool match = inner_matcher_.MatchAndExplain(selected_args, + &inner_listener); + PrintIfNotEmpty(inner_listener.str(), listener->stream()); + return match; + } + + virtual void DescribeTo(::std::ostream* os) const { + *os << "are a tuple "; + PrintIndices(os); + inner_matcher_.DescribeTo(os); + } + + virtual void DescribeNegationTo(::std::ostream* os) const { + *os << "are a tuple "; + PrintIndices(os); + inner_matcher_.DescribeNegationTo(os); + } + + private: + static SelectedArgs GetSelectedArgs(ArgsTuple args) { + return TupleFields::GetSelectedFields(args); + } + + // Prints the indices of the selected fields. + static void PrintIndices(::std::ostream* os) { + *os << "whose fields ("; + const int indices[$n] = { $ks }; + for (int i = 0; i < $n; i++) { + if (indices[i] < 0) + break; + + if (i >= 1) + *os << ", "; + + *os << "#" << indices[i]; + } + *os << ") "; + } + + const MonomorphicInnerMatcher inner_matcher_; + + GTEST_DISALLOW_ASSIGN_(ArgsMatcherImpl); +}; + +template +class ArgsMatcher { + public: + explicit ArgsMatcher(const InnerMatcher& inner_matcher) + : inner_matcher_(inner_matcher) {} + + template + operator Matcher() const { + return MakeMatcher(new ArgsMatcherImpl(inner_matcher_)); + } + + private: + const InnerMatcher inner_matcher_; + + GTEST_DISALLOW_ASSIGN_(ArgsMatcher); +}; + +// Implements ElementsAre() of 1-$n arguments. + + +$range i 1..n +$for i [[ +$range j 1..i +template <$for j, [[typename T$j]]> +class ElementsAreMatcher$i { + public: + $if i==1 [[explicit ]]ElementsAreMatcher$i($for j, [[const T$j& e$j]])$if i > 0 [[ : ]] + $for j, [[e$j[[]]_(e$j)]] {} + + template + operator Matcher() const { + typedef GTEST_REMOVE_REFERENCE_AND_CONST_(Container) RawContainer; + typedef typename internal::StlContainerView::type::value_type + Element; + +$if i==1 [[ + + // Nokia's Symbian Compiler has a nasty bug where the object put + // in a one-element local array is not destructed when the array + // goes out of scope. This leads to obvious badness as we've + // added the linked_ptr in it to our other linked_ptrs list. + // Hence we implement ElementsAreMatcher1 specially to avoid using + // a local array. + const Matcher matcher = + MatcherCast(e1_); + return MakeMatcher(new ElementsAreMatcherImpl(&matcher, 1)); +]] $else [[ + + const Matcher matchers[] = { + +$for j [[ + MatcherCast(e$j[[]]_), + +]] + }; + + return MakeMatcher(new ElementsAreMatcherImpl(matchers, $i)); +]] + + } + + private: + +$for j [[ + const T$j& e$j[[]]_; + +]] + + GTEST_DISALLOW_ASSIGN_(ElementsAreMatcher$i); +}; + + +]] +} // namespace internal + +// Args(a_matcher) matches a tuple if the selected +// fields of it matches a_matcher. C++ doesn't support default +// arguments for function templates, so we have to overload it. + +$range i 0..n +$for i [[ +$range j 1..i +template <$for j [[int k$j, ]]typename InnerMatcher> +inline internal::ArgsMatcher +Args(const InnerMatcher& matcher) { + return internal::ArgsMatcher(matcher); +} + + +]] +// ElementsAre(e0, e1, ..., e_n) matches an STL-style container with +// (n + 1) elements, where the i-th element in the container must +// match the i-th argument in the list. Each argument of +// ElementsAre() can be either a value or a matcher. We support up to +// $n arguments. +// +// NOTE: Since ElementsAre() cares about the order of the elements, it +// must not be used with containers whose elements's order is +// undefined (e.g. hash_map). + +inline internal::ElementsAreMatcher0 ElementsAre() { + return internal::ElementsAreMatcher0(); +} + +$range i 1..n +$for i [[ +$range j 1..i + +template <$for j, [[typename T$j]]> +inline internal::ElementsAreMatcher$i<$for j, [[T$j]]> ElementsAre($for j, [[const T$j& e$j]]) { + return internal::ElementsAreMatcher$i<$for j, [[T$j]]>($for j, [[e$j]]); +} + +]] + +// ElementsAreArray(array) and ElementAreArray(array, count) are like +// ElementsAre(), except that they take an array of values or +// matchers. The former form infers the size of 'array', which must +// be a static C-style array. In the latter form, 'array' can either +// be a static array or a pointer to a dynamically created array. + +template +inline internal::ElementsAreArrayMatcher ElementsAreArray( + const T* first, size_t count) { + return internal::ElementsAreArrayMatcher(first, count); +} + +template +inline internal::ElementsAreArrayMatcher +ElementsAreArray(const T (&array)[N]) { + return internal::ElementsAreArrayMatcher(array, N); +} + +// AllOf(m1, m2, ..., mk) matches any value that matches all of the given +// sub-matchers. AllOf is called fully qualified to prevent ADL from firing. + +$range i 2..n +$for i [[ +$range j 1..i +$range k 1..i-1 + +template <$for j, [[typename Matcher$j]]> +inline $for k[[internal::BothOfMatcher ]] + +AllOf($for j, [[Matcher$j m$j]]) { + +$if i == 2 [[ + return internal::BothOfMatcher(m1, m2); +]] $else [[ + return ::testing::AllOf(m1, ::testing::AllOf($for k, [[m$(k + 1)]])); +]] + +} + +]] + +// AnyOf(m1, m2, ..., mk) matches any value that matches any of the given +// sub-matchers. AnyOf is called fully qualified to prevent ADL from firing. + +$range i 2..n +$for i [[ +$range j 1..i +$range k 1..i-1 + +template <$for j, [[typename Matcher$j]]> +inline $for k[[internal::EitherOfMatcher ]] + +AnyOf($for j, [[Matcher$j m$j]]) { + +$if i == 2 [[ + return internal::EitherOfMatcher(m1, m2); +]] $else [[ + return ::testing::AnyOf(m1, ::testing::AnyOf($for k, [[m$(k + 1)]])); +]] + +} + +]] + +} // namespace testing +$$ } // This Pump meta comment fixes auto-indentation in Emacs. It will not +$$ // show up in the generated code. + + +// The MATCHER* family of macros can be used in a namespace scope to +// define custom matchers easily. +// +// Basic Usage +// =========== +// +// The syntax +// +// MATCHER(name, description_string) { statements; } +// +// defines a matcher with the given name that executes the statements, +// which must return a bool to indicate if the match succeeds. Inside +// the statements, you can refer to the value being matched by 'arg', +// and refer to its type by 'arg_type'. +// +// The description string documents what the matcher does, and is used +// to generate the failure message when the match fails. Since a +// MATCHER() is usually defined in a header file shared by multiple +// C++ source files, we require the description to be a C-string +// literal to avoid possible side effects. It can be empty, in which +// case we'll use the sequence of words in the matcher name as the +// description. +// +// For example: +// +// MATCHER(IsEven, "") { return (arg % 2) == 0; } +// +// allows you to write +// +// // Expects mock_foo.Bar(n) to be called where n is even. +// EXPECT_CALL(mock_foo, Bar(IsEven())); +// +// or, +// +// // Verifies that the value of some_expression is even. +// EXPECT_THAT(some_expression, IsEven()); +// +// If the above assertion fails, it will print something like: +// +// Value of: some_expression +// Expected: is even +// Actual: 7 +// +// where the description "is even" is automatically calculated from the +// matcher name IsEven. +// +// Argument Type +// ============= +// +// Note that the type of the value being matched (arg_type) is +// determined by the context in which you use the matcher and is +// supplied to you by the compiler, so you don't need to worry about +// declaring it (nor can you). This allows the matcher to be +// polymorphic. For example, IsEven() can be used to match any type +// where the value of "(arg % 2) == 0" can be implicitly converted to +// a bool. In the "Bar(IsEven())" example above, if method Bar() +// takes an int, 'arg_type' will be int; if it takes an unsigned long, +// 'arg_type' will be unsigned long; and so on. +// +// Parameterizing Matchers +// ======================= +// +// Sometimes you'll want to parameterize the matcher. For that you +// can use another macro: +// +// MATCHER_P(name, param_name, description_string) { statements; } +// +// For example: +// +// MATCHER_P(HasAbsoluteValue, value, "") { return abs(arg) == value; } +// +// will allow you to write: +// +// EXPECT_THAT(Blah("a"), HasAbsoluteValue(n)); +// +// which may lead to this message (assuming n is 10): +// +// Value of: Blah("a") +// Expected: has absolute value 10 +// Actual: -9 +// +// Note that both the matcher description and its parameter are +// printed, making the message human-friendly. +// +// In the matcher definition body, you can write 'foo_type' to +// reference the type of a parameter named 'foo'. For example, in the +// body of MATCHER_P(HasAbsoluteValue, value) above, you can write +// 'value_type' to refer to the type of 'value'. +// +// We also provide MATCHER_P2, MATCHER_P3, ..., up to MATCHER_P$n to +// support multi-parameter matchers. +// +// Describing Parameterized Matchers +// ================================= +// +// The last argument to MATCHER*() is a string-typed expression. The +// expression can reference all of the matcher's parameters and a +// special bool-typed variable named 'negation'. When 'negation' is +// false, the expression should evaluate to the matcher's description; +// otherwise it should evaluate to the description of the negation of +// the matcher. For example, +// +// using testing::PrintToString; +// +// MATCHER_P2(InClosedRange, low, hi, +// string(negation ? "is not" : "is") + " in range [" + +// PrintToString(low) + ", " + PrintToString(hi) + "]") { +// return low <= arg && arg <= hi; +// } +// ... +// EXPECT_THAT(3, InClosedRange(4, 6)); +// EXPECT_THAT(3, Not(InClosedRange(2, 4))); +// +// would generate two failures that contain the text: +// +// Expected: is in range [4, 6] +// ... +// Expected: is not in range [2, 4] +// +// If you specify "" as the description, the failure message will +// contain the sequence of words in the matcher name followed by the +// parameter values printed as a tuple. For example, +// +// MATCHER_P2(InClosedRange, low, hi, "") { ... } +// ... +// EXPECT_THAT(3, InClosedRange(4, 6)); +// EXPECT_THAT(3, Not(InClosedRange(2, 4))); +// +// would generate two failures that contain the text: +// +// Expected: in closed range (4, 6) +// ... +// Expected: not (in closed range (2, 4)) +// +// Types of Matcher Parameters +// =========================== +// +// For the purpose of typing, you can view +// +// MATCHER_Pk(Foo, p1, ..., pk, description_string) { ... } +// +// as shorthand for +// +// template +// FooMatcherPk +// Foo(p1_type p1, ..., pk_type pk) { ... } +// +// When you write Foo(v1, ..., vk), the compiler infers the types of +// the parameters v1, ..., and vk for you. If you are not happy with +// the result of the type inference, you can specify the types by +// explicitly instantiating the template, as in Foo(5, +// false). As said earlier, you don't get to (or need to) specify +// 'arg_type' as that's determined by the context in which the matcher +// is used. You can assign the result of expression Foo(p1, ..., pk) +// to a variable of type FooMatcherPk. This +// can be useful when composing matchers. +// +// While you can instantiate a matcher template with reference types, +// passing the parameters by pointer usually makes your code more +// readable. If, however, you still want to pass a parameter by +// reference, be aware that in the failure message generated by the +// matcher you will see the value of the referenced object but not its +// address. +// +// Explaining Match Results +// ======================== +// +// Sometimes the matcher description alone isn't enough to explain why +// the match has failed or succeeded. For example, when expecting a +// long string, it can be very helpful to also print the diff between +// the expected string and the actual one. To achieve that, you can +// optionally stream additional information to a special variable +// named result_listener, whose type is a pointer to class +// MatchResultListener: +// +// MATCHER_P(EqualsLongString, str, "") { +// if (arg == str) return true; +// +// *result_listener << "the difference: " +/// << DiffStrings(str, arg); +// return false; +// } +// +// Overloading Matchers +// ==================== +// +// You can overload matchers with different numbers of parameters: +// +// MATCHER_P(Blah, a, description_string1) { ... } +// MATCHER_P2(Blah, a, b, description_string2) { ... } +// +// Caveats +// ======= +// +// When defining a new matcher, you should also consider implementing +// MatcherInterface or using MakePolymorphicMatcher(). These +// approaches require more work than the MATCHER* macros, but also +// give you more control on the types of the value being matched and +// the matcher parameters, which may leads to better compiler error +// messages when the matcher is used wrong. They also allow +// overloading matchers based on parameter types (as opposed to just +// based on the number of parameters). +// +// MATCHER*() can only be used in a namespace scope. The reason is +// that C++ doesn't yet allow function-local types to be used to +// instantiate templates. The up-coming C++0x standard will fix this. +// Once that's done, we'll consider supporting using MATCHER*() inside +// a function. +// +// More Information +// ================ +// +// To learn more about using these macros, please search for 'MATCHER' +// on http://code.google.com/p/googlemock/wiki/CookBook. + +$range i 0..n +$for i + +[[ +$var macro_name = [[$if i==0 [[MATCHER]] $elif i==1 [[MATCHER_P]] + $else [[MATCHER_P$i]]]] +$var class_name = [[name##Matcher[[$if i==0 [[]] $elif i==1 [[P]] + $else [[P$i]]]]]] +$range j 0..i-1 +$var template = [[$if i==0 [[]] $else [[ + + template <$for j, [[typename p$j##_type]]>\ +]]]] +$var ctor_param_list = [[$for j, [[p$j##_type gmock_p$j]]]] +$var impl_ctor_param_list = [[$for j, [[p$j##_type gmock_p$j]]]] +$var impl_inits = [[$if i==0 [[]] $else [[ : $for j, [[p$j(gmock_p$j)]]]]]] +$var inits = [[$if i==0 [[]] $else [[ : $for j, [[p$j(gmock_p$j)]]]]]] +$var params = [[$for j, [[p$j]]]] +$var param_types = [[$if i==0 [[]] $else [[<$for j, [[p$j##_type]]>]]]] +$var param_types_and_names = [[$for j, [[p$j##_type p$j]]]] +$var param_field_decls = [[$for j +[[ + + p$j##_type p$j;\ +]]]] +$var param_field_decls2 = [[$for j +[[ + + p$j##_type p$j;\ +]]]] + +#define $macro_name(name$for j [[, p$j]], description)\$template + class $class_name {\ + public:\ + template \ + class gmock_Impl : public ::testing::MatcherInterface {\ + public:\ + [[$if i==1 [[explicit ]]]]gmock_Impl($impl_ctor_param_list)\ + $impl_inits {}\ + virtual bool MatchAndExplain(\ + arg_type arg, ::testing::MatchResultListener* result_listener) const;\ + virtual void DescribeTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(false);\ + }\ + virtual void DescribeNegationTo(::std::ostream* gmock_os) const {\ + *gmock_os << FormatDescription(true);\ + }\$param_field_decls + private:\ + ::testing::internal::string FormatDescription(bool negation) const {\ + const ::testing::internal::string gmock_description = (description);\ + if (!gmock_description.empty())\ + return gmock_description;\ + return ::testing::internal::FormatMatcherDescription(\ + negation, #name,\ + ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ + ::std::tr1::tuple<$for j, [[p$j##_type]]>($for j, [[p$j]])));\ + }\ + GTEST_DISALLOW_ASSIGN_(gmock_Impl);\ + };\ + template \ + operator ::testing::Matcher() const {\ + return ::testing::Matcher(\ + new gmock_Impl($params));\ + }\ + $class_name($ctor_param_list)$inits {\ + }\$param_field_decls2 + private:\ + GTEST_DISALLOW_ASSIGN_($class_name);\ + };\$template + inline $class_name$param_types name($param_types_and_names) {\ + return $class_name$param_types($params);\ + }\$template + template \ + bool $class_name$param_types::gmock_Impl::MatchAndExplain(\ + arg_type arg,\ + ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_)\ + const +]] + + +#endif // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_MATCHERS_H_ diff --git a/libwvdrmengine/test/gmock/include/gmock/gmock-generated-nice-strict.h b/libwvdrmengine/test/gmock/include/gmock/gmock-generated-nice-strict.h new file mode 100644 index 00000000..6099e81e --- /dev/null +++ b/libwvdrmengine/test/gmock/include/gmock/gmock-generated-nice-strict.h @@ -0,0 +1,274 @@ +// This file was GENERATED by a script. DO NOT EDIT BY HAND!!! + +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Implements class templates NiceMock and StrictMock. +// +// Given a mock class MockFoo that is created using Google Mock, +// NiceMock is a subclass of MockFoo that allows +// uninteresting calls (i.e. calls to mock methods that have no +// EXPECT_CALL specs), and StrictMock is a subclass of +// MockFoo that treats all uninteresting calls as errors. +// +// NiceMock and StrictMock "inherits" the constructors of their +// respective base class, with up-to 10 arguments. Therefore you can +// write NiceMock(5, "a") to construct a nice mock where +// MockFoo has a constructor that accepts (int, const char*), for +// example. +// +// A known limitation is that NiceMock and +// StrictMock only works for mock methods defined using the +// MOCK_METHOD* family of macros DIRECTLY in the MockFoo class. If a +// mock method is defined in a base class of MockFoo, the "nice" or +// "strict" modifier may not affect it, depending on the compiler. In +// particular, nesting NiceMock and StrictMock is NOT supported. +// +// Another known limitation is that the constructors of the base mock +// cannot have arguments passed by non-const reference, which are +// banned by the Google C++ style guide anyway. + +#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_NICE_STRICT_H_ +#define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_NICE_STRICT_H_ + +#include "gmock/gmock-spec-builders.h" +#include "gmock/internal/gmock-port.h" + +namespace testing { + +template +class NiceMock : public MockClass { + public: + // We don't factor out the constructor body to a common method, as + // we have to avoid a possible clash with members of MockClass. + NiceMock() { + ::testing::Mock::AllowUninterestingCalls( + internal::ImplicitCast_(this)); + } + + // C++ doesn't (yet) allow inheritance of constructors, so we have + // to define it for each arity. + template + explicit NiceMock(const A1& a1) : MockClass(a1) { + ::testing::Mock::AllowUninterestingCalls( + internal::ImplicitCast_(this)); + } + template + NiceMock(const A1& a1, const A2& a2) : MockClass(a1, a2) { + ::testing::Mock::AllowUninterestingCalls( + internal::ImplicitCast_(this)); + } + + template + NiceMock(const A1& a1, const A2& a2, const A3& a3) : MockClass(a1, a2, a3) { + ::testing::Mock::AllowUninterestingCalls( + internal::ImplicitCast_(this)); + } + + template + NiceMock(const A1& a1, const A2& a2, const A3& a3, + const A4& a4) : MockClass(a1, a2, a3, a4) { + ::testing::Mock::AllowUninterestingCalls( + internal::ImplicitCast_(this)); + } + + template + NiceMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, + const A5& a5) : MockClass(a1, a2, a3, a4, a5) { + ::testing::Mock::AllowUninterestingCalls( + internal::ImplicitCast_(this)); + } + + template + NiceMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, + const A5& a5, const A6& a6) : MockClass(a1, a2, a3, a4, a5, a6) { + ::testing::Mock::AllowUninterestingCalls( + internal::ImplicitCast_(this)); + } + + template + NiceMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, + const A5& a5, const A6& a6, const A7& a7) : MockClass(a1, a2, a3, a4, a5, + a6, a7) { + ::testing::Mock::AllowUninterestingCalls( + internal::ImplicitCast_(this)); + } + + template + NiceMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, + const A5& a5, const A6& a6, const A7& a7, const A8& a8) : MockClass(a1, + a2, a3, a4, a5, a6, a7, a8) { + ::testing::Mock::AllowUninterestingCalls( + internal::ImplicitCast_(this)); + } + + template + NiceMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, + const A5& a5, const A6& a6, const A7& a7, const A8& a8, + const A9& a9) : MockClass(a1, a2, a3, a4, a5, a6, a7, a8, a9) { + ::testing::Mock::AllowUninterestingCalls( + internal::ImplicitCast_(this)); + } + + template + NiceMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, + const A5& a5, const A6& a6, const A7& a7, const A8& a8, const A9& a9, + const A10& a10) : MockClass(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) { + ::testing::Mock::AllowUninterestingCalls( + internal::ImplicitCast_(this)); + } + + virtual ~NiceMock() { + ::testing::Mock::UnregisterCallReaction( + internal::ImplicitCast_(this)); + } + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(NiceMock); +}; + +template +class StrictMock : public MockClass { + public: + // We don't factor out the constructor body to a common method, as + // we have to avoid a possible clash with members of MockClass. + StrictMock() { + ::testing::Mock::FailUninterestingCalls( + internal::ImplicitCast_(this)); + } + + template + explicit StrictMock(const A1& a1) : MockClass(a1) { + ::testing::Mock::FailUninterestingCalls( + internal::ImplicitCast_(this)); + } + template + StrictMock(const A1& a1, const A2& a2) : MockClass(a1, a2) { + ::testing::Mock::FailUninterestingCalls( + internal::ImplicitCast_(this)); + } + + template + StrictMock(const A1& a1, const A2& a2, const A3& a3) : MockClass(a1, a2, a3) { + ::testing::Mock::FailUninterestingCalls( + internal::ImplicitCast_(this)); + } + + template + StrictMock(const A1& a1, const A2& a2, const A3& a3, + const A4& a4) : MockClass(a1, a2, a3, a4) { + ::testing::Mock::FailUninterestingCalls( + internal::ImplicitCast_(this)); + } + + template + StrictMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, + const A5& a5) : MockClass(a1, a2, a3, a4, a5) { + ::testing::Mock::FailUninterestingCalls( + internal::ImplicitCast_(this)); + } + + template + StrictMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, + const A5& a5, const A6& a6) : MockClass(a1, a2, a3, a4, a5, a6) { + ::testing::Mock::FailUninterestingCalls( + internal::ImplicitCast_(this)); + } + + template + StrictMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, + const A5& a5, const A6& a6, const A7& a7) : MockClass(a1, a2, a3, a4, a5, + a6, a7) { + ::testing::Mock::FailUninterestingCalls( + internal::ImplicitCast_(this)); + } + + template + StrictMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, + const A5& a5, const A6& a6, const A7& a7, const A8& a8) : MockClass(a1, + a2, a3, a4, a5, a6, a7, a8) { + ::testing::Mock::FailUninterestingCalls( + internal::ImplicitCast_(this)); + } + + template + StrictMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, + const A5& a5, const A6& a6, const A7& a7, const A8& a8, + const A9& a9) : MockClass(a1, a2, a3, a4, a5, a6, a7, a8, a9) { + ::testing::Mock::FailUninterestingCalls( + internal::ImplicitCast_(this)); + } + + template + StrictMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4, + const A5& a5, const A6& a6, const A7& a7, const A8& a8, const A9& a9, + const A10& a10) : MockClass(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) { + ::testing::Mock::FailUninterestingCalls( + internal::ImplicitCast_(this)); + } + + virtual ~StrictMock() { + ::testing::Mock::UnregisterCallReaction( + internal::ImplicitCast_(this)); + } + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(StrictMock); +}; + +// The following specializations catch some (relatively more common) +// user errors of nesting nice and strict mocks. They do NOT catch +// all possible errors. + +// These specializations are declared but not defined, as NiceMock and +// StrictMock cannot be nested. +template +class NiceMock >; +template +class NiceMock >; +template +class StrictMock >; +template +class StrictMock >; + +} // namespace testing + +#endif // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_NICE_STRICT_H_ diff --git a/libwvdrmengine/test/gmock/include/gmock/gmock-generated-nice-strict.h.pump b/libwvdrmengine/test/gmock/include/gmock/gmock-generated-nice-strict.h.pump new file mode 100644 index 00000000..b7964db3 --- /dev/null +++ b/libwvdrmengine/test/gmock/include/gmock/gmock-generated-nice-strict.h.pump @@ -0,0 +1,160 @@ +$$ -*- mode: c++; -*- +$$ This is a Pump source file. Please use Pump to convert it to +$$ gmock-generated-nice-strict.h. +$$ +$var n = 10 $$ The maximum arity we support. +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Implements class templates NiceMock and StrictMock. +// +// Given a mock class MockFoo that is created using Google Mock, +// NiceMock is a subclass of MockFoo that allows +// uninteresting calls (i.e. calls to mock methods that have no +// EXPECT_CALL specs), and StrictMock is a subclass of +// MockFoo that treats all uninteresting calls as errors. +// +// NiceMock and StrictMock "inherits" the constructors of their +// respective base class, with up-to $n arguments. Therefore you can +// write NiceMock(5, "a") to construct a nice mock where +// MockFoo has a constructor that accepts (int, const char*), for +// example. +// +// A known limitation is that NiceMock and +// StrictMock only works for mock methods defined using the +// MOCK_METHOD* family of macros DIRECTLY in the MockFoo class. If a +// mock method is defined in a base class of MockFoo, the "nice" or +// "strict" modifier may not affect it, depending on the compiler. In +// particular, nesting NiceMock and StrictMock is NOT supported. +// +// Another known limitation is that the constructors of the base mock +// cannot have arguments passed by non-const reference, which are +// banned by the Google C++ style guide anyway. + +#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_NICE_STRICT_H_ +#define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_NICE_STRICT_H_ + +#include "gmock/gmock-spec-builders.h" +#include "gmock/internal/gmock-port.h" + +namespace testing { + +template +class NiceMock : public MockClass { + public: + // We don't factor out the constructor body to a common method, as + // we have to avoid a possible clash with members of MockClass. + NiceMock() { + ::testing::Mock::AllowUninterestingCalls( + internal::ImplicitCast_(this)); + } + + // C++ doesn't (yet) allow inheritance of constructors, so we have + // to define it for each arity. + template + explicit NiceMock(const A1& a1) : MockClass(a1) { + ::testing::Mock::AllowUninterestingCalls( + internal::ImplicitCast_(this)); + } + +$range i 2..n +$for i [[ +$range j 1..i + template <$for j, [[typename A$j]]> + NiceMock($for j, [[const A$j& a$j]]) : MockClass($for j, [[a$j]]) { + ::testing::Mock::AllowUninterestingCalls( + internal::ImplicitCast_(this)); + } + + +]] + virtual ~NiceMock() { + ::testing::Mock::UnregisterCallReaction( + internal::ImplicitCast_(this)); + } + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(NiceMock); +}; + +template +class StrictMock : public MockClass { + public: + // We don't factor out the constructor body to a common method, as + // we have to avoid a possible clash with members of MockClass. + StrictMock() { + ::testing::Mock::FailUninterestingCalls( + internal::ImplicitCast_(this)); + } + + template + explicit StrictMock(const A1& a1) : MockClass(a1) { + ::testing::Mock::FailUninterestingCalls( + internal::ImplicitCast_(this)); + } + +$for i [[ +$range j 1..i + template <$for j, [[typename A$j]]> + StrictMock($for j, [[const A$j& a$j]]) : MockClass($for j, [[a$j]]) { + ::testing::Mock::FailUninterestingCalls( + internal::ImplicitCast_(this)); + } + + +]] + virtual ~StrictMock() { + ::testing::Mock::UnregisterCallReaction( + internal::ImplicitCast_(this)); + } + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(StrictMock); +}; + +// The following specializations catch some (relatively more common) +// user errors of nesting nice and strict mocks. They do NOT catch +// all possible errors. + +// These specializations are declared but not defined, as NiceMock and +// StrictMock cannot be nested. +template +class NiceMock >; +template +class NiceMock >; +template +class StrictMock >; +template +class StrictMock >; + +} // namespace testing + +#endif // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_NICE_STRICT_H_ diff --git a/libwvdrmengine/test/gmock/include/gmock/gmock-matchers.h b/libwvdrmengine/test/gmock/include/gmock/gmock-matchers.h new file mode 100644 index 00000000..c21fa515 --- /dev/null +++ b/libwvdrmengine/test/gmock/include/gmock/gmock-matchers.h @@ -0,0 +1,3066 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file implements some commonly used argument matchers. More +// matchers can be defined by the user implementing the +// MatcherInterface interface if necessary. + +#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_MATCHERS_H_ +#define GMOCK_INCLUDE_GMOCK_GMOCK_MATCHERS_H_ + +#include +#include +#include // NOLINT +#include +#include +#include +#include + +#include "gmock/internal/gmock-internal-utils.h" +#include "gmock/internal/gmock-port.h" +#include "gtest/gtest.h" + +namespace testing { + +// To implement a matcher Foo for type T, define: +// 1. a class FooMatcherImpl that implements the +// MatcherInterface interface, and +// 2. a factory function that creates a Matcher object from a +// FooMatcherImpl*. +// +// The two-level delegation design makes it possible to allow a user +// to write "v" instead of "Eq(v)" where a Matcher is expected, which +// is impossible if we pass matchers by pointers. It also eases +// ownership management as Matcher objects can now be copied like +// plain values. + +// MatchResultListener is an abstract class. Its << operator can be +// used by a matcher to explain why a value matches or doesn't match. +// +// TODO(wan@google.com): add method +// bool InterestedInWhy(bool result) const; +// to indicate whether the listener is interested in why the match +// result is 'result'. +class MatchResultListener { + public: + // Creates a listener object with the given underlying ostream. The + // listener does not own the ostream. + explicit MatchResultListener(::std::ostream* os) : stream_(os) {} + virtual ~MatchResultListener() = 0; // Makes this class abstract. + + // Streams x to the underlying ostream; does nothing if the ostream + // is NULL. + template + MatchResultListener& operator<<(const T& x) { + if (stream_ != NULL) + *stream_ << x; + return *this; + } + + // Returns the underlying ostream. + ::std::ostream* stream() { return stream_; } + + // Returns true iff the listener is interested in an explanation of + // the match result. A matcher's MatchAndExplain() method can use + // this information to avoid generating the explanation when no one + // intends to hear it. + bool IsInterested() const { return stream_ != NULL; } + + private: + ::std::ostream* const stream_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(MatchResultListener); +}; + +inline MatchResultListener::~MatchResultListener() { +} + +// The implementation of a matcher. +template +class MatcherInterface { + public: + virtual ~MatcherInterface() {} + + // Returns true iff the matcher matches x; also explains the match + // result to 'listener', in the form of a non-restrictive relative + // clause ("which ...", "whose ...", etc) that describes x. For + // example, the MatchAndExplain() method of the Pointee(...) matcher + // should generate an explanation like "which points to ...". + // + // You should override this method when defining a new matcher. + // + // It's the responsibility of the caller (Google Mock) to guarantee + // that 'listener' is not NULL. This helps to simplify a matcher's + // implementation when it doesn't care about the performance, as it + // can talk to 'listener' without checking its validity first. + // However, in order to implement dummy listeners efficiently, + // listener->stream() may be NULL. + virtual bool MatchAndExplain(T x, MatchResultListener* listener) const = 0; + + // Describes this matcher to an ostream. The function should print + // a verb phrase that describes the property a value matching this + // matcher should have. The subject of the verb phrase is the value + // being matched. For example, the DescribeTo() method of the Gt(7) + // matcher prints "is greater than 7". + virtual void DescribeTo(::std::ostream* os) const = 0; + + // Describes the negation of this matcher to an ostream. For + // example, if the description of this matcher is "is greater than + // 7", the negated description could be "is not greater than 7". + // You are not required to override this when implementing + // MatcherInterface, but it is highly advised so that your matcher + // can produce good error messages. + virtual void DescribeNegationTo(::std::ostream* os) const { + *os << "not ("; + DescribeTo(os); + *os << ")"; + } +}; + +namespace internal { + +// A match result listener that ignores the explanation. +class DummyMatchResultListener : public MatchResultListener { + public: + DummyMatchResultListener() : MatchResultListener(NULL) {} + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(DummyMatchResultListener); +}; + +// A match result listener that forwards the explanation to a given +// ostream. The difference between this and MatchResultListener is +// that the former is concrete. +class StreamMatchResultListener : public MatchResultListener { + public: + explicit StreamMatchResultListener(::std::ostream* os) + : MatchResultListener(os) {} + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(StreamMatchResultListener); +}; + +// A match result listener that stores the explanation in a string. +class StringMatchResultListener : public MatchResultListener { + public: + StringMatchResultListener() : MatchResultListener(&ss_) {} + + // Returns the explanation heard so far. + internal::string str() const { return ss_.str(); } + + private: + ::std::stringstream ss_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(StringMatchResultListener); +}; + +// An internal class for implementing Matcher, which will derive +// from it. We put functionalities common to all Matcher +// specializations here to avoid code duplication. +template +class MatcherBase { + public: + // Returns true iff the matcher matches x; also explains the match + // result to 'listener'. + bool MatchAndExplain(T x, MatchResultListener* listener) const { + return impl_->MatchAndExplain(x, listener); + } + + // Returns true iff this matcher matches x. + bool Matches(T x) const { + DummyMatchResultListener dummy; + return MatchAndExplain(x, &dummy); + } + + // Describes this matcher to an ostream. + void DescribeTo(::std::ostream* os) const { impl_->DescribeTo(os); } + + // Describes the negation of this matcher to an ostream. + void DescribeNegationTo(::std::ostream* os) const { + impl_->DescribeNegationTo(os); + } + + // Explains why x matches, or doesn't match, the matcher. + void ExplainMatchResultTo(T x, ::std::ostream* os) const { + StreamMatchResultListener listener(os); + MatchAndExplain(x, &listener); + } + + protected: + MatcherBase() {} + + // Constructs a matcher from its implementation. + explicit MatcherBase(const MatcherInterface* impl) + : impl_(impl) {} + + virtual ~MatcherBase() {} + + private: + // shared_ptr (util/gtl/shared_ptr.h) and linked_ptr have similar + // interfaces. The former dynamically allocates a chunk of memory + // to hold the reference count, while the latter tracks all + // references using a circular linked list without allocating + // memory. It has been observed that linked_ptr performs better in + // typical scenarios. However, shared_ptr can out-perform + // linked_ptr when there are many more uses of the copy constructor + // than the default constructor. + // + // If performance becomes a problem, we should see if using + // shared_ptr helps. + ::testing::internal::linked_ptr > impl_; +}; + +} // namespace internal + +// A Matcher is a copyable and IMMUTABLE (except by assignment) +// object that can check whether a value of type T matches. The +// implementation of Matcher is just a linked_ptr to const +// MatcherInterface, so copying is fairly cheap. Don't inherit +// from Matcher! +template +class Matcher : public internal::MatcherBase { + public: + // Constructs a null matcher. Needed for storing Matcher objects in STL + // containers. A default-constructed matcher is not yet initialized. You + // cannot use it until a valid value has been assigned to it. + Matcher() {} + + // Constructs a matcher from its implementation. + explicit Matcher(const MatcherInterface* impl) + : internal::MatcherBase(impl) {} + + // Implicit constructor here allows people to write + // EXPECT_CALL(foo, Bar(5)) instead of EXPECT_CALL(foo, Bar(Eq(5))) sometimes + Matcher(T value); // NOLINT +}; + +// The following two specializations allow the user to write str +// instead of Eq(str) and "foo" instead of Eq("foo") when a string +// matcher is expected. +template <> +class Matcher + : public internal::MatcherBase { + public: + Matcher() {} + + explicit Matcher(const MatcherInterface* impl) + : internal::MatcherBase(impl) {} + + // Allows the user to write str instead of Eq(str) sometimes, where + // str is a string object. + Matcher(const internal::string& s); // NOLINT + + // Allows the user to write "foo" instead of Eq("foo") sometimes. + Matcher(const char* s); // NOLINT +}; + +template <> +class Matcher + : public internal::MatcherBase { + public: + Matcher() {} + + explicit Matcher(const MatcherInterface* impl) + : internal::MatcherBase(impl) {} + + // Allows the user to write str instead of Eq(str) sometimes, where + // str is a string object. + Matcher(const internal::string& s); // NOLINT + + // Allows the user to write "foo" instead of Eq("foo") sometimes. + Matcher(const char* s); // NOLINT +}; + +// The PolymorphicMatcher class template makes it easy to implement a +// polymorphic matcher (i.e. a matcher that can match values of more +// than one type, e.g. Eq(n) and NotNull()). +// +// To define a polymorphic matcher, a user should provide an Impl +// class that has a DescribeTo() method and a DescribeNegationTo() +// method, and define a member function (or member function template) +// +// bool MatchAndExplain(const Value& value, +// MatchResultListener* listener) const; +// +// See the definition of NotNull() for a complete example. +template +class PolymorphicMatcher { + public: + explicit PolymorphicMatcher(const Impl& an_impl) : impl_(an_impl) {} + + // Returns a mutable reference to the underlying matcher + // implementation object. + Impl& mutable_impl() { return impl_; } + + // Returns an immutable reference to the underlying matcher + // implementation object. + const Impl& impl() const { return impl_; } + + template + operator Matcher() const { + return Matcher(new MonomorphicImpl(impl_)); + } + + private: + template + class MonomorphicImpl : public MatcherInterface { + public: + explicit MonomorphicImpl(const Impl& impl) : impl_(impl) {} + + virtual void DescribeTo(::std::ostream* os) const { + impl_.DescribeTo(os); + } + + virtual void DescribeNegationTo(::std::ostream* os) const { + impl_.DescribeNegationTo(os); + } + + virtual bool MatchAndExplain(T x, MatchResultListener* listener) const { + return impl_.MatchAndExplain(x, listener); + } + + private: + const Impl impl_; + + GTEST_DISALLOW_ASSIGN_(MonomorphicImpl); + }; + + Impl impl_; + + GTEST_DISALLOW_ASSIGN_(PolymorphicMatcher); +}; + +// Creates a matcher from its implementation. This is easier to use +// than the Matcher constructor as it doesn't require you to +// explicitly write the template argument, e.g. +// +// MakeMatcher(foo); +// vs +// Matcher(foo); +template +inline Matcher MakeMatcher(const MatcherInterface* impl) { + return Matcher(impl); +}; + +// Creates a polymorphic matcher from its implementation. This is +// easier to use than the PolymorphicMatcher constructor as it +// doesn't require you to explicitly write the template argument, e.g. +// +// MakePolymorphicMatcher(foo); +// vs +// PolymorphicMatcher(foo); +template +inline PolymorphicMatcher MakePolymorphicMatcher(const Impl& impl) { + return PolymorphicMatcher(impl); +} + +// In order to be safe and clear, casting between different matcher +// types is done explicitly via MatcherCast(m), which takes a +// matcher m and returns a Matcher. It compiles only when T can be +// statically converted to the argument type of m. +template +Matcher MatcherCast(M m); + +// Implements SafeMatcherCast(). +// +// We use an intermediate class to do the actual safe casting as Nokia's +// Symbian compiler cannot decide between +// template ... (M) and +// template ... (const Matcher&) +// for function templates but can for member function templates. +template +class SafeMatcherCastImpl { + public: + // This overload handles polymorphic matchers only since monomorphic + // matchers are handled by the next one. + template + static inline Matcher Cast(M polymorphic_matcher) { + return Matcher(polymorphic_matcher); + } + + // This overload handles monomorphic matchers. + // + // In general, if type T can be implicitly converted to type U, we can + // safely convert a Matcher to a Matcher (i.e. Matcher is + // contravariant): just keep a copy of the original Matcher, convert the + // argument from type T to U, and then pass it to the underlying Matcher. + // The only exception is when U is a reference and T is not, as the + // underlying Matcher may be interested in the argument's address, which + // is not preserved in the conversion from T to U. + template + static inline Matcher Cast(const Matcher& matcher) { + // Enforce that T can be implicitly converted to U. + GTEST_COMPILE_ASSERT_((internal::ImplicitlyConvertible::value), + T_must_be_implicitly_convertible_to_U); + // Enforce that we are not converting a non-reference type T to a reference + // type U. + GTEST_COMPILE_ASSERT_( + internal::is_reference::value || !internal::is_reference::value, + cannot_convert_non_referentce_arg_to_reference); + // In case both T and U are arithmetic types, enforce that the + // conversion is not lossy. + typedef GTEST_REMOVE_REFERENCE_AND_CONST_(T) RawT; + typedef GTEST_REMOVE_REFERENCE_AND_CONST_(U) RawU; + const bool kTIsOther = GMOCK_KIND_OF_(RawT) == internal::kOther; + const bool kUIsOther = GMOCK_KIND_OF_(RawU) == internal::kOther; + GTEST_COMPILE_ASSERT_( + kTIsOther || kUIsOther || + (internal::LosslessArithmeticConvertible::value), + conversion_of_arithmetic_types_must_be_lossless); + return MatcherCast(matcher); + } +}; + +template +inline Matcher SafeMatcherCast(const M& polymorphic_matcher) { + return SafeMatcherCastImpl::Cast(polymorphic_matcher); +} + +// A() returns a matcher that matches any value of type T. +template +Matcher A(); + +// Anything inside the 'internal' namespace IS INTERNAL IMPLEMENTATION +// and MUST NOT BE USED IN USER CODE!!! +namespace internal { + +// If the explanation is not empty, prints it to the ostream. +inline void PrintIfNotEmpty(const internal::string& explanation, + std::ostream* os) { + if (explanation != "" && os != NULL) { + *os << ", " << explanation; + } +} + +// Returns true if the given type name is easy to read by a human. +// This is used to decide whether printing the type of a value might +// be helpful. +inline bool IsReadableTypeName(const string& type_name) { + // We consider a type name readable if it's short or doesn't contain + // a template or function type. + return (type_name.length() <= 20 || + type_name.find_first_of("<(") == string::npos); +} + +// Matches the value against the given matcher, prints the value and explains +// the match result to the listener. Returns the match result. +// 'listener' must not be NULL. +// Value cannot be passed by const reference, because some matchers take a +// non-const argument. +template +bool MatchPrintAndExplain(Value& value, const Matcher& matcher, + MatchResultListener* listener) { + if (!listener->IsInterested()) { + // If the listener is not interested, we do not need to construct the + // inner explanation. + return matcher.Matches(value); + } + + StringMatchResultListener inner_listener; + const bool match = matcher.MatchAndExplain(value, &inner_listener); + + UniversalPrint(value, listener->stream()); +#if GTEST_HAS_RTTI + const string& type_name = GetTypeName(); + if (IsReadableTypeName(type_name)) + *listener->stream() << " (of type " << type_name << ")"; +#endif + PrintIfNotEmpty(inner_listener.str(), listener->stream()); + + return match; +} + +// An internal helper class for doing compile-time loop on a tuple's +// fields. +template +class TuplePrefix { + public: + // TuplePrefix::Matches(matcher_tuple, value_tuple) returns true + // iff the first N fields of matcher_tuple matches the first N + // fields of value_tuple, respectively. + template + static bool Matches(const MatcherTuple& matcher_tuple, + const ValueTuple& value_tuple) { + using ::std::tr1::get; + return TuplePrefix::Matches(matcher_tuple, value_tuple) + && get(matcher_tuple).Matches(get(value_tuple)); + } + + // TuplePrefix::ExplainMatchFailuresTo(matchers, values, os) + // describes failures in matching the first N fields of matchers + // against the first N fields of values. If there is no failure, + // nothing will be streamed to os. + template + static void ExplainMatchFailuresTo(const MatcherTuple& matchers, + const ValueTuple& values, + ::std::ostream* os) { + using ::std::tr1::tuple_element; + using ::std::tr1::get; + + // First, describes failures in the first N - 1 fields. + TuplePrefix::ExplainMatchFailuresTo(matchers, values, os); + + // Then describes the failure (if any) in the (N - 1)-th (0-based) + // field. + typename tuple_element::type matcher = + get(matchers); + typedef typename tuple_element::type Value; + Value value = get(values); + StringMatchResultListener listener; + if (!matcher.MatchAndExplain(value, &listener)) { + // TODO(wan): include in the message the name of the parameter + // as used in MOCK_METHOD*() when possible. + *os << " Expected arg #" << N - 1 << ": "; + get(matchers).DescribeTo(os); + *os << "\n Actual: "; + // We remove the reference in type Value to prevent the + // universal printer from printing the address of value, which + // isn't interesting to the user most of the time. The + // matcher's MatchAndExplain() method handles the case when + // the address is interesting. + internal::UniversalPrint(value, os); + PrintIfNotEmpty(listener.str(), os); + *os << "\n"; + } + } +}; + +// The base case. +template <> +class TuplePrefix<0> { + public: + template + static bool Matches(const MatcherTuple& /* matcher_tuple */, + const ValueTuple& /* value_tuple */) { + return true; + } + + template + static void ExplainMatchFailuresTo(const MatcherTuple& /* matchers */, + const ValueTuple& /* values */, + ::std::ostream* /* os */) {} +}; + +// TupleMatches(matcher_tuple, value_tuple) returns true iff all +// matchers in matcher_tuple match the corresponding fields in +// value_tuple. It is a compiler error if matcher_tuple and +// value_tuple have different number of fields or incompatible field +// types. +template +bool TupleMatches(const MatcherTuple& matcher_tuple, + const ValueTuple& value_tuple) { + using ::std::tr1::tuple_size; + // Makes sure that matcher_tuple and value_tuple have the same + // number of fields. + GTEST_COMPILE_ASSERT_(tuple_size::value == + tuple_size::value, + matcher_and_value_have_different_numbers_of_fields); + return TuplePrefix::value>:: + Matches(matcher_tuple, value_tuple); +} + +// Describes failures in matching matchers against values. If there +// is no failure, nothing will be streamed to os. +template +void ExplainMatchFailureTupleTo(const MatcherTuple& matchers, + const ValueTuple& values, + ::std::ostream* os) { + using ::std::tr1::tuple_size; + TuplePrefix::value>::ExplainMatchFailuresTo( + matchers, values, os); +} + +// The MatcherCastImpl class template is a helper for implementing +// MatcherCast(). We need this helper in order to partially +// specialize the implementation of MatcherCast() (C++ allows +// class/struct templates to be partially specialized, but not +// function templates.). + +// This general version is used when MatcherCast()'s argument is a +// polymorphic matcher (i.e. something that can be converted to a +// Matcher but is not one yet; for example, Eq(value)). +template +class MatcherCastImpl { + public: + static Matcher Cast(M polymorphic_matcher) { + return Matcher(polymorphic_matcher); + } +}; + +// This more specialized version is used when MatcherCast()'s argument +// is already a Matcher. This only compiles when type T can be +// statically converted to type U. +template +class MatcherCastImpl > { + public: + static Matcher Cast(const Matcher& source_matcher) { + return Matcher(new Impl(source_matcher)); + } + + private: + class Impl : public MatcherInterface { + public: + explicit Impl(const Matcher& source_matcher) + : source_matcher_(source_matcher) {} + + // We delegate the matching logic to the source matcher. + virtual bool MatchAndExplain(T x, MatchResultListener* listener) const { + return source_matcher_.MatchAndExplain(static_cast(x), listener); + } + + virtual void DescribeTo(::std::ostream* os) const { + source_matcher_.DescribeTo(os); + } + + virtual void DescribeNegationTo(::std::ostream* os) const { + source_matcher_.DescribeNegationTo(os); + } + + private: + const Matcher source_matcher_; + + GTEST_DISALLOW_ASSIGN_(Impl); + }; +}; + +// This even more specialized version is used for efficiently casting +// a matcher to its own type. +template +class MatcherCastImpl > { + public: + static Matcher Cast(const Matcher& matcher) { return matcher; } +}; + +// Implements A(). +template +class AnyMatcherImpl : public MatcherInterface { + public: + virtual bool MatchAndExplain( + T /* x */, MatchResultListener* /* listener */) const { return true; } + virtual void DescribeTo(::std::ostream* os) const { *os << "is anything"; } + virtual void DescribeNegationTo(::std::ostream* os) const { + // This is mostly for completeness' safe, as it's not very useful + // to write Not(A()). However we cannot completely rule out + // such a possibility, and it doesn't hurt to be prepared. + *os << "never matches"; + } +}; + +// Implements _, a matcher that matches any value of any +// type. This is a polymorphic matcher, so we need a template type +// conversion operator to make it appearing as a Matcher for any +// type T. +class AnythingMatcher { + public: + template + operator Matcher() const { return A(); } +}; + +// Implements a matcher that compares a given value with a +// pre-supplied value using one of the ==, <=, <, etc, operators. The +// two values being compared don't have to have the same type. +// +// The matcher defined here is polymorphic (for example, Eq(5) can be +// used to match an int, a short, a double, etc). Therefore we use +// a template type conversion operator in the implementation. +// +// We define this as a macro in order to eliminate duplicated source +// code. +// +// The following template definition assumes that the Rhs parameter is +// a "bare" type (i.e. neither 'const T' nor 'T&'). +#define GMOCK_IMPLEMENT_COMPARISON_MATCHER_( \ + name, op, relation, negated_relation) \ + template class name##Matcher { \ + public: \ + explicit name##Matcher(const Rhs& rhs) : rhs_(rhs) {} \ + template \ + operator Matcher() const { \ + return MakeMatcher(new Impl(rhs_)); \ + } \ + private: \ + template \ + class Impl : public MatcherInterface { \ + public: \ + explicit Impl(const Rhs& rhs) : rhs_(rhs) {} \ + virtual bool MatchAndExplain(\ + Lhs lhs, MatchResultListener* /* listener */) const { \ + return lhs op rhs_; \ + } \ + virtual void DescribeTo(::std::ostream* os) const { \ + *os << relation " "; \ + UniversalPrint(rhs_, os); \ + } \ + virtual void DescribeNegationTo(::std::ostream* os) const { \ + *os << negated_relation " "; \ + UniversalPrint(rhs_, os); \ + } \ + private: \ + Rhs rhs_; \ + GTEST_DISALLOW_ASSIGN_(Impl); \ + }; \ + Rhs rhs_; \ + GTEST_DISALLOW_ASSIGN_(name##Matcher); \ + } + +// Implements Eq(v), Ge(v), Gt(v), Le(v), Lt(v), and Ne(v) +// respectively. +GMOCK_IMPLEMENT_COMPARISON_MATCHER_(Eq, ==, "is equal to", "isn't equal to"); +GMOCK_IMPLEMENT_COMPARISON_MATCHER_(Ge, >=, "is >=", "isn't >="); +GMOCK_IMPLEMENT_COMPARISON_MATCHER_(Gt, >, "is >", "isn't >"); +GMOCK_IMPLEMENT_COMPARISON_MATCHER_(Le, <=, "is <=", "isn't <="); +GMOCK_IMPLEMENT_COMPARISON_MATCHER_(Lt, <, "is <", "isn't <"); +GMOCK_IMPLEMENT_COMPARISON_MATCHER_(Ne, !=, "isn't equal to", "is equal to"); + +#undef GMOCK_IMPLEMENT_COMPARISON_MATCHER_ + +// Implements the polymorphic IsNull() matcher, which matches any raw or smart +// pointer that is NULL. +class IsNullMatcher { + public: + template + bool MatchAndExplain(const Pointer& p, + MatchResultListener* /* listener */) const { + return GetRawPointer(p) == NULL; + } + + void DescribeTo(::std::ostream* os) const { *os << "is NULL"; } + void DescribeNegationTo(::std::ostream* os) const { + *os << "isn't NULL"; + } +}; + +// Implements the polymorphic NotNull() matcher, which matches any raw or smart +// pointer that is not NULL. +class NotNullMatcher { + public: + template + bool MatchAndExplain(const Pointer& p, + MatchResultListener* /* listener */) const { + return GetRawPointer(p) != NULL; + } + + void DescribeTo(::std::ostream* os) const { *os << "isn't NULL"; } + void DescribeNegationTo(::std::ostream* os) const { + *os << "is NULL"; + } +}; + +// Ref(variable) matches any argument that is a reference to +// 'variable'. This matcher is polymorphic as it can match any +// super type of the type of 'variable'. +// +// The RefMatcher template class implements Ref(variable). It can +// only be instantiated with a reference type. This prevents a user +// from mistakenly using Ref(x) to match a non-reference function +// argument. For example, the following will righteously cause a +// compiler error: +// +// int n; +// Matcher m1 = Ref(n); // This won't compile. +// Matcher m2 = Ref(n); // This will compile. +template +class RefMatcher; + +template +class RefMatcher { + // Google Mock is a generic framework and thus needs to support + // mocking any function types, including those that take non-const + // reference arguments. Therefore the template parameter T (and + // Super below) can be instantiated to either a const type or a + // non-const type. + public: + // RefMatcher() takes a T& instead of const T&, as we want the + // compiler to catch using Ref(const_value) as a matcher for a + // non-const reference. + explicit RefMatcher(T& x) : object_(x) {} // NOLINT + + template + operator Matcher() const { + // By passing object_ (type T&) to Impl(), which expects a Super&, + // we make sure that Super is a super type of T. In particular, + // this catches using Ref(const_value) as a matcher for a + // non-const reference, as you cannot implicitly convert a const + // reference to a non-const reference. + return MakeMatcher(new Impl(object_)); + } + + private: + template + class Impl : public MatcherInterface { + public: + explicit Impl(Super& x) : object_(x) {} // NOLINT + + // MatchAndExplain() takes a Super& (as opposed to const Super&) + // in order to match the interface MatcherInterface. + virtual bool MatchAndExplain( + Super& x, MatchResultListener* listener) const { + *listener << "which is located @" << static_cast(&x); + return &x == &object_; + } + + virtual void DescribeTo(::std::ostream* os) const { + *os << "references the variable "; + UniversalPrinter::Print(object_, os); + } + + virtual void DescribeNegationTo(::std::ostream* os) const { + *os << "does not reference the variable "; + UniversalPrinter::Print(object_, os); + } + + private: + const Super& object_; + + GTEST_DISALLOW_ASSIGN_(Impl); + }; + + T& object_; + + GTEST_DISALLOW_ASSIGN_(RefMatcher); +}; + +// Polymorphic helper functions for narrow and wide string matchers. +inline bool CaseInsensitiveCStringEquals(const char* lhs, const char* rhs) { + return String::CaseInsensitiveCStringEquals(lhs, rhs); +} + +inline bool CaseInsensitiveCStringEquals(const wchar_t* lhs, + const wchar_t* rhs) { + return String::CaseInsensitiveWideCStringEquals(lhs, rhs); +} + +// String comparison for narrow or wide strings that can have embedded NUL +// characters. +template +bool CaseInsensitiveStringEquals(const StringType& s1, + const StringType& s2) { + // Are the heads equal? + if (!CaseInsensitiveCStringEquals(s1.c_str(), s2.c_str())) { + return false; + } + + // Skip the equal heads. + const typename StringType::value_type nul = 0; + const size_t i1 = s1.find(nul), i2 = s2.find(nul); + + // Are we at the end of either s1 or s2? + if (i1 == StringType::npos || i2 == StringType::npos) { + return i1 == i2; + } + + // Are the tails equal? + return CaseInsensitiveStringEquals(s1.substr(i1 + 1), s2.substr(i2 + 1)); +} + +// String matchers. + +// Implements equality-based string matchers like StrEq, StrCaseNe, and etc. +template +class StrEqualityMatcher { + public: + typedef typename StringType::const_pointer ConstCharPointer; + + StrEqualityMatcher(const StringType& str, bool expect_eq, + bool case_sensitive) + : string_(str), expect_eq_(expect_eq), case_sensitive_(case_sensitive) {} + + // When expect_eq_ is true, returns true iff s is equal to string_; + // otherwise returns true iff s is not equal to string_. + bool MatchAndExplain(ConstCharPointer s, + MatchResultListener* listener) const { + if (s == NULL) { + return !expect_eq_; + } + return MatchAndExplain(StringType(s), listener); + } + + bool MatchAndExplain(const StringType& s, + MatchResultListener* /* listener */) const { + const bool eq = case_sensitive_ ? s == string_ : + CaseInsensitiveStringEquals(s, string_); + return expect_eq_ == eq; + } + + void DescribeTo(::std::ostream* os) const { + DescribeToHelper(expect_eq_, os); + } + + void DescribeNegationTo(::std::ostream* os) const { + DescribeToHelper(!expect_eq_, os); + } + + private: + void DescribeToHelper(bool expect_eq, ::std::ostream* os) const { + *os << (expect_eq ? "is " : "isn't "); + *os << "equal to "; + if (!case_sensitive_) { + *os << "(ignoring case) "; + } + UniversalPrint(string_, os); + } + + const StringType string_; + const bool expect_eq_; + const bool case_sensitive_; + + GTEST_DISALLOW_ASSIGN_(StrEqualityMatcher); +}; + +// Implements the polymorphic HasSubstr(substring) matcher, which +// can be used as a Matcher as long as T can be converted to a +// string. +template +class HasSubstrMatcher { + public: + typedef typename StringType::const_pointer ConstCharPointer; + + explicit HasSubstrMatcher(const StringType& substring) + : substring_(substring) {} + + // These overloaded methods allow HasSubstr(substring) to be used as a + // Matcher as long as T can be converted to string. Returns true + // iff s contains substring_ as a substring. + bool MatchAndExplain(ConstCharPointer s, + MatchResultListener* listener) const { + return s != NULL && MatchAndExplain(StringType(s), listener); + } + + bool MatchAndExplain(const StringType& s, + MatchResultListener* /* listener */) const { + return s.find(substring_) != StringType::npos; + } + + // Describes what this matcher matches. + void DescribeTo(::std::ostream* os) const { + *os << "has substring "; + UniversalPrint(substring_, os); + } + + void DescribeNegationTo(::std::ostream* os) const { + *os << "has no substring "; + UniversalPrint(substring_, os); + } + + private: + const StringType substring_; + + GTEST_DISALLOW_ASSIGN_(HasSubstrMatcher); +}; + +// Implements the polymorphic StartsWith(substring) matcher, which +// can be used as a Matcher as long as T can be converted to a +// string. +template +class StartsWithMatcher { + public: + typedef typename StringType::const_pointer ConstCharPointer; + + explicit StartsWithMatcher(const StringType& prefix) : prefix_(prefix) { + } + + // These overloaded methods allow StartsWith(prefix) to be used as a + // Matcher as long as T can be converted to string. Returns true + // iff s starts with prefix_. + bool MatchAndExplain(ConstCharPointer s, + MatchResultListener* listener) const { + return s != NULL && MatchAndExplain(StringType(s), listener); + } + + bool MatchAndExplain(const StringType& s, + MatchResultListener* /* listener */) const { + return s.length() >= prefix_.length() && + s.substr(0, prefix_.length()) == prefix_; + } + + void DescribeTo(::std::ostream* os) const { + *os << "starts with "; + UniversalPrint(prefix_, os); + } + + void DescribeNegationTo(::std::ostream* os) const { + *os << "doesn't start with "; + UniversalPrint(prefix_, os); + } + + private: + const StringType prefix_; + + GTEST_DISALLOW_ASSIGN_(StartsWithMatcher); +}; + +// Implements the polymorphic EndsWith(substring) matcher, which +// can be used as a Matcher as long as T can be converted to a +// string. +template +class EndsWithMatcher { + public: + typedef typename StringType::const_pointer ConstCharPointer; + + explicit EndsWithMatcher(const StringType& suffix) : suffix_(suffix) {} + + // These overloaded methods allow EndsWith(suffix) to be used as a + // Matcher as long as T can be converted to string. Returns true + // iff s ends with suffix_. + bool MatchAndExplain(ConstCharPointer s, + MatchResultListener* listener) const { + return s != NULL && MatchAndExplain(StringType(s), listener); + } + + bool MatchAndExplain(const StringType& s, + MatchResultListener* /* listener */) const { + return s.length() >= suffix_.length() && + s.substr(s.length() - suffix_.length()) == suffix_; + } + + void DescribeTo(::std::ostream* os) const { + *os << "ends with "; + UniversalPrint(suffix_, os); + } + + void DescribeNegationTo(::std::ostream* os) const { + *os << "doesn't end with "; + UniversalPrint(suffix_, os); + } + + private: + const StringType suffix_; + + GTEST_DISALLOW_ASSIGN_(EndsWithMatcher); +}; + +// Implements polymorphic matchers MatchesRegex(regex) and +// ContainsRegex(regex), which can be used as a Matcher as long as +// T can be converted to a string. +class MatchesRegexMatcher { + public: + MatchesRegexMatcher(const RE* regex, bool full_match) + : regex_(regex), full_match_(full_match) {} + + // These overloaded methods allow MatchesRegex(regex) to be used as + // a Matcher as long as T can be converted to string. Returns + // true iff s matches regular expression regex. When full_match_ is + // true, a full match is done; otherwise a partial match is done. + bool MatchAndExplain(const char* s, + MatchResultListener* listener) const { + return s != NULL && MatchAndExplain(internal::string(s), listener); + } + + bool MatchAndExplain(const internal::string& s, + MatchResultListener* /* listener */) const { + return full_match_ ? RE::FullMatch(s, *regex_) : + RE::PartialMatch(s, *regex_); + } + + void DescribeTo(::std::ostream* os) const { + *os << (full_match_ ? "matches" : "contains") + << " regular expression "; + UniversalPrinter::Print(regex_->pattern(), os); + } + + void DescribeNegationTo(::std::ostream* os) const { + *os << "doesn't " << (full_match_ ? "match" : "contain") + << " regular expression "; + UniversalPrinter::Print(regex_->pattern(), os); + } + + private: + const internal::linked_ptr regex_; + const bool full_match_; + + GTEST_DISALLOW_ASSIGN_(MatchesRegexMatcher); +}; + +// Implements a matcher that compares the two fields of a 2-tuple +// using one of the ==, <=, <, etc, operators. The two fields being +// compared don't have to have the same type. +// +// The matcher defined here is polymorphic (for example, Eq() can be +// used to match a tuple, a tuple, +// etc). Therefore we use a template type conversion operator in the +// implementation. +// +// We define this as a macro in order to eliminate duplicated source +// code. +#define GMOCK_IMPLEMENT_COMPARISON2_MATCHER_(name, op, relation) \ + class name##2Matcher { \ + public: \ + template \ + operator Matcher< ::std::tr1::tuple >() const { \ + return MakeMatcher(new Impl< ::std::tr1::tuple >); \ + } \ + template \ + operator Matcher&>() const { \ + return MakeMatcher(new Impl&>); \ + } \ + private: \ + template \ + class Impl : public MatcherInterface { \ + public: \ + virtual bool MatchAndExplain( \ + Tuple args, \ + MatchResultListener* /* listener */) const { \ + return ::std::tr1::get<0>(args) op ::std::tr1::get<1>(args); \ + } \ + virtual void DescribeTo(::std::ostream* os) const { \ + *os << "are " relation; \ + } \ + virtual void DescribeNegationTo(::std::ostream* os) const { \ + *os << "aren't " relation; \ + } \ + }; \ + } + +// Implements Eq(), Ge(), Gt(), Le(), Lt(), and Ne() respectively. +GMOCK_IMPLEMENT_COMPARISON2_MATCHER_(Eq, ==, "an equal pair"); +GMOCK_IMPLEMENT_COMPARISON2_MATCHER_( + Ge, >=, "a pair where the first >= the second"); +GMOCK_IMPLEMENT_COMPARISON2_MATCHER_( + Gt, >, "a pair where the first > the second"); +GMOCK_IMPLEMENT_COMPARISON2_MATCHER_( + Le, <=, "a pair where the first <= the second"); +GMOCK_IMPLEMENT_COMPARISON2_MATCHER_( + Lt, <, "a pair where the first < the second"); +GMOCK_IMPLEMENT_COMPARISON2_MATCHER_(Ne, !=, "an unequal pair"); + +#undef GMOCK_IMPLEMENT_COMPARISON2_MATCHER_ + +// Implements the Not(...) matcher for a particular argument type T. +// We do not nest it inside the NotMatcher class template, as that +// will prevent different instantiations of NotMatcher from sharing +// the same NotMatcherImpl class. +template +class NotMatcherImpl : public MatcherInterface { + public: + explicit NotMatcherImpl(const Matcher& matcher) + : matcher_(matcher) {} + + virtual bool MatchAndExplain(T x, MatchResultListener* listener) const { + return !matcher_.MatchAndExplain(x, listener); + } + + virtual void DescribeTo(::std::ostream* os) const { + matcher_.DescribeNegationTo(os); + } + + virtual void DescribeNegationTo(::std::ostream* os) const { + matcher_.DescribeTo(os); + } + + private: + const Matcher matcher_; + + GTEST_DISALLOW_ASSIGN_(NotMatcherImpl); +}; + +// Implements the Not(m) matcher, which matches a value that doesn't +// match matcher m. +template +class NotMatcher { + public: + explicit NotMatcher(InnerMatcher matcher) : matcher_(matcher) {} + + // This template type conversion operator allows Not(m) to be used + // to match any type m can match. + template + operator Matcher() const { + return Matcher(new NotMatcherImpl(SafeMatcherCast(matcher_))); + } + + private: + InnerMatcher matcher_; + + GTEST_DISALLOW_ASSIGN_(NotMatcher); +}; + +// Implements the AllOf(m1, m2) matcher for a particular argument type +// T. We do not nest it inside the BothOfMatcher class template, as +// that will prevent different instantiations of BothOfMatcher from +// sharing the same BothOfMatcherImpl class. +template +class BothOfMatcherImpl : public MatcherInterface { + public: + BothOfMatcherImpl(const Matcher& matcher1, const Matcher& matcher2) + : matcher1_(matcher1), matcher2_(matcher2) {} + + virtual void DescribeTo(::std::ostream* os) const { + *os << "("; + matcher1_.DescribeTo(os); + *os << ") and ("; + matcher2_.DescribeTo(os); + *os << ")"; + } + + virtual void DescribeNegationTo(::std::ostream* os) const { + *os << "("; + matcher1_.DescribeNegationTo(os); + *os << ") or ("; + matcher2_.DescribeNegationTo(os); + *os << ")"; + } + + virtual bool MatchAndExplain(T x, MatchResultListener* listener) const { + // If either matcher1_ or matcher2_ doesn't match x, we only need + // to explain why one of them fails. + StringMatchResultListener listener1; + if (!matcher1_.MatchAndExplain(x, &listener1)) { + *listener << listener1.str(); + return false; + } + + StringMatchResultListener listener2; + if (!matcher2_.MatchAndExplain(x, &listener2)) { + *listener << listener2.str(); + return false; + } + + // Otherwise we need to explain why *both* of them match. + const internal::string s1 = listener1.str(); + const internal::string s2 = listener2.str(); + + if (s1 == "") { + *listener << s2; + } else { + *listener << s1; + if (s2 != "") { + *listener << ", and " << s2; + } + } + return true; + } + + private: + const Matcher matcher1_; + const Matcher matcher2_; + + GTEST_DISALLOW_ASSIGN_(BothOfMatcherImpl); +}; + +// Used for implementing the AllOf(m_1, ..., m_n) matcher, which +// matches a value that matches all of the matchers m_1, ..., and m_n. +template +class BothOfMatcher { + public: + BothOfMatcher(Matcher1 matcher1, Matcher2 matcher2) + : matcher1_(matcher1), matcher2_(matcher2) {} + + // This template type conversion operator allows a + // BothOfMatcher object to match any type that + // both Matcher1 and Matcher2 can match. + template + operator Matcher() const { + return Matcher(new BothOfMatcherImpl(SafeMatcherCast(matcher1_), + SafeMatcherCast(matcher2_))); + } + + private: + Matcher1 matcher1_; + Matcher2 matcher2_; + + GTEST_DISALLOW_ASSIGN_(BothOfMatcher); +}; + +// Implements the AnyOf(m1, m2) matcher for a particular argument type +// T. We do not nest it inside the AnyOfMatcher class template, as +// that will prevent different instantiations of AnyOfMatcher from +// sharing the same EitherOfMatcherImpl class. +template +class EitherOfMatcherImpl : public MatcherInterface { + public: + EitherOfMatcherImpl(const Matcher& matcher1, const Matcher& matcher2) + : matcher1_(matcher1), matcher2_(matcher2) {} + + virtual void DescribeTo(::std::ostream* os) const { + *os << "("; + matcher1_.DescribeTo(os); + *os << ") or ("; + matcher2_.DescribeTo(os); + *os << ")"; + } + + virtual void DescribeNegationTo(::std::ostream* os) const { + *os << "("; + matcher1_.DescribeNegationTo(os); + *os << ") and ("; + matcher2_.DescribeNegationTo(os); + *os << ")"; + } + + virtual bool MatchAndExplain(T x, MatchResultListener* listener) const { + // If either matcher1_ or matcher2_ matches x, we just need to + // explain why *one* of them matches. + StringMatchResultListener listener1; + if (matcher1_.MatchAndExplain(x, &listener1)) { + *listener << listener1.str(); + return true; + } + + StringMatchResultListener listener2; + if (matcher2_.MatchAndExplain(x, &listener2)) { + *listener << listener2.str(); + return true; + } + + // Otherwise we need to explain why *both* of them fail. + const internal::string s1 = listener1.str(); + const internal::string s2 = listener2.str(); + + if (s1 == "") { + *listener << s2; + } else { + *listener << s1; + if (s2 != "") { + *listener << ", and " << s2; + } + } + return false; + } + + private: + const Matcher matcher1_; + const Matcher matcher2_; + + GTEST_DISALLOW_ASSIGN_(EitherOfMatcherImpl); +}; + +// Used for implementing the AnyOf(m_1, ..., m_n) matcher, which +// matches a value that matches at least one of the matchers m_1, ..., +// and m_n. +template +class EitherOfMatcher { + public: + EitherOfMatcher(Matcher1 matcher1, Matcher2 matcher2) + : matcher1_(matcher1), matcher2_(matcher2) {} + + // This template type conversion operator allows a + // EitherOfMatcher object to match any type that + // both Matcher1 and Matcher2 can match. + template + operator Matcher() const { + return Matcher(new EitherOfMatcherImpl( + SafeMatcherCast(matcher1_), SafeMatcherCast(matcher2_))); + } + + private: + Matcher1 matcher1_; + Matcher2 matcher2_; + + GTEST_DISALLOW_ASSIGN_(EitherOfMatcher); +}; + +// Used for implementing Truly(pred), which turns a predicate into a +// matcher. +template +class TrulyMatcher { + public: + explicit TrulyMatcher(Predicate pred) : predicate_(pred) {} + + // This method template allows Truly(pred) to be used as a matcher + // for type T where T is the argument type of predicate 'pred'. The + // argument is passed by reference as the predicate may be + // interested in the address of the argument. + template + bool MatchAndExplain(T& x, // NOLINT + MatchResultListener* /* listener */) const { + // Without the if-statement, MSVC sometimes warns about converting + // a value to bool (warning 4800). + // + // We cannot write 'return !!predicate_(x);' as that doesn't work + // when predicate_(x) returns a class convertible to bool but + // having no operator!(). + if (predicate_(x)) + return true; + return false; + } + + void DescribeTo(::std::ostream* os) const { + *os << "satisfies the given predicate"; + } + + void DescribeNegationTo(::std::ostream* os) const { + *os << "doesn't satisfy the given predicate"; + } + + private: + Predicate predicate_; + + GTEST_DISALLOW_ASSIGN_(TrulyMatcher); +}; + +// Used for implementing Matches(matcher), which turns a matcher into +// a predicate. +template +class MatcherAsPredicate { + public: + explicit MatcherAsPredicate(M matcher) : matcher_(matcher) {} + + // This template operator() allows Matches(m) to be used as a + // predicate on type T where m is a matcher on type T. + // + // The argument x is passed by reference instead of by value, as + // some matcher may be interested in its address (e.g. as in + // Matches(Ref(n))(x)). + template + bool operator()(const T& x) const { + // We let matcher_ commit to a particular type here instead of + // when the MatcherAsPredicate object was constructed. This + // allows us to write Matches(m) where m is a polymorphic matcher + // (e.g. Eq(5)). + // + // If we write Matcher(matcher_).Matches(x) here, it won't + // compile when matcher_ has type Matcher; if we write + // Matcher(matcher_).Matches(x) here, it won't compile + // when matcher_ has type Matcher; if we just write + // matcher_.Matches(x), it won't compile when matcher_ is + // polymorphic, e.g. Eq(5). + // + // MatcherCast() is necessary for making the code work + // in all of the above situations. + return MatcherCast(matcher_).Matches(x); + } + + private: + M matcher_; + + GTEST_DISALLOW_ASSIGN_(MatcherAsPredicate); +}; + +// For implementing ASSERT_THAT() and EXPECT_THAT(). The template +// argument M must be a type that can be converted to a matcher. +template +class PredicateFormatterFromMatcher { + public: + explicit PredicateFormatterFromMatcher(const M& m) : matcher_(m) {} + + // This template () operator allows a PredicateFormatterFromMatcher + // object to act as a predicate-formatter suitable for using with + // Google Test's EXPECT_PRED_FORMAT1() macro. + template + AssertionResult operator()(const char* value_text, const T& x) const { + // We convert matcher_ to a Matcher *now* instead of + // when the PredicateFormatterFromMatcher object was constructed, + // as matcher_ may be polymorphic (e.g. NotNull()) and we won't + // know which type to instantiate it to until we actually see the + // type of x here. + // + // We write MatcherCast(matcher_) instead of + // Matcher(matcher_), as the latter won't compile when + // matcher_ has type Matcher (e.g. An()). + const Matcher matcher = MatcherCast(matcher_); + StringMatchResultListener listener; + if (MatchPrintAndExplain(x, matcher, &listener)) + return AssertionSuccess(); + + ::std::stringstream ss; + ss << "Value of: " << value_text << "\n" + << "Expected: "; + matcher.DescribeTo(&ss); + ss << "\n Actual: " << listener.str(); + return AssertionFailure() << ss.str(); + } + + private: + const M matcher_; + + GTEST_DISALLOW_ASSIGN_(PredicateFormatterFromMatcher); +}; + +// A helper function for converting a matcher to a predicate-formatter +// without the user needing to explicitly write the type. This is +// used for implementing ASSERT_THAT() and EXPECT_THAT(). +template +inline PredicateFormatterFromMatcher +MakePredicateFormatterFromMatcher(const M& matcher) { + return PredicateFormatterFromMatcher(matcher); +} + +// Implements the polymorphic floating point equality matcher, which +// matches two float values using ULP-based approximation. The +// template is meant to be instantiated with FloatType being either +// float or double. +template +class FloatingEqMatcher { + public: + // Constructor for FloatingEqMatcher. + // The matcher's input will be compared with rhs. The matcher treats two + // NANs as equal if nan_eq_nan is true. Otherwise, under IEEE standards, + // equality comparisons between NANs will always return false. + FloatingEqMatcher(FloatType rhs, bool nan_eq_nan) : + rhs_(rhs), nan_eq_nan_(nan_eq_nan) {} + + // Implements floating point equality matcher as a Matcher. + template + class Impl : public MatcherInterface { + public: + Impl(FloatType rhs, bool nan_eq_nan) : + rhs_(rhs), nan_eq_nan_(nan_eq_nan) {} + + virtual bool MatchAndExplain(T value, + MatchResultListener* /* listener */) const { + const FloatingPoint lhs(value), rhs(rhs_); + + // Compares NaNs first, if nan_eq_nan_ is true. + if (nan_eq_nan_ && lhs.is_nan()) { + return rhs.is_nan(); + } + + return lhs.AlmostEquals(rhs); + } + + virtual void DescribeTo(::std::ostream* os) const { + // os->precision() returns the previously set precision, which we + // store to restore the ostream to its original configuration + // after outputting. + const ::std::streamsize old_precision = os->precision( + ::std::numeric_limits::digits10 + 2); + if (FloatingPoint(rhs_).is_nan()) { + if (nan_eq_nan_) { + *os << "is NaN"; + } else { + *os << "never matches"; + } + } else { + *os << "is approximately " << rhs_; + } + os->precision(old_precision); + } + + virtual void DescribeNegationTo(::std::ostream* os) const { + // As before, get original precision. + const ::std::streamsize old_precision = os->precision( + ::std::numeric_limits::digits10 + 2); + if (FloatingPoint(rhs_).is_nan()) { + if (nan_eq_nan_) { + *os << "isn't NaN"; + } else { + *os << "is anything"; + } + } else { + *os << "isn't approximately " << rhs_; + } + // Restore original precision. + os->precision(old_precision); + } + + private: + const FloatType rhs_; + const bool nan_eq_nan_; + + GTEST_DISALLOW_ASSIGN_(Impl); + }; + + // The following 3 type conversion operators allow FloatEq(rhs) and + // NanSensitiveFloatEq(rhs) to be used as a Matcher, a + // Matcher, or a Matcher, but nothing else. + // (While Google's C++ coding style doesn't allow arguments passed + // by non-const reference, we may see them in code not conforming to + // the style. Therefore Google Mock needs to support them.) + operator Matcher() const { + return MakeMatcher(new Impl(rhs_, nan_eq_nan_)); + } + + operator Matcher() const { + return MakeMatcher(new Impl(rhs_, nan_eq_nan_)); + } + + operator Matcher() const { + return MakeMatcher(new Impl(rhs_, nan_eq_nan_)); + } + private: + const FloatType rhs_; + const bool nan_eq_nan_; + + GTEST_DISALLOW_ASSIGN_(FloatingEqMatcher); +}; + +// Implements the Pointee(m) matcher for matching a pointer whose +// pointee matches matcher m. The pointer can be either raw or smart. +template +class PointeeMatcher { + public: + explicit PointeeMatcher(const InnerMatcher& matcher) : matcher_(matcher) {} + + // This type conversion operator template allows Pointee(m) to be + // used as a matcher for any pointer type whose pointee type is + // compatible with the inner matcher, where type Pointer can be + // either a raw pointer or a smart pointer. + // + // The reason we do this instead of relying on + // MakePolymorphicMatcher() is that the latter is not flexible + // enough for implementing the DescribeTo() method of Pointee(). + template + operator Matcher() const { + return MakeMatcher(new Impl(matcher_)); + } + + private: + // The monomorphic implementation that works for a particular pointer type. + template + class Impl : public MatcherInterface { + public: + typedef typename PointeeOf::type Pointee; + + explicit Impl(const InnerMatcher& matcher) + : matcher_(MatcherCast(matcher)) {} + + virtual void DescribeTo(::std::ostream* os) const { + *os << "points to a value that "; + matcher_.DescribeTo(os); + } + + virtual void DescribeNegationTo(::std::ostream* os) const { + *os << "does not point to a value that "; + matcher_.DescribeTo(os); + } + + virtual bool MatchAndExplain(Pointer pointer, + MatchResultListener* listener) const { + if (GetRawPointer(pointer) == NULL) + return false; + + *listener << "which points to "; + return MatchPrintAndExplain(*pointer, matcher_, listener); + } + + private: + const Matcher matcher_; + + GTEST_DISALLOW_ASSIGN_(Impl); + }; + + const InnerMatcher matcher_; + + GTEST_DISALLOW_ASSIGN_(PointeeMatcher); +}; + +// Implements the Field() matcher for matching a field (i.e. member +// variable) of an object. +template +class FieldMatcher { + public: + FieldMatcher(FieldType Class::*field, + const Matcher& matcher) + : field_(field), matcher_(matcher) {} + + void DescribeTo(::std::ostream* os) const { + *os << "is an object whose given field "; + matcher_.DescribeTo(os); + } + + void DescribeNegationTo(::std::ostream* os) const { + *os << "is an object whose given field "; + matcher_.DescribeNegationTo(os); + } + + template + bool MatchAndExplain(const T& value, MatchResultListener* listener) const { + return MatchAndExplainImpl( + typename ::testing::internal:: + is_pointer::type(), + value, listener); + } + + private: + // The first argument of MatchAndExplainImpl() is needed to help + // Symbian's C++ compiler choose which overload to use. Its type is + // true_type iff the Field() matcher is used to match a pointer. + bool MatchAndExplainImpl(false_type /* is_not_pointer */, const Class& obj, + MatchResultListener* listener) const { + *listener << "whose given field is "; + return MatchPrintAndExplain(obj.*field_, matcher_, listener); + } + + bool MatchAndExplainImpl(true_type /* is_pointer */, const Class* p, + MatchResultListener* listener) const { + if (p == NULL) + return false; + + *listener << "which points to an object "; + // Since *p has a field, it must be a class/struct/union type and + // thus cannot be a pointer. Therefore we pass false_type() as + // the first argument. + return MatchAndExplainImpl(false_type(), *p, listener); + } + + const FieldType Class::*field_; + const Matcher matcher_; + + GTEST_DISALLOW_ASSIGN_(FieldMatcher); +}; + +// Implements the Property() matcher for matching a property +// (i.e. return value of a getter method) of an object. +template +class PropertyMatcher { + public: + // The property may have a reference type, so 'const PropertyType&' + // may cause double references and fail to compile. That's why we + // need GTEST_REFERENCE_TO_CONST, which works regardless of + // PropertyType being a reference or not. + typedef GTEST_REFERENCE_TO_CONST_(PropertyType) RefToConstProperty; + + PropertyMatcher(PropertyType (Class::*property)() const, + const Matcher& matcher) + : property_(property), matcher_(matcher) {} + + void DescribeTo(::std::ostream* os) const { + *os << "is an object whose given property "; + matcher_.DescribeTo(os); + } + + void DescribeNegationTo(::std::ostream* os) const { + *os << "is an object whose given property "; + matcher_.DescribeNegationTo(os); + } + + template + bool MatchAndExplain(const T&value, MatchResultListener* listener) const { + return MatchAndExplainImpl( + typename ::testing::internal:: + is_pointer::type(), + value, listener); + } + + private: + // The first argument of MatchAndExplainImpl() is needed to help + // Symbian's C++ compiler choose which overload to use. Its type is + // true_type iff the Property() matcher is used to match a pointer. + bool MatchAndExplainImpl(false_type /* is_not_pointer */, const Class& obj, + MatchResultListener* listener) const { + *listener << "whose given property is "; + // Cannot pass the return value (for example, int) to MatchPrintAndExplain, + // which takes a non-const reference as argument. + RefToConstProperty result = (obj.*property_)(); + return MatchPrintAndExplain(result, matcher_, listener); + } + + bool MatchAndExplainImpl(true_type /* is_pointer */, const Class* p, + MatchResultListener* listener) const { + if (p == NULL) + return false; + + *listener << "which points to an object "; + // Since *p has a property method, it must be a class/struct/union + // type and thus cannot be a pointer. Therefore we pass + // false_type() as the first argument. + return MatchAndExplainImpl(false_type(), *p, listener); + } + + PropertyType (Class::*property_)() const; + const Matcher matcher_; + + GTEST_DISALLOW_ASSIGN_(PropertyMatcher); +}; + +// Type traits specifying various features of different functors for ResultOf. +// The default template specifies features for functor objects. +// Functor classes have to typedef argument_type and result_type +// to be compatible with ResultOf. +template +struct CallableTraits { + typedef typename Functor::result_type ResultType; + typedef Functor StorageType; + + static void CheckIsValid(Functor /* functor */) {} + template + static ResultType Invoke(Functor f, T arg) { return f(arg); } +}; + +// Specialization for function pointers. +template +struct CallableTraits { + typedef ResType ResultType; + typedef ResType(*StorageType)(ArgType); + + static void CheckIsValid(ResType(*f)(ArgType)) { + GTEST_CHECK_(f != NULL) + << "NULL function pointer is passed into ResultOf()."; + } + template + static ResType Invoke(ResType(*f)(ArgType), T arg) { + return (*f)(arg); + } +}; + +// Implements the ResultOf() matcher for matching a return value of a +// unary function of an object. +template +class ResultOfMatcher { + public: + typedef typename CallableTraits::ResultType ResultType; + + ResultOfMatcher(Callable callable, const Matcher& matcher) + : callable_(callable), matcher_(matcher) { + CallableTraits::CheckIsValid(callable_); + } + + template + operator Matcher() const { + return Matcher(new Impl(callable_, matcher_)); + } + + private: + typedef typename CallableTraits::StorageType CallableStorageType; + + template + class Impl : public MatcherInterface { + public: + Impl(CallableStorageType callable, const Matcher& matcher) + : callable_(callable), matcher_(matcher) {} + + virtual void DescribeTo(::std::ostream* os) const { + *os << "is mapped by the given callable to a value that "; + matcher_.DescribeTo(os); + } + + virtual void DescribeNegationTo(::std::ostream* os) const { + *os << "is mapped by the given callable to a value that "; + matcher_.DescribeNegationTo(os); + } + + virtual bool MatchAndExplain(T obj, MatchResultListener* listener) const { + *listener << "which is mapped by the given callable to "; + // Cannot pass the return value (for example, int) to + // MatchPrintAndExplain, which takes a non-const reference as argument. + ResultType result = + CallableTraits::template Invoke(callable_, obj); + return MatchPrintAndExplain(result, matcher_, listener); + } + + private: + // Functors often define operator() as non-const method even though + // they are actualy stateless. But we need to use them even when + // 'this' is a const pointer. It's the user's responsibility not to + // use stateful callables with ResultOf(), which does't guarantee + // how many times the callable will be invoked. + mutable CallableStorageType callable_; + const Matcher matcher_; + + GTEST_DISALLOW_ASSIGN_(Impl); + }; // class Impl + + const CallableStorageType callable_; + const Matcher matcher_; + + GTEST_DISALLOW_ASSIGN_(ResultOfMatcher); +}; + +// Implements an equality matcher for any STL-style container whose elements +// support ==. This matcher is like Eq(), but its failure explanations provide +// more detailed information that is useful when the container is used as a set. +// The failure message reports elements that are in one of the operands but not +// the other. The failure messages do not report duplicate or out-of-order +// elements in the containers (which don't properly matter to sets, but can +// occur if the containers are vectors or lists, for example). +// +// Uses the container's const_iterator, value_type, operator ==, +// begin(), and end(). +template +class ContainerEqMatcher { + public: + typedef internal::StlContainerView View; + typedef typename View::type StlContainer; + typedef typename View::const_reference StlContainerReference; + + // We make a copy of rhs in case the elements in it are modified + // after this matcher is created. + explicit ContainerEqMatcher(const Container& rhs) : rhs_(View::Copy(rhs)) { + // Makes sure the user doesn't instantiate this class template + // with a const or reference type. + (void)testing::StaticAssertTypeEq(); + } + + void DescribeTo(::std::ostream* os) const { + *os << "equals "; + UniversalPrint(rhs_, os); + } + void DescribeNegationTo(::std::ostream* os) const { + *os << "does not equal "; + UniversalPrint(rhs_, os); + } + + template + bool MatchAndExplain(const LhsContainer& lhs, + MatchResultListener* listener) const { + // GTEST_REMOVE_CONST_() is needed to work around an MSVC 8.0 bug + // that causes LhsContainer to be a const type sometimes. + typedef internal::StlContainerView + LhsView; + typedef typename LhsView::type LhsStlContainer; + StlContainerReference lhs_stl_container = LhsView::ConstReference(lhs); + if (lhs_stl_container == rhs_) + return true; + + ::std::ostream* const os = listener->stream(); + if (os != NULL) { + // Something is different. Check for extra values first. + bool printed_header = false; + for (typename LhsStlContainer::const_iterator it = + lhs_stl_container.begin(); + it != lhs_stl_container.end(); ++it) { + if (internal::ArrayAwareFind(rhs_.begin(), rhs_.end(), *it) == + rhs_.end()) { + if (printed_header) { + *os << ", "; + } else { + *os << "which has these unexpected elements: "; + printed_header = true; + } + UniversalPrint(*it, os); + } + } + + // Now check for missing values. + bool printed_header2 = false; + for (typename StlContainer::const_iterator it = rhs_.begin(); + it != rhs_.end(); ++it) { + if (internal::ArrayAwareFind( + lhs_stl_container.begin(), lhs_stl_container.end(), *it) == + lhs_stl_container.end()) { + if (printed_header2) { + *os << ", "; + } else { + *os << (printed_header ? ",\nand" : "which") + << " doesn't have these expected elements: "; + printed_header2 = true; + } + UniversalPrint(*it, os); + } + } + } + + return false; + } + + private: + const StlContainer rhs_; + + GTEST_DISALLOW_ASSIGN_(ContainerEqMatcher); +}; + +// Implements Pointwise(tuple_matcher, rhs_container). tuple_matcher +// must be able to be safely cast to Matcher >, where T1 and T2 are the types of elements in the LHS +// container and the RHS container respectively. +template +class PointwiseMatcher { + public: + typedef internal::StlContainerView RhsView; + typedef typename RhsView::type RhsStlContainer; + typedef typename RhsStlContainer::value_type RhsValue; + + // Like ContainerEq, we make a copy of rhs in case the elements in + // it are modified after this matcher is created. + PointwiseMatcher(const TupleMatcher& tuple_matcher, const RhsContainer& rhs) + : tuple_matcher_(tuple_matcher), rhs_(RhsView::Copy(rhs)) { + // Makes sure the user doesn't instantiate this class template + // with a const or reference type. + (void)testing::StaticAssertTypeEq(); + } + + template + operator Matcher() const { + return MakeMatcher(new Impl(tuple_matcher_, rhs_)); + } + + template + class Impl : public MatcherInterface { + public: + typedef internal::StlContainerView< + GTEST_REMOVE_REFERENCE_AND_CONST_(LhsContainer)> LhsView; + typedef typename LhsView::type LhsStlContainer; + typedef typename LhsView::const_reference LhsStlContainerReference; + typedef typename LhsStlContainer::value_type LhsValue; + // We pass the LHS value and the RHS value to the inner matcher by + // reference, as they may be expensive to copy. We must use tuple + // instead of pair here, as a pair cannot hold references (C++ 98, + // 20.2.2 [lib.pairs]). + typedef std::tr1::tuple InnerMatcherArg; + + Impl(const TupleMatcher& tuple_matcher, const RhsStlContainer& rhs) + // mono_tuple_matcher_ holds a monomorphic version of the tuple matcher. + : mono_tuple_matcher_(SafeMatcherCast(tuple_matcher)), + rhs_(rhs) {} + + virtual void DescribeTo(::std::ostream* os) const { + *os << "contains " << rhs_.size() + << " values, where each value and its corresponding value in "; + UniversalPrinter::Print(rhs_, os); + *os << " "; + mono_tuple_matcher_.DescribeTo(os); + } + virtual void DescribeNegationTo(::std::ostream* os) const { + *os << "doesn't contain exactly " << rhs_.size() + << " values, or contains a value x at some index i" + << " where x and the i-th value of "; + UniversalPrint(rhs_, os); + *os << " "; + mono_tuple_matcher_.DescribeNegationTo(os); + } + + virtual bool MatchAndExplain(LhsContainer lhs, + MatchResultListener* listener) const { + LhsStlContainerReference lhs_stl_container = LhsView::ConstReference(lhs); + const size_t actual_size = lhs_stl_container.size(); + if (actual_size != rhs_.size()) { + *listener << "which contains " << actual_size << " values"; + return false; + } + + typename LhsStlContainer::const_iterator left = lhs_stl_container.begin(); + typename RhsStlContainer::const_iterator right = rhs_.begin(); + for (size_t i = 0; i != actual_size; ++i, ++left, ++right) { + const InnerMatcherArg value_pair(*left, *right); + + if (listener->IsInterested()) { + StringMatchResultListener inner_listener; + if (!mono_tuple_matcher_.MatchAndExplain( + value_pair, &inner_listener)) { + *listener << "where the value pair ("; + UniversalPrint(*left, listener->stream()); + *listener << ", "; + UniversalPrint(*right, listener->stream()); + *listener << ") at index #" << i << " don't match"; + PrintIfNotEmpty(inner_listener.str(), listener->stream()); + return false; + } + } else { + if (!mono_tuple_matcher_.Matches(value_pair)) + return false; + } + } + + return true; + } + + private: + const Matcher mono_tuple_matcher_; + const RhsStlContainer rhs_; + + GTEST_DISALLOW_ASSIGN_(Impl); + }; + + private: + const TupleMatcher tuple_matcher_; + const RhsStlContainer rhs_; + + GTEST_DISALLOW_ASSIGN_(PointwiseMatcher); +}; + +// Holds the logic common to ContainsMatcherImpl and EachMatcherImpl. +template +class QuantifierMatcherImpl : public MatcherInterface { + public: + typedef GTEST_REMOVE_REFERENCE_AND_CONST_(Container) RawContainer; + typedef StlContainerView View; + typedef typename View::type StlContainer; + typedef typename View::const_reference StlContainerReference; + typedef typename StlContainer::value_type Element; + + template + explicit QuantifierMatcherImpl(InnerMatcher inner_matcher) + : inner_matcher_( + testing::SafeMatcherCast(inner_matcher)) {} + + // Checks whether: + // * All elements in the container match, if all_elements_should_match. + // * Any element in the container matches, if !all_elements_should_match. + bool MatchAndExplainImpl(bool all_elements_should_match, + Container container, + MatchResultListener* listener) const { + StlContainerReference stl_container = View::ConstReference(container); + size_t i = 0; + for (typename StlContainer::const_iterator it = stl_container.begin(); + it != stl_container.end(); ++it, ++i) { + StringMatchResultListener inner_listener; + const bool matches = inner_matcher_.MatchAndExplain(*it, &inner_listener); + + if (matches != all_elements_should_match) { + *listener << "whose element #" << i + << (matches ? " matches" : " doesn't match"); + PrintIfNotEmpty(inner_listener.str(), listener->stream()); + return !all_elements_should_match; + } + } + return all_elements_should_match; + } + + protected: + const Matcher inner_matcher_; + + GTEST_DISALLOW_ASSIGN_(QuantifierMatcherImpl); +}; + +// Implements Contains(element_matcher) for the given argument type Container. +// Symmetric to EachMatcherImpl. +template +class ContainsMatcherImpl : public QuantifierMatcherImpl { + public: + template + explicit ContainsMatcherImpl(InnerMatcher inner_matcher) + : QuantifierMatcherImpl(inner_matcher) {} + + // Describes what this matcher does. + virtual void DescribeTo(::std::ostream* os) const { + *os << "contains at least one element that "; + this->inner_matcher_.DescribeTo(os); + } + + virtual void DescribeNegationTo(::std::ostream* os) const { + *os << "doesn't contain any element that "; + this->inner_matcher_.DescribeTo(os); + } + + virtual bool MatchAndExplain(Container container, + MatchResultListener* listener) const { + return this->MatchAndExplainImpl(false, container, listener); + } + + private: + GTEST_DISALLOW_ASSIGN_(ContainsMatcherImpl); +}; + +// Implements Each(element_matcher) for the given argument type Container. +// Symmetric to ContainsMatcherImpl. +template +class EachMatcherImpl : public QuantifierMatcherImpl { + public: + template + explicit EachMatcherImpl(InnerMatcher inner_matcher) + : QuantifierMatcherImpl(inner_matcher) {} + + // Describes what this matcher does. + virtual void DescribeTo(::std::ostream* os) const { + *os << "only contains elements that "; + this->inner_matcher_.DescribeTo(os); + } + + virtual void DescribeNegationTo(::std::ostream* os) const { + *os << "contains some element that "; + this->inner_matcher_.DescribeNegationTo(os); + } + + virtual bool MatchAndExplain(Container container, + MatchResultListener* listener) const { + return this->MatchAndExplainImpl(true, container, listener); + } + + private: + GTEST_DISALLOW_ASSIGN_(EachMatcherImpl); +}; + +// Implements polymorphic Contains(element_matcher). +template +class ContainsMatcher { + public: + explicit ContainsMatcher(M m) : inner_matcher_(m) {} + + template + operator Matcher() const { + return MakeMatcher(new ContainsMatcherImpl(inner_matcher_)); + } + + private: + const M inner_matcher_; + + GTEST_DISALLOW_ASSIGN_(ContainsMatcher); +}; + +// Implements polymorphic Each(element_matcher). +template +class EachMatcher { + public: + explicit EachMatcher(M m) : inner_matcher_(m) {} + + template + operator Matcher() const { + return MakeMatcher(new EachMatcherImpl(inner_matcher_)); + } + + private: + const M inner_matcher_; + + GTEST_DISALLOW_ASSIGN_(EachMatcher); +}; + +// Implements Key(inner_matcher) for the given argument pair type. +// Key(inner_matcher) matches an std::pair whose 'first' field matches +// inner_matcher. For example, Contains(Key(Ge(5))) can be used to match an +// std::map that contains at least one element whose key is >= 5. +template +class KeyMatcherImpl : public MatcherInterface { + public: + typedef GTEST_REMOVE_REFERENCE_AND_CONST_(PairType) RawPairType; + typedef typename RawPairType::first_type KeyType; + + template + explicit KeyMatcherImpl(InnerMatcher inner_matcher) + : inner_matcher_( + testing::SafeMatcherCast(inner_matcher)) { + } + + // Returns true iff 'key_value.first' (the key) matches the inner matcher. + virtual bool MatchAndExplain(PairType key_value, + MatchResultListener* listener) const { + StringMatchResultListener inner_listener; + const bool match = inner_matcher_.MatchAndExplain(key_value.first, + &inner_listener); + const internal::string explanation = inner_listener.str(); + if (explanation != "") { + *listener << "whose first field is a value " << explanation; + } + return match; + } + + // Describes what this matcher does. + virtual void DescribeTo(::std::ostream* os) const { + *os << "has a key that "; + inner_matcher_.DescribeTo(os); + } + + // Describes what the negation of this matcher does. + virtual void DescribeNegationTo(::std::ostream* os) const { + *os << "doesn't have a key that "; + inner_matcher_.DescribeTo(os); + } + + private: + const Matcher inner_matcher_; + + GTEST_DISALLOW_ASSIGN_(KeyMatcherImpl); +}; + +// Implements polymorphic Key(matcher_for_key). +template +class KeyMatcher { + public: + explicit KeyMatcher(M m) : matcher_for_key_(m) {} + + template + operator Matcher() const { + return MakeMatcher(new KeyMatcherImpl(matcher_for_key_)); + } + + private: + const M matcher_for_key_; + + GTEST_DISALLOW_ASSIGN_(KeyMatcher); +}; + +// Implements Pair(first_matcher, second_matcher) for the given argument pair +// type with its two matchers. See Pair() function below. +template +class PairMatcherImpl : public MatcherInterface { + public: + typedef GTEST_REMOVE_REFERENCE_AND_CONST_(PairType) RawPairType; + typedef typename RawPairType::first_type FirstType; + typedef typename RawPairType::second_type SecondType; + + template + PairMatcherImpl(FirstMatcher first_matcher, SecondMatcher second_matcher) + : first_matcher_( + testing::SafeMatcherCast(first_matcher)), + second_matcher_( + testing::SafeMatcherCast(second_matcher)) { + } + + // Describes what this matcher does. + virtual void DescribeTo(::std::ostream* os) const { + *os << "has a first field that "; + first_matcher_.DescribeTo(os); + *os << ", and has a second field that "; + second_matcher_.DescribeTo(os); + } + + // Describes what the negation of this matcher does. + virtual void DescribeNegationTo(::std::ostream* os) const { + *os << "has a first field that "; + first_matcher_.DescribeNegationTo(os); + *os << ", or has a second field that "; + second_matcher_.DescribeNegationTo(os); + } + + // Returns true iff 'a_pair.first' matches first_matcher and 'a_pair.second' + // matches second_matcher. + virtual bool MatchAndExplain(PairType a_pair, + MatchResultListener* listener) const { + if (!listener->IsInterested()) { + // If the listener is not interested, we don't need to construct the + // explanation. + return first_matcher_.Matches(a_pair.first) && + second_matcher_.Matches(a_pair.second); + } + StringMatchResultListener first_inner_listener; + if (!first_matcher_.MatchAndExplain(a_pair.first, + &first_inner_listener)) { + *listener << "whose first field does not match"; + PrintIfNotEmpty(first_inner_listener.str(), listener->stream()); + return false; + } + StringMatchResultListener second_inner_listener; + if (!second_matcher_.MatchAndExplain(a_pair.second, + &second_inner_listener)) { + *listener << "whose second field does not match"; + PrintIfNotEmpty(second_inner_listener.str(), listener->stream()); + return false; + } + ExplainSuccess(first_inner_listener.str(), second_inner_listener.str(), + listener); + return true; + } + + private: + void ExplainSuccess(const internal::string& first_explanation, + const internal::string& second_explanation, + MatchResultListener* listener) const { + *listener << "whose both fields match"; + if (first_explanation != "") { + *listener << ", where the first field is a value " << first_explanation; + } + if (second_explanation != "") { + *listener << ", "; + if (first_explanation != "") { + *listener << "and "; + } else { + *listener << "where "; + } + *listener << "the second field is a value " << second_explanation; + } + } + + const Matcher first_matcher_; + const Matcher second_matcher_; + + GTEST_DISALLOW_ASSIGN_(PairMatcherImpl); +}; + +// Implements polymorphic Pair(first_matcher, second_matcher). +template +class PairMatcher { + public: + PairMatcher(FirstMatcher first_matcher, SecondMatcher second_matcher) + : first_matcher_(first_matcher), second_matcher_(second_matcher) {} + + template + operator Matcher () const { + return MakeMatcher( + new PairMatcherImpl( + first_matcher_, second_matcher_)); + } + + private: + const FirstMatcher first_matcher_; + const SecondMatcher second_matcher_; + + GTEST_DISALLOW_ASSIGN_(PairMatcher); +}; + +// Implements ElementsAre() and ElementsAreArray(). +template +class ElementsAreMatcherImpl : public MatcherInterface { + public: + typedef GTEST_REMOVE_REFERENCE_AND_CONST_(Container) RawContainer; + typedef internal::StlContainerView View; + typedef typename View::type StlContainer; + typedef typename View::const_reference StlContainerReference; + typedef typename StlContainer::value_type Element; + + // Constructs the matcher from a sequence of element values or + // element matchers. + template + ElementsAreMatcherImpl(InputIter first, size_t a_count) { + matchers_.reserve(a_count); + InputIter it = first; + for (size_t i = 0; i != a_count; ++i, ++it) { + matchers_.push_back(MatcherCast(*it)); + } + } + + // Describes what this matcher does. + virtual void DescribeTo(::std::ostream* os) const { + if (count() == 0) { + *os << "is empty"; + } else if (count() == 1) { + *os << "has 1 element that "; + matchers_[0].DescribeTo(os); + } else { + *os << "has " << Elements(count()) << " where\n"; + for (size_t i = 0; i != count(); ++i) { + *os << "element #" << i << " "; + matchers_[i].DescribeTo(os); + if (i + 1 < count()) { + *os << ",\n"; + } + } + } + } + + // Describes what the negation of this matcher does. + virtual void DescribeNegationTo(::std::ostream* os) const { + if (count() == 0) { + *os << "isn't empty"; + return; + } + + *os << "doesn't have " << Elements(count()) << ", or\n"; + for (size_t i = 0; i != count(); ++i) { + *os << "element #" << i << " "; + matchers_[i].DescribeNegationTo(os); + if (i + 1 < count()) { + *os << ", or\n"; + } + } + } + + virtual bool MatchAndExplain(Container container, + MatchResultListener* listener) const { + StlContainerReference stl_container = View::ConstReference(container); + const size_t actual_count = stl_container.size(); + if (actual_count != count()) { + // The element count doesn't match. If the container is empty, + // there's no need to explain anything as Google Mock already + // prints the empty container. Otherwise we just need to show + // how many elements there actually are. + if (actual_count != 0) { + *listener << "which has " << Elements(actual_count); + } + return false; + } + + typename StlContainer::const_iterator it = stl_container.begin(); + // explanations[i] is the explanation of the element at index i. + std::vector explanations(count()); + for (size_t i = 0; i != count(); ++it, ++i) { + StringMatchResultListener s; + if (matchers_[i].MatchAndExplain(*it, &s)) { + explanations[i] = s.str(); + } else { + // The container has the right size but the i-th element + // doesn't match its expectation. + *listener << "whose element #" << i << " doesn't match"; + PrintIfNotEmpty(s.str(), listener->stream()); + return false; + } + } + + // Every element matches its expectation. We need to explain why + // (the obvious ones can be skipped). + bool reason_printed = false; + for (size_t i = 0; i != count(); ++i) { + const internal::string& s = explanations[i]; + if (!s.empty()) { + if (reason_printed) { + *listener << ",\nand "; + } + *listener << "whose element #" << i << " matches, " << s; + reason_printed = true; + } + } + + return true; + } + + private: + static Message Elements(size_t count) { + return Message() << count << (count == 1 ? " element" : " elements"); + } + + size_t count() const { return matchers_.size(); } + std::vector > matchers_; + + GTEST_DISALLOW_ASSIGN_(ElementsAreMatcherImpl); +}; + +// Implements ElementsAre() of 0 arguments. +class ElementsAreMatcher0 { + public: + ElementsAreMatcher0() {} + + template + operator Matcher() const { + typedef GTEST_REMOVE_REFERENCE_AND_CONST_(Container) RawContainer; + typedef typename internal::StlContainerView::type::value_type + Element; + + const Matcher* const matchers = NULL; + return MakeMatcher(new ElementsAreMatcherImpl(matchers, 0)); + } +}; + +// Implements ElementsAreArray(). +template +class ElementsAreArrayMatcher { + public: + ElementsAreArrayMatcher(const T* first, size_t count) : + first_(first), count_(count) {} + + template + operator Matcher() const { + typedef GTEST_REMOVE_REFERENCE_AND_CONST_(Container) RawContainer; + typedef typename internal::StlContainerView::type::value_type + Element; + + return MakeMatcher(new ElementsAreMatcherImpl(first_, count_)); + } + + private: + const T* const first_; + const size_t count_; + + GTEST_DISALLOW_ASSIGN_(ElementsAreArrayMatcher); +}; + +// Returns the description for a matcher defined using the MATCHER*() +// macro where the user-supplied description string is "", if +// 'negation' is false; otherwise returns the description of the +// negation of the matcher. 'param_values' contains a list of strings +// that are the print-out of the matcher's parameters. +string FormatMatcherDescription(bool negation, const char* matcher_name, + const Strings& param_values); + +} // namespace internal + +// Implements MatcherCast(). +template +inline Matcher MatcherCast(M matcher) { + return internal::MatcherCastImpl::Cast(matcher); +} + +// _ is a matcher that matches anything of any type. +// +// This definition is fine as: +// +// 1. The C++ standard permits using the name _ in a namespace that +// is not the global namespace or ::std. +// 2. The AnythingMatcher class has no data member or constructor, +// so it's OK to create global variables of this type. +// 3. c-style has approved of using _ in this case. +const internal::AnythingMatcher _ = {}; +// Creates a matcher that matches any value of the given type T. +template +inline Matcher A() { return MakeMatcher(new internal::AnyMatcherImpl()); } + +// Creates a matcher that matches any value of the given type T. +template +inline Matcher An() { return A(); } + +// Creates a polymorphic matcher that matches anything equal to x. +// Note: if the parameter of Eq() were declared as const T&, Eq("foo") +// wouldn't compile. +template +inline internal::EqMatcher Eq(T x) { return internal::EqMatcher(x); } + +// Constructs a Matcher from a 'value' of type T. The constructed +// matcher matches any value that's equal to 'value'. +template +Matcher::Matcher(T value) { *this = Eq(value); } + +// Creates a monomorphic matcher that matches anything with type Lhs +// and equal to rhs. A user may need to use this instead of Eq(...) +// in order to resolve an overloading ambiguity. +// +// TypedEq(x) is just a convenient short-hand for Matcher(Eq(x)) +// or Matcher(x), but more readable than the latter. +// +// We could define similar monomorphic matchers for other comparison +// operations (e.g. TypedLt, TypedGe, and etc), but decided not to do +// it yet as those are used much less than Eq() in practice. A user +// can always write Matcher(Lt(5)) to be explicit about the type, +// for example. +template +inline Matcher TypedEq(const Rhs& rhs) { return Eq(rhs); } + +// Creates a polymorphic matcher that matches anything >= x. +template +inline internal::GeMatcher Ge(Rhs x) { + return internal::GeMatcher(x); +} + +// Creates a polymorphic matcher that matches anything > x. +template +inline internal::GtMatcher Gt(Rhs x) { + return internal::GtMatcher(x); +} + +// Creates a polymorphic matcher that matches anything <= x. +template +inline internal::LeMatcher Le(Rhs x) { + return internal::LeMatcher(x); +} + +// Creates a polymorphic matcher that matches anything < x. +template +inline internal::LtMatcher Lt(Rhs x) { + return internal::LtMatcher(x); +} + +// Creates a polymorphic matcher that matches anything != x. +template +inline internal::NeMatcher Ne(Rhs x) { + return internal::NeMatcher(x); +} + +// Creates a polymorphic matcher that matches any NULL pointer. +inline PolymorphicMatcher IsNull() { + return MakePolymorphicMatcher(internal::IsNullMatcher()); +} + +// Creates a polymorphic matcher that matches any non-NULL pointer. +// This is convenient as Not(NULL) doesn't compile (the compiler +// thinks that that expression is comparing a pointer with an integer). +inline PolymorphicMatcher NotNull() { + return MakePolymorphicMatcher(internal::NotNullMatcher()); +} + +// Creates a polymorphic matcher that matches any argument that +// references variable x. +template +inline internal::RefMatcher Ref(T& x) { // NOLINT + return internal::RefMatcher(x); +} + +// Creates a matcher that matches any double argument approximately +// equal to rhs, where two NANs are considered unequal. +inline internal::FloatingEqMatcher DoubleEq(double rhs) { + return internal::FloatingEqMatcher(rhs, false); +} + +// Creates a matcher that matches any double argument approximately +// equal to rhs, including NaN values when rhs is NaN. +inline internal::FloatingEqMatcher NanSensitiveDoubleEq(double rhs) { + return internal::FloatingEqMatcher(rhs, true); +} + +// Creates a matcher that matches any float argument approximately +// equal to rhs, where two NANs are considered unequal. +inline internal::FloatingEqMatcher FloatEq(float rhs) { + return internal::FloatingEqMatcher(rhs, false); +} + +// Creates a matcher that matches any double argument approximately +// equal to rhs, including NaN values when rhs is NaN. +inline internal::FloatingEqMatcher NanSensitiveFloatEq(float rhs) { + return internal::FloatingEqMatcher(rhs, true); +} + +// Creates a matcher that matches a pointer (raw or smart) that points +// to a value that matches inner_matcher. +template +inline internal::PointeeMatcher Pointee( + const InnerMatcher& inner_matcher) { + return internal::PointeeMatcher(inner_matcher); +} + +// Creates a matcher that matches an object whose given field matches +// 'matcher'. For example, +// Field(&Foo::number, Ge(5)) +// matches a Foo object x iff x.number >= 5. +template +inline PolymorphicMatcher< + internal::FieldMatcher > Field( + FieldType Class::*field, const FieldMatcher& matcher) { + return MakePolymorphicMatcher( + internal::FieldMatcher( + field, MatcherCast(matcher))); + // The call to MatcherCast() is required for supporting inner + // matchers of compatible types. For example, it allows + // Field(&Foo::bar, m) + // to compile where bar is an int32 and m is a matcher for int64. +} + +// Creates a matcher that matches an object whose given property +// matches 'matcher'. For example, +// Property(&Foo::str, StartsWith("hi")) +// matches a Foo object x iff x.str() starts with "hi". +template +inline PolymorphicMatcher< + internal::PropertyMatcher > Property( + PropertyType (Class::*property)() const, const PropertyMatcher& matcher) { + return MakePolymorphicMatcher( + internal::PropertyMatcher( + property, + MatcherCast(matcher))); + // The call to MatcherCast() is required for supporting inner + // matchers of compatible types. For example, it allows + // Property(&Foo::bar, m) + // to compile where bar() returns an int32 and m is a matcher for int64. +} + +// Creates a matcher that matches an object iff the result of applying +// a callable to x matches 'matcher'. +// For example, +// ResultOf(f, StartsWith("hi")) +// matches a Foo object x iff f(x) starts with "hi". +// callable parameter can be a function, function pointer, or a functor. +// Callable has to satisfy the following conditions: +// * It is required to keep no state affecting the results of +// the calls on it and make no assumptions about how many calls +// will be made. Any state it keeps must be protected from the +// concurrent access. +// * If it is a function object, it has to define type result_type. +// We recommend deriving your functor classes from std::unary_function. +template +internal::ResultOfMatcher ResultOf( + Callable callable, const ResultOfMatcher& matcher) { + return internal::ResultOfMatcher( + callable, + MatcherCast::ResultType>( + matcher)); + // The call to MatcherCast() is required for supporting inner + // matchers of compatible types. For example, it allows + // ResultOf(Function, m) + // to compile where Function() returns an int32 and m is a matcher for int64. +} + +// String matchers. + +// Matches a string equal to str. +inline PolymorphicMatcher > + StrEq(const internal::string& str) { + return MakePolymorphicMatcher(internal::StrEqualityMatcher( + str, true, true)); +} + +// Matches a string not equal to str. +inline PolymorphicMatcher > + StrNe(const internal::string& str) { + return MakePolymorphicMatcher(internal::StrEqualityMatcher( + str, false, true)); +} + +// Matches a string equal to str, ignoring case. +inline PolymorphicMatcher > + StrCaseEq(const internal::string& str) { + return MakePolymorphicMatcher(internal::StrEqualityMatcher( + str, true, false)); +} + +// Matches a string not equal to str, ignoring case. +inline PolymorphicMatcher > + StrCaseNe(const internal::string& str) { + return MakePolymorphicMatcher(internal::StrEqualityMatcher( + str, false, false)); +} + +// Creates a matcher that matches any string, std::string, or C string +// that contains the given substring. +inline PolymorphicMatcher > + HasSubstr(const internal::string& substring) { + return MakePolymorphicMatcher(internal::HasSubstrMatcher( + substring)); +} + +// Matches a string that starts with 'prefix' (case-sensitive). +inline PolymorphicMatcher > + StartsWith(const internal::string& prefix) { + return MakePolymorphicMatcher(internal::StartsWithMatcher( + prefix)); +} + +// Matches a string that ends with 'suffix' (case-sensitive). +inline PolymorphicMatcher > + EndsWith(const internal::string& suffix) { + return MakePolymorphicMatcher(internal::EndsWithMatcher( + suffix)); +} + +// Matches a string that fully matches regular expression 'regex'. +// The matcher takes ownership of 'regex'. +inline PolymorphicMatcher MatchesRegex( + const internal::RE* regex) { + return MakePolymorphicMatcher(internal::MatchesRegexMatcher(regex, true)); +} +inline PolymorphicMatcher MatchesRegex( + const internal::string& regex) { + return MatchesRegex(new internal::RE(regex)); +} + +// Matches a string that contains regular expression 'regex'. +// The matcher takes ownership of 'regex'. +inline PolymorphicMatcher ContainsRegex( + const internal::RE* regex) { + return MakePolymorphicMatcher(internal::MatchesRegexMatcher(regex, false)); +} +inline PolymorphicMatcher ContainsRegex( + const internal::string& regex) { + return ContainsRegex(new internal::RE(regex)); +} + +#if GTEST_HAS_GLOBAL_WSTRING || GTEST_HAS_STD_WSTRING +// Wide string matchers. + +// Matches a string equal to str. +inline PolymorphicMatcher > + StrEq(const internal::wstring& str) { + return MakePolymorphicMatcher(internal::StrEqualityMatcher( + str, true, true)); +} + +// Matches a string not equal to str. +inline PolymorphicMatcher > + StrNe(const internal::wstring& str) { + return MakePolymorphicMatcher(internal::StrEqualityMatcher( + str, false, true)); +} + +// Matches a string equal to str, ignoring case. +inline PolymorphicMatcher > + StrCaseEq(const internal::wstring& str) { + return MakePolymorphicMatcher(internal::StrEqualityMatcher( + str, true, false)); +} + +// Matches a string not equal to str, ignoring case. +inline PolymorphicMatcher > + StrCaseNe(const internal::wstring& str) { + return MakePolymorphicMatcher(internal::StrEqualityMatcher( + str, false, false)); +} + +// Creates a matcher that matches any wstring, std::wstring, or C wide string +// that contains the given substring. +inline PolymorphicMatcher > + HasSubstr(const internal::wstring& substring) { + return MakePolymorphicMatcher(internal::HasSubstrMatcher( + substring)); +} + +// Matches a string that starts with 'prefix' (case-sensitive). +inline PolymorphicMatcher > + StartsWith(const internal::wstring& prefix) { + return MakePolymorphicMatcher(internal::StartsWithMatcher( + prefix)); +} + +// Matches a string that ends with 'suffix' (case-sensitive). +inline PolymorphicMatcher > + EndsWith(const internal::wstring& suffix) { + return MakePolymorphicMatcher(internal::EndsWithMatcher( + suffix)); +} + +#endif // GTEST_HAS_GLOBAL_WSTRING || GTEST_HAS_STD_WSTRING + +// Creates a polymorphic matcher that matches a 2-tuple where the +// first field == the second field. +inline internal::Eq2Matcher Eq() { return internal::Eq2Matcher(); } + +// Creates a polymorphic matcher that matches a 2-tuple where the +// first field >= the second field. +inline internal::Ge2Matcher Ge() { return internal::Ge2Matcher(); } + +// Creates a polymorphic matcher that matches a 2-tuple where the +// first field > the second field. +inline internal::Gt2Matcher Gt() { return internal::Gt2Matcher(); } + +// Creates a polymorphic matcher that matches a 2-tuple where the +// first field <= the second field. +inline internal::Le2Matcher Le() { return internal::Le2Matcher(); } + +// Creates a polymorphic matcher that matches a 2-tuple where the +// first field < the second field. +inline internal::Lt2Matcher Lt() { return internal::Lt2Matcher(); } + +// Creates a polymorphic matcher that matches a 2-tuple where the +// first field != the second field. +inline internal::Ne2Matcher Ne() { return internal::Ne2Matcher(); } + +// Creates a matcher that matches any value of type T that m doesn't +// match. +template +inline internal::NotMatcher Not(InnerMatcher m) { + return internal::NotMatcher(m); +} + +// Returns a matcher that matches anything that satisfies the given +// predicate. The predicate can be any unary function or functor +// whose return type can be implicitly converted to bool. +template +inline PolymorphicMatcher > +Truly(Predicate pred) { + return MakePolymorphicMatcher(internal::TrulyMatcher(pred)); +} + +// Returns a matcher that matches an equal container. +// This matcher behaves like Eq(), but in the event of mismatch lists the +// values that are included in one container but not the other. (Duplicate +// values and order differences are not explained.) +template +inline PolymorphicMatcher > + ContainerEq(const Container& rhs) { + // This following line is for working around a bug in MSVC 8.0, + // which causes Container to be a const type sometimes. + typedef GTEST_REMOVE_CONST_(Container) RawContainer; + return MakePolymorphicMatcher( + internal::ContainerEqMatcher(rhs)); +} + +// Matches an STL-style container or a native array that contains the +// same number of elements as in rhs, where its i-th element and rhs's +// i-th element (as a pair) satisfy the given pair matcher, for all i. +// TupleMatcher must be able to be safely cast to Matcher >, where T1 and T2 are the types of elements in the +// LHS container and the RHS container respectively. +template +inline internal::PointwiseMatcher +Pointwise(const TupleMatcher& tuple_matcher, const Container& rhs) { + // This following line is for working around a bug in MSVC 8.0, + // which causes Container to be a const type sometimes. + typedef GTEST_REMOVE_CONST_(Container) RawContainer; + return internal::PointwiseMatcher( + tuple_matcher, rhs); +} + +// Matches an STL-style container or a native array that contains at +// least one element matching the given value or matcher. +// +// Examples: +// ::std::set page_ids; +// page_ids.insert(3); +// page_ids.insert(1); +// EXPECT_THAT(page_ids, Contains(1)); +// EXPECT_THAT(page_ids, Contains(Gt(2))); +// EXPECT_THAT(page_ids, Not(Contains(4))); +// +// ::std::map page_lengths; +// page_lengths[1] = 100; +// EXPECT_THAT(page_lengths, +// Contains(::std::pair(1, 100))); +// +// const char* user_ids[] = { "joe", "mike", "tom" }; +// EXPECT_THAT(user_ids, Contains(Eq(::std::string("tom")))); +template +inline internal::ContainsMatcher Contains(M matcher) { + return internal::ContainsMatcher(matcher); +} + +// Matches an STL-style container or a native array that contains only +// elements matching the given value or matcher. +// +// Each(m) is semantically equivalent to Not(Contains(Not(m))). Only +// the messages are different. +// +// Examples: +// ::std::set page_ids; +// // Each(m) matches an empty container, regardless of what m is. +// EXPECT_THAT(page_ids, Each(Eq(1))); +// EXPECT_THAT(page_ids, Each(Eq(77))); +// +// page_ids.insert(3); +// EXPECT_THAT(page_ids, Each(Gt(0))); +// EXPECT_THAT(page_ids, Not(Each(Gt(4)))); +// page_ids.insert(1); +// EXPECT_THAT(page_ids, Not(Each(Lt(2)))); +// +// ::std::map page_lengths; +// page_lengths[1] = 100; +// page_lengths[2] = 200; +// page_lengths[3] = 300; +// EXPECT_THAT(page_lengths, Not(Each(Pair(1, 100)))); +// EXPECT_THAT(page_lengths, Each(Key(Le(3)))); +// +// const char* user_ids[] = { "joe", "mike", "tom" }; +// EXPECT_THAT(user_ids, Not(Each(Eq(::std::string("tom"))))); +template +inline internal::EachMatcher Each(M matcher) { + return internal::EachMatcher(matcher); +} + +// Key(inner_matcher) matches an std::pair whose 'first' field matches +// inner_matcher. For example, Contains(Key(Ge(5))) can be used to match an +// std::map that contains at least one element whose key is >= 5. +template +inline internal::KeyMatcher Key(M inner_matcher) { + return internal::KeyMatcher(inner_matcher); +} + +// Pair(first_matcher, second_matcher) matches a std::pair whose 'first' field +// matches first_matcher and whose 'second' field matches second_matcher. For +// example, EXPECT_THAT(map_type, ElementsAre(Pair(Ge(5), "foo"))) can be used +// to match a std::map that contains exactly one element whose key +// is >= 5 and whose value equals "foo". +template +inline internal::PairMatcher +Pair(FirstMatcher first_matcher, SecondMatcher second_matcher) { + return internal::PairMatcher( + first_matcher, second_matcher); +} + +// Returns a predicate that is satisfied by anything that matches the +// given matcher. +template +inline internal::MatcherAsPredicate Matches(M matcher) { + return internal::MatcherAsPredicate(matcher); +} + +// Returns true iff the value matches the matcher. +template +inline bool Value(const T& value, M matcher) { + return testing::Matches(matcher)(value); +} + +// Matches the value against the given matcher and explains the match +// result to listener. +template +inline bool ExplainMatchResult( + M matcher, const T& value, MatchResultListener* listener) { + return SafeMatcherCast(matcher).MatchAndExplain(value, listener); +} + +// AllArgs(m) is a synonym of m. This is useful in +// +// EXPECT_CALL(foo, Bar(_, _)).With(AllArgs(Eq())); +// +// which is easier to read than +// +// EXPECT_CALL(foo, Bar(_, _)).With(Eq()); +template +inline InnerMatcher AllArgs(const InnerMatcher& matcher) { return matcher; } + +// These macros allow using matchers to check values in Google Test +// tests. ASSERT_THAT(value, matcher) and EXPECT_THAT(value, matcher) +// succeed iff the value matches the matcher. If the assertion fails, +// the value and the description of the matcher will be printed. +#define ASSERT_THAT(value, matcher) ASSERT_PRED_FORMAT1(\ + ::testing::internal::MakePredicateFormatterFromMatcher(matcher), value) +#define EXPECT_THAT(value, matcher) EXPECT_PRED_FORMAT1(\ + ::testing::internal::MakePredicateFormatterFromMatcher(matcher), value) + +} // namespace testing + +#endif // GMOCK_INCLUDE_GMOCK_GMOCK_MATCHERS_H_ diff --git a/libwvdrmengine/test/gmock/include/gmock/gmock-more-actions.h b/libwvdrmengine/test/gmock/include/gmock/gmock-more-actions.h new file mode 100644 index 00000000..fc5e5ca8 --- /dev/null +++ b/libwvdrmengine/test/gmock/include/gmock/gmock-more-actions.h @@ -0,0 +1,233 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file implements some actions that depend on gmock-generated-actions.h. + +#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_MORE_ACTIONS_H_ +#define GMOCK_INCLUDE_GMOCK_GMOCK_MORE_ACTIONS_H_ + +#include + +#include "gmock/gmock-generated-actions.h" + +namespace testing { +namespace internal { + +// Implements the Invoke(f) action. The template argument +// FunctionImpl is the implementation type of f, which can be either a +// function pointer or a functor. Invoke(f) can be used as an +// Action as long as f's type is compatible with F (i.e. f can be +// assigned to a tr1::function). +template +class InvokeAction { + public: + // The c'tor makes a copy of function_impl (either a function + // pointer or a functor). + explicit InvokeAction(FunctionImpl function_impl) + : function_impl_(function_impl) {} + + template + Result Perform(const ArgumentTuple& args) { + return InvokeHelper::Invoke(function_impl_, args); + } + + private: + FunctionImpl function_impl_; + + GTEST_DISALLOW_ASSIGN_(InvokeAction); +}; + +// Implements the Invoke(object_ptr, &Class::Method) action. +template +class InvokeMethodAction { + public: + InvokeMethodAction(Class* obj_ptr, MethodPtr method_ptr) + : obj_ptr_(obj_ptr), method_ptr_(method_ptr) {} + + template + Result Perform(const ArgumentTuple& args) const { + return InvokeHelper::InvokeMethod( + obj_ptr_, method_ptr_, args); + } + + private: + Class* const obj_ptr_; + const MethodPtr method_ptr_; + + GTEST_DISALLOW_ASSIGN_(InvokeMethodAction); +}; + +} // namespace internal + +// Various overloads for Invoke(). + +// Creates an action that invokes 'function_impl' with the mock +// function's arguments. +template +PolymorphicAction > Invoke( + FunctionImpl function_impl) { + return MakePolymorphicAction( + internal::InvokeAction(function_impl)); +} + +// Creates an action that invokes the given method on the given object +// with the mock function's arguments. +template +PolymorphicAction > Invoke( + Class* obj_ptr, MethodPtr method_ptr) { + return MakePolymorphicAction( + internal::InvokeMethodAction(obj_ptr, method_ptr)); +} + +// WithoutArgs(inner_action) can be used in a mock function with a +// non-empty argument list to perform inner_action, which takes no +// argument. In other words, it adapts an action accepting no +// argument to one that accepts (and ignores) arguments. +template +inline internal::WithArgsAction +WithoutArgs(const InnerAction& action) { + return internal::WithArgsAction(action); +} + +// WithArg(an_action) creates an action that passes the k-th +// (0-based) argument of the mock function to an_action and performs +// it. It adapts an action accepting one argument to one that accepts +// multiple arguments. For convenience, we also provide +// WithArgs(an_action) (defined below) as a synonym. +template +inline internal::WithArgsAction +WithArg(const InnerAction& action) { + return internal::WithArgsAction(action); +} + +// The ACTION*() macros trigger warning C4100 (unreferenced formal +// parameter) in MSVC with -W4. Unfortunately they cannot be fixed in +// the macro definition, as the warnings are generated when the macro +// is expanded and macro expansion cannot contain #pragma. Therefore +// we suppress them here. +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable:4100) +#endif + +// Action ReturnArg() returns the k-th argument of the mock function. +ACTION_TEMPLATE(ReturnArg, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_0_VALUE_PARAMS()) { + return std::tr1::get(args); +} + +// Action SaveArg(pointer) saves the k-th (0-based) argument of the +// mock function to *pointer. +ACTION_TEMPLATE(SaveArg, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_1_VALUE_PARAMS(pointer)) { + *pointer = ::std::tr1::get(args); +} + +// Action SaveArgPointee(pointer) saves the value pointed to +// by the k-th (0-based) argument of the mock function to *pointer. +ACTION_TEMPLATE(SaveArgPointee, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_1_VALUE_PARAMS(pointer)) { + *pointer = *::std::tr1::get(args); +} + +// Action SetArgReferee(value) assigns 'value' to the variable +// referenced by the k-th (0-based) argument of the mock function. +ACTION_TEMPLATE(SetArgReferee, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_1_VALUE_PARAMS(value)) { + typedef typename ::std::tr1::tuple_element::type argk_type; + // Ensures that argument #k is a reference. If you get a compiler + // error on the next line, you are using SetArgReferee(value) in + // a mock function whose k-th (0-based) argument is not a reference. + GTEST_COMPILE_ASSERT_(internal::is_reference::value, + SetArgReferee_must_be_used_with_a_reference_argument); + ::std::tr1::get(args) = value; +} + +// Action SetArrayArgument(first, last) copies the elements in +// source range [first, last) to the array pointed to by the k-th +// (0-based) argument, which can be either a pointer or an +// iterator. The action does not take ownership of the elements in the +// source range. +ACTION_TEMPLATE(SetArrayArgument, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_2_VALUE_PARAMS(first, last)) { + // Microsoft compiler deprecates ::std::copy, so we want to suppress warning + // 4996 (Function call with parameters that may be unsafe) there. +#ifdef _MSC_VER +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4996) // Temporarily disables warning 4996. +#endif + ::std::copy(first, last, ::std::tr1::get(args)); +#ifdef _MSC_VER +# pragma warning(pop) // Restores the warning state. +#endif +} + +// Action DeleteArg() deletes the k-th (0-based) argument of the mock +// function. +ACTION_TEMPLATE(DeleteArg, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_0_VALUE_PARAMS()) { + delete ::std::tr1::get(args); +} + +// This action returns the value pointed to by 'pointer'. +ACTION_P(ReturnPointee, pointer) { return *pointer; } + +// Action Throw(exception) can be used in a mock function of any type +// to throw the given exception. Any copyable value can be thrown. +#if GTEST_HAS_EXCEPTIONS + +// Suppresses the 'unreachable code' warning that VC generates in opt modes. +# ifdef _MSC_VER +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4702) // Temporarily disables warning 4702. +# endif +ACTION_P(Throw, exception) { throw exception; } +# ifdef _MSC_VER +# pragma warning(pop) // Restores the warning state. +# endif + +#endif // GTEST_HAS_EXCEPTIONS + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + +} // namespace testing + +#endif // GMOCK_INCLUDE_GMOCK_GMOCK_MORE_ACTIONS_H_ diff --git a/libwvdrmengine/test/gmock/include/gmock/gmock-spec-builders.h b/libwvdrmengine/test/gmock/include/gmock/gmock-spec-builders.h new file mode 100644 index 00000000..400d4d71 --- /dev/null +++ b/libwvdrmengine/test/gmock/include/gmock/gmock-spec-builders.h @@ -0,0 +1,1749 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file implements the ON_CALL() and EXPECT_CALL() macros. +// +// A user can use the ON_CALL() macro to specify the default action of +// a mock method. The syntax is: +// +// ON_CALL(mock_object, Method(argument-matchers)) +// .With(multi-argument-matcher) +// .WillByDefault(action); +// +// where the .With() clause is optional. +// +// A user can use the EXPECT_CALL() macro to specify an expectation on +// a mock method. The syntax is: +// +// EXPECT_CALL(mock_object, Method(argument-matchers)) +// .With(multi-argument-matchers) +// .Times(cardinality) +// .InSequence(sequences) +// .After(expectations) +// .WillOnce(action) +// .WillRepeatedly(action) +// .RetiresOnSaturation(); +// +// where all clauses are optional, and .InSequence()/.After()/ +// .WillOnce() can appear any number of times. + +#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_SPEC_BUILDERS_H_ +#define GMOCK_INCLUDE_GMOCK_GMOCK_SPEC_BUILDERS_H_ + +#include +#include +#include +#include +#include + +#include "gmock/gmock-actions.h" +#include "gmock/gmock-cardinalities.h" +#include "gmock/gmock-matchers.h" +#include "gmock/internal/gmock-internal-utils.h" +#include "gmock/internal/gmock-port.h" +#include "gtest/gtest.h" + +namespace testing { + +// An abstract handle of an expectation. +class Expectation; + +// A set of expectation handles. +class ExpectationSet; + +// Anything inside the 'internal' namespace IS INTERNAL IMPLEMENTATION +// and MUST NOT BE USED IN USER CODE!!! +namespace internal { + +// Implements a mock function. +template class FunctionMocker; + +// Base class for expectations. +class ExpectationBase; + +// Implements an expectation. +template class TypedExpectation; + +// Helper class for testing the Expectation class template. +class ExpectationTester; + +// Base class for function mockers. +template class FunctionMockerBase; + +// Protects the mock object registry (in class Mock), all function +// mockers, and all expectations. +// +// The reason we don't use more fine-grained protection is: when a +// mock function Foo() is called, it needs to consult its expectations +// to see which one should be picked. If another thread is allowed to +// call a mock function (either Foo() or a different one) at the same +// time, it could affect the "retired" attributes of Foo()'s +// expectations when InSequence() is used, and thus affect which +// expectation gets picked. Therefore, we sequence all mock function +// calls to ensure the integrity of the mock objects' states. +GTEST_DECLARE_STATIC_MUTEX_(g_gmock_mutex); + +// Untyped base class for ActionResultHolder. +class UntypedActionResultHolderBase; + +// Abstract base class of FunctionMockerBase. This is the +// type-agnostic part of the function mocker interface. Its pure +// virtual methods are implemented by FunctionMockerBase. +class UntypedFunctionMockerBase { + public: + UntypedFunctionMockerBase(); + virtual ~UntypedFunctionMockerBase(); + + // Verifies that all expectations on this mock function have been + // satisfied. Reports one or more Google Test non-fatal failures + // and returns false if not. + // L >= g_gmock_mutex + bool VerifyAndClearExpectationsLocked(); + + // Clears the ON_CALL()s set on this mock function. + // L >= g_gmock_mutex + virtual void ClearDefaultActionsLocked() = 0; + + // In all of the following Untyped* functions, it's the caller's + // responsibility to guarantee the correctness of the arguments' + // types. + + // Performs the default action with the given arguments and returns + // the action's result. The call description string will be used in + // the error message to describe the call in the case the default + // action fails. + // L = * + virtual UntypedActionResultHolderBase* UntypedPerformDefaultAction( + const void* untyped_args, + const string& call_description) const = 0; + + // Performs the given action with the given arguments and returns + // the action's result. + // L = * + virtual UntypedActionResultHolderBase* UntypedPerformAction( + const void* untyped_action, + const void* untyped_args) const = 0; + + // Writes a message that the call is uninteresting (i.e. neither + // explicitly expected nor explicitly unexpected) to the given + // ostream. + // L < g_gmock_mutex + virtual void UntypedDescribeUninterestingCall(const void* untyped_args, + ::std::ostream* os) const = 0; + + // Returns the expectation that matches the given function arguments + // (or NULL is there's no match); when a match is found, + // untyped_action is set to point to the action that should be + // performed (or NULL if the action is "do default"), and + // is_excessive is modified to indicate whether the call exceeds the + // expected number. + // L < g_gmock_mutex + virtual const ExpectationBase* UntypedFindMatchingExpectation( + const void* untyped_args, + const void** untyped_action, bool* is_excessive, + ::std::ostream* what, ::std::ostream* why) = 0; + + // Prints the given function arguments to the ostream. + virtual void UntypedPrintArgs(const void* untyped_args, + ::std::ostream* os) const = 0; + + // Sets the mock object this mock method belongs to, and registers + // this information in the global mock registry. Will be called + // whenever an EXPECT_CALL() or ON_CALL() is executed on this mock + // method. + // TODO(wan@google.com): rename to SetAndRegisterOwner(). + // L < g_gmock_mutex + void RegisterOwner(const void* mock_obj); + + // Sets the mock object this mock method belongs to, and sets the + // name of the mock function. Will be called upon each invocation + // of this mock function. + // L < g_gmock_mutex + void SetOwnerAndName(const void* mock_obj, const char* name); + + // Returns the mock object this mock method belongs to. Must be + // called after RegisterOwner() or SetOwnerAndName() has been + // called. + // L < g_gmock_mutex + const void* MockObject() const; + + // Returns the name of this mock method. Must be called after + // SetOwnerAndName() has been called. + // L < g_gmock_mutex + const char* Name() const; + + // Returns the result of invoking this mock function with the given + // arguments. This function can be safely called from multiple + // threads concurrently. The caller is responsible for deleting the + // result. + // L < g_gmock_mutex + const UntypedActionResultHolderBase* UntypedInvokeWith( + const void* untyped_args); + + protected: + typedef std::vector UntypedOnCallSpecs; + + typedef std::vector > + UntypedExpectations; + + // Returns an Expectation object that references and co-owns exp, + // which must be an expectation on this mock function. + Expectation GetHandleOf(ExpectationBase* exp); + + // Address of the mock object this mock method belongs to. Only + // valid after this mock method has been called or + // ON_CALL/EXPECT_CALL has been invoked on it. + const void* mock_obj_; // Protected by g_gmock_mutex. + + // Name of the function being mocked. Only valid after this mock + // method has been called. + const char* name_; // Protected by g_gmock_mutex. + + // All default action specs for this function mocker. + UntypedOnCallSpecs untyped_on_call_specs_; + + // All expectations for this function mocker. + UntypedExpectations untyped_expectations_; +}; // class UntypedFunctionMockerBase + +// Untyped base class for OnCallSpec. +class UntypedOnCallSpecBase { + public: + // The arguments are the location of the ON_CALL() statement. + UntypedOnCallSpecBase(const char* a_file, int a_line) + : file_(a_file), line_(a_line), last_clause_(kNone) {} + + // Where in the source file was the default action spec defined? + const char* file() const { return file_; } + int line() const { return line_; } + + protected: + // Gives each clause in the ON_CALL() statement a name. + enum Clause { + // Do not change the order of the enum members! The run-time + // syntax checking relies on it. + kNone, + kWith, + kWillByDefault + }; + + // Asserts that the ON_CALL() statement has a certain property. + void AssertSpecProperty(bool property, const string& failure_message) const { + Assert(property, file_, line_, failure_message); + } + + // Expects that the ON_CALL() statement has a certain property. + void ExpectSpecProperty(bool property, const string& failure_message) const { + Expect(property, file_, line_, failure_message); + } + + const char* file_; + int line_; + + // The last clause in the ON_CALL() statement as seen so far. + // Initially kNone and changes as the statement is parsed. + Clause last_clause_; +}; // class UntypedOnCallSpecBase + +// This template class implements an ON_CALL spec. +template +class OnCallSpec : public UntypedOnCallSpecBase { + public: + typedef typename Function::ArgumentTuple ArgumentTuple; + typedef typename Function::ArgumentMatcherTuple ArgumentMatcherTuple; + + // Constructs an OnCallSpec object from the information inside + // the parenthesis of an ON_CALL() statement. + OnCallSpec(const char* a_file, int a_line, + const ArgumentMatcherTuple& matchers) + : UntypedOnCallSpecBase(a_file, a_line), + matchers_(matchers), + // By default, extra_matcher_ should match anything. However, + // we cannot initialize it with _ as that triggers a compiler + // bug in Symbian's C++ compiler (cannot decide between two + // overloaded constructors of Matcher). + extra_matcher_(A()) { + } + + // Implements the .With() clause. + OnCallSpec& With(const Matcher& m) { + // Makes sure this is called at most once. + ExpectSpecProperty(last_clause_ < kWith, + ".With() cannot appear " + "more than once in an ON_CALL()."); + last_clause_ = kWith; + + extra_matcher_ = m; + return *this; + } + + // Implements the .WillByDefault() clause. + OnCallSpec& WillByDefault(const Action& action) { + ExpectSpecProperty(last_clause_ < kWillByDefault, + ".WillByDefault() must appear " + "exactly once in an ON_CALL()."); + last_clause_ = kWillByDefault; + + ExpectSpecProperty(!action.IsDoDefault(), + "DoDefault() cannot be used in ON_CALL()."); + action_ = action; + return *this; + } + + // Returns true iff the given arguments match the matchers. + bool Matches(const ArgumentTuple& args) const { + return TupleMatches(matchers_, args) && extra_matcher_.Matches(args); + } + + // Returns the action specified by the user. + const Action& GetAction() const { + AssertSpecProperty(last_clause_ == kWillByDefault, + ".WillByDefault() must appear exactly " + "once in an ON_CALL()."); + return action_; + } + + private: + // The information in statement + // + // ON_CALL(mock_object, Method(matchers)) + // .With(multi-argument-matcher) + // .WillByDefault(action); + // + // is recorded in the data members like this: + // + // source file that contains the statement => file_ + // line number of the statement => line_ + // matchers => matchers_ + // multi-argument-matcher => extra_matcher_ + // action => action_ + ArgumentMatcherTuple matchers_; + Matcher extra_matcher_; + Action action_; +}; // class OnCallSpec + +// Possible reactions on uninteresting calls. TODO(wan@google.com): +// rename the enum values to the kFoo style. +enum CallReaction { + ALLOW, + WARN, + FAIL +}; + +} // namespace internal + +// Utilities for manipulating mock objects. +class Mock { + public: + // The following public methods can be called concurrently. + + // Tells Google Mock to ignore mock_obj when checking for leaked + // mock objects. + static void AllowLeak(const void* mock_obj); + + // Verifies and clears all expectations on the given mock object. + // If the expectations aren't satisfied, generates one or more + // Google Test non-fatal failures and returns false. + static bool VerifyAndClearExpectations(void* mock_obj); + + // Verifies all expectations on the given mock object and clears its + // default actions and expectations. Returns true iff the + // verification was successful. + static bool VerifyAndClear(void* mock_obj); + private: + friend class internal::UntypedFunctionMockerBase; + + // Needed for a function mocker to register itself (so that we know + // how to clear a mock object). + template + friend class internal::FunctionMockerBase; + + template + friend class NiceMock; + + template + friend class StrictMock; + + // Tells Google Mock to allow uninteresting calls on the given mock + // object. + // L < g_gmock_mutex + static void AllowUninterestingCalls(const void* mock_obj); + + // Tells Google Mock to warn the user about uninteresting calls on + // the given mock object. + // L < g_gmock_mutex + static void WarnUninterestingCalls(const void* mock_obj); + + // Tells Google Mock to fail uninteresting calls on the given mock + // object. + // L < g_gmock_mutex + static void FailUninterestingCalls(const void* mock_obj); + + // Tells Google Mock the given mock object is being destroyed and + // its entry in the call-reaction table should be removed. + // L < g_gmock_mutex + static void UnregisterCallReaction(const void* mock_obj); + + // Returns the reaction Google Mock will have on uninteresting calls + // made on the given mock object. + // L < g_gmock_mutex + static internal::CallReaction GetReactionOnUninterestingCalls( + const void* mock_obj); + + // Verifies that all expectations on the given mock object have been + // satisfied. Reports one or more Google Test non-fatal failures + // and returns false if not. + // L >= g_gmock_mutex + static bool VerifyAndClearExpectationsLocked(void* mock_obj); + + // Clears all ON_CALL()s set on the given mock object. + // L >= g_gmock_mutex + static void ClearDefaultActionsLocked(void* mock_obj); + + // Registers a mock object and a mock method it owns. + // L < g_gmock_mutex + static void Register(const void* mock_obj, + internal::UntypedFunctionMockerBase* mocker); + + // Tells Google Mock where in the source code mock_obj is used in an + // ON_CALL or EXPECT_CALL. In case mock_obj is leaked, this + // information helps the user identify which object it is. + // L < g_gmock_mutex + static void RegisterUseByOnCallOrExpectCall( + const void* mock_obj, const char* file, int line); + + // Unregisters a mock method; removes the owning mock object from + // the registry when the last mock method associated with it has + // been unregistered. This is called only in the destructor of + // FunctionMockerBase. + // L >= g_gmock_mutex + static void UnregisterLocked(internal::UntypedFunctionMockerBase* mocker); +}; // class Mock + +// An abstract handle of an expectation. Useful in the .After() +// clause of EXPECT_CALL() for setting the (partial) order of +// expectations. The syntax: +// +// Expectation e1 = EXPECT_CALL(...)...; +// EXPECT_CALL(...).After(e1)...; +// +// sets two expectations where the latter can only be matched after +// the former has been satisfied. +// +// Notes: +// - This class is copyable and has value semantics. +// - Constness is shallow: a const Expectation object itself cannot +// be modified, but the mutable methods of the ExpectationBase +// object it references can be called via expectation_base(). +// - The constructors and destructor are defined out-of-line because +// the Symbian WINSCW compiler wants to otherwise instantiate them +// when it sees this class definition, at which point it doesn't have +// ExpectationBase available yet, leading to incorrect destruction +// in the linked_ptr (or compilation errors if using a checking +// linked_ptr). +class Expectation { + public: + // Constructs a null object that doesn't reference any expectation. + Expectation(); + + ~Expectation(); + + // This single-argument ctor must not be explicit, in order to support the + // Expectation e = EXPECT_CALL(...); + // syntax. + // + // A TypedExpectation object stores its pre-requisites as + // Expectation objects, and needs to call the non-const Retire() + // method on the ExpectationBase objects they reference. Therefore + // Expectation must receive a *non-const* reference to the + // ExpectationBase object. + Expectation(internal::ExpectationBase& exp); // NOLINT + + // The compiler-generated copy ctor and operator= work exactly as + // intended, so we don't need to define our own. + + // Returns true iff rhs references the same expectation as this object does. + bool operator==(const Expectation& rhs) const { + return expectation_base_ == rhs.expectation_base_; + } + + bool operator!=(const Expectation& rhs) const { return !(*this == rhs); } + + private: + friend class ExpectationSet; + friend class Sequence; + friend class ::testing::internal::ExpectationBase; + friend class ::testing::internal::UntypedFunctionMockerBase; + + template + friend class ::testing::internal::FunctionMockerBase; + + template + friend class ::testing::internal::TypedExpectation; + + // This comparator is needed for putting Expectation objects into a set. + class Less { + public: + bool operator()(const Expectation& lhs, const Expectation& rhs) const { + return lhs.expectation_base_.get() < rhs.expectation_base_.get(); + } + }; + + typedef ::std::set Set; + + Expectation( + const internal::linked_ptr& expectation_base); + + // Returns the expectation this object references. + const internal::linked_ptr& + expectation_base() const { + return expectation_base_; + } + + // A linked_ptr that co-owns the expectation this handle references. + internal::linked_ptr expectation_base_; +}; + +// A set of expectation handles. Useful in the .After() clause of +// EXPECT_CALL() for setting the (partial) order of expectations. The +// syntax: +// +// ExpectationSet es; +// es += EXPECT_CALL(...)...; +// es += EXPECT_CALL(...)...; +// EXPECT_CALL(...).After(es)...; +// +// sets three expectations where the last one can only be matched +// after the first two have both been satisfied. +// +// This class is copyable and has value semantics. +class ExpectationSet { + public: + // A bidirectional iterator that can read a const element in the set. + typedef Expectation::Set::const_iterator const_iterator; + + // An object stored in the set. This is an alias of Expectation. + typedef Expectation::Set::value_type value_type; + + // Constructs an empty set. + ExpectationSet() {} + + // This single-argument ctor must not be explicit, in order to support the + // ExpectationSet es = EXPECT_CALL(...); + // syntax. + ExpectationSet(internal::ExpectationBase& exp) { // NOLINT + *this += Expectation(exp); + } + + // This single-argument ctor implements implicit conversion from + // Expectation and thus must not be explicit. This allows either an + // Expectation or an ExpectationSet to be used in .After(). + ExpectationSet(const Expectation& e) { // NOLINT + *this += e; + } + + // The compiler-generator ctor and operator= works exactly as + // intended, so we don't need to define our own. + + // Returns true iff rhs contains the same set of Expectation objects + // as this does. + bool operator==(const ExpectationSet& rhs) const { + return expectations_ == rhs.expectations_; + } + + bool operator!=(const ExpectationSet& rhs) const { return !(*this == rhs); } + + // Implements the syntax + // expectation_set += EXPECT_CALL(...); + ExpectationSet& operator+=(const Expectation& e) { + expectations_.insert(e); + return *this; + } + + int size() const { return static_cast(expectations_.size()); } + + const_iterator begin() const { return expectations_.begin(); } + const_iterator end() const { return expectations_.end(); } + + private: + Expectation::Set expectations_; +}; + + +// Sequence objects are used by a user to specify the relative order +// in which the expectations should match. They are copyable (we rely +// on the compiler-defined copy constructor and assignment operator). +class Sequence { + public: + // Constructs an empty sequence. + Sequence() : last_expectation_(new Expectation) {} + + // Adds an expectation to this sequence. The caller must ensure + // that no other thread is accessing this Sequence object. + void AddExpectation(const Expectation& expectation) const; + + private: + // The last expectation in this sequence. We use a linked_ptr here + // because Sequence objects are copyable and we want the copies to + // be aliases. The linked_ptr allows the copies to co-own and share + // the same Expectation object. + internal::linked_ptr last_expectation_; +}; // class Sequence + +// An object of this type causes all EXPECT_CALL() statements +// encountered in its scope to be put in an anonymous sequence. The +// work is done in the constructor and destructor. You should only +// create an InSequence object on the stack. +// +// The sole purpose for this class is to support easy definition of +// sequential expectations, e.g. +// +// { +// InSequence dummy; // The name of the object doesn't matter. +// +// // The following expectations must match in the order they appear. +// EXPECT_CALL(a, Bar())...; +// EXPECT_CALL(a, Baz())...; +// ... +// EXPECT_CALL(b, Xyz())...; +// } +// +// You can create InSequence objects in multiple threads, as long as +// they are used to affect different mock objects. The idea is that +// each thread can create and set up its own mocks as if it's the only +// thread. However, for clarity of your tests we recommend you to set +// up mocks in the main thread unless you have a good reason not to do +// so. +class InSequence { + public: + InSequence(); + ~InSequence(); + private: + bool sequence_created_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(InSequence); // NOLINT +} GTEST_ATTRIBUTE_UNUSED_; + +namespace internal { + +// Points to the implicit sequence introduced by a living InSequence +// object (if any) in the current thread or NULL. +extern ThreadLocal g_gmock_implicit_sequence; + +// Base class for implementing expectations. +// +// There are two reasons for having a type-agnostic base class for +// Expectation: +// +// 1. We need to store collections of expectations of different +// types (e.g. all pre-requisites of a particular expectation, all +// expectations in a sequence). Therefore these expectation objects +// must share a common base class. +// +// 2. We can avoid binary code bloat by moving methods not depending +// on the template argument of Expectation to the base class. +// +// This class is internal and mustn't be used by user code directly. +class ExpectationBase { + public: + // source_text is the EXPECT_CALL(...) source that created this Expectation. + ExpectationBase(const char* file, int line, const string& source_text); + + virtual ~ExpectationBase(); + + // Where in the source file was the expectation spec defined? + const char* file() const { return file_; } + int line() const { return line_; } + const char* source_text() const { return source_text_.c_str(); } + // Returns the cardinality specified in the expectation spec. + const Cardinality& cardinality() const { return cardinality_; } + + // Describes the source file location of this expectation. + void DescribeLocationTo(::std::ostream* os) const { + *os << FormatFileLocation(file(), line()) << " "; + } + + // Describes how many times a function call matching this + // expectation has occurred. + // L >= g_gmock_mutex + void DescribeCallCountTo(::std::ostream* os) const; + + // If this mock method has an extra matcher (i.e. .With(matcher)), + // describes it to the ostream. + virtual void MaybeDescribeExtraMatcherTo(::std::ostream* os) = 0; + + protected: + friend class ::testing::Expectation; + friend class UntypedFunctionMockerBase; + + enum Clause { + // Don't change the order of the enum members! + kNone, + kWith, + kTimes, + kInSequence, + kAfter, + kWillOnce, + kWillRepeatedly, + kRetiresOnSaturation + }; + + typedef std::vector UntypedActions; + + // Returns an Expectation object that references and co-owns this + // expectation. + virtual Expectation GetHandle() = 0; + + // Asserts that the EXPECT_CALL() statement has the given property. + void AssertSpecProperty(bool property, const string& failure_message) const { + Assert(property, file_, line_, failure_message); + } + + // Expects that the EXPECT_CALL() statement has the given property. + void ExpectSpecProperty(bool property, const string& failure_message) const { + Expect(property, file_, line_, failure_message); + } + + // Explicitly specifies the cardinality of this expectation. Used + // by the subclasses to implement the .Times() clause. + void SpecifyCardinality(const Cardinality& cardinality); + + // Returns true iff the user specified the cardinality explicitly + // using a .Times(). + bool cardinality_specified() const { return cardinality_specified_; } + + // Sets the cardinality of this expectation spec. + void set_cardinality(const Cardinality& a_cardinality) { + cardinality_ = a_cardinality; + } + + // The following group of methods should only be called after the + // EXPECT_CALL() statement, and only when g_gmock_mutex is held by + // the current thread. + + // Retires all pre-requisites of this expectation. + // L >= g_gmock_mutex + void RetireAllPreRequisites(); + + // Returns true iff this expectation is retired. + // L >= g_gmock_mutex + bool is_retired() const { + g_gmock_mutex.AssertHeld(); + return retired_; + } + + // Retires this expectation. + // L >= g_gmock_mutex + void Retire() { + g_gmock_mutex.AssertHeld(); + retired_ = true; + } + + // Returns true iff this expectation is satisfied. + // L >= g_gmock_mutex + bool IsSatisfied() const { + g_gmock_mutex.AssertHeld(); + return cardinality().IsSatisfiedByCallCount(call_count_); + } + + // Returns true iff this expectation is saturated. + // L >= g_gmock_mutex + bool IsSaturated() const { + g_gmock_mutex.AssertHeld(); + return cardinality().IsSaturatedByCallCount(call_count_); + } + + // Returns true iff this expectation is over-saturated. + // L >= g_gmock_mutex + bool IsOverSaturated() const { + g_gmock_mutex.AssertHeld(); + return cardinality().IsOverSaturatedByCallCount(call_count_); + } + + // Returns true iff all pre-requisites of this expectation are satisfied. + // L >= g_gmock_mutex + bool AllPrerequisitesAreSatisfied() const; + + // Adds unsatisfied pre-requisites of this expectation to 'result'. + // L >= g_gmock_mutex + void FindUnsatisfiedPrerequisites(ExpectationSet* result) const; + + // Returns the number this expectation has been invoked. + // L >= g_gmock_mutex + int call_count() const { + g_gmock_mutex.AssertHeld(); + return call_count_; + } + + // Increments the number this expectation has been invoked. + // L >= g_gmock_mutex + void IncrementCallCount() { + g_gmock_mutex.AssertHeld(); + call_count_++; + } + + // Checks the action count (i.e. the number of WillOnce() and + // WillRepeatedly() clauses) against the cardinality if this hasn't + // been done before. Prints a warning if there are too many or too + // few actions. + // L < mutex_ + void CheckActionCountIfNotDone() const; + + friend class ::testing::Sequence; + friend class ::testing::internal::ExpectationTester; + + template + friend class TypedExpectation; + + // Implements the .Times() clause. + void UntypedTimes(const Cardinality& a_cardinality); + + // This group of fields are part of the spec and won't change after + // an EXPECT_CALL() statement finishes. + const char* file_; // The file that contains the expectation. + int line_; // The line number of the expectation. + const string source_text_; // The EXPECT_CALL(...) source text. + // True iff the cardinality is specified explicitly. + bool cardinality_specified_; + Cardinality cardinality_; // The cardinality of the expectation. + // The immediate pre-requisites (i.e. expectations that must be + // satisfied before this expectation can be matched) of this + // expectation. We use linked_ptr in the set because we want an + // Expectation object to be co-owned by its FunctionMocker and its + // successors. This allows multiple mock objects to be deleted at + // different times. + ExpectationSet immediate_prerequisites_; + + // This group of fields are the current state of the expectation, + // and can change as the mock function is called. + int call_count_; // How many times this expectation has been invoked. + bool retired_; // True iff this expectation has retired. + UntypedActions untyped_actions_; + bool extra_matcher_specified_; + bool repeated_action_specified_; // True if a WillRepeatedly() was specified. + bool retires_on_saturation_; + Clause last_clause_; + mutable bool action_count_checked_; // Under mutex_. + mutable Mutex mutex_; // Protects action_count_checked_. + + GTEST_DISALLOW_ASSIGN_(ExpectationBase); +}; // class ExpectationBase + +// Impements an expectation for the given function type. +template +class TypedExpectation : public ExpectationBase { + public: + typedef typename Function::ArgumentTuple ArgumentTuple; + typedef typename Function::ArgumentMatcherTuple ArgumentMatcherTuple; + typedef typename Function::Result Result; + + TypedExpectation(FunctionMockerBase* owner, + const char* a_file, int a_line, const string& a_source_text, + const ArgumentMatcherTuple& m) + : ExpectationBase(a_file, a_line, a_source_text), + owner_(owner), + matchers_(m), + // By default, extra_matcher_ should match anything. However, + // we cannot initialize it with _ as that triggers a compiler + // bug in Symbian's C++ compiler (cannot decide between two + // overloaded constructors of Matcher). + extra_matcher_(A()), + repeated_action_(DoDefault()) {} + + virtual ~TypedExpectation() { + // Check the validity of the action count if it hasn't been done + // yet (for example, if the expectation was never used). + CheckActionCountIfNotDone(); + for (UntypedActions::const_iterator it = untyped_actions_.begin(); + it != untyped_actions_.end(); ++it) { + delete static_cast*>(*it); + } + } + + // Implements the .With() clause. + TypedExpectation& With(const Matcher& m) { + if (last_clause_ == kWith) { + ExpectSpecProperty(false, + ".With() cannot appear " + "more than once in an EXPECT_CALL()."); + } else { + ExpectSpecProperty(last_clause_ < kWith, + ".With() must be the first " + "clause in an EXPECT_CALL()."); + } + last_clause_ = kWith; + + extra_matcher_ = m; + extra_matcher_specified_ = true; + return *this; + } + + // Implements the .Times() clause. + TypedExpectation& Times(const Cardinality& a_cardinality) { + ExpectationBase::UntypedTimes(a_cardinality); + return *this; + } + + // Implements the .Times() clause. + TypedExpectation& Times(int n) { + return Times(Exactly(n)); + } + + // Implements the .InSequence() clause. + TypedExpectation& InSequence(const Sequence& s) { + ExpectSpecProperty(last_clause_ <= kInSequence, + ".InSequence() cannot appear after .After()," + " .WillOnce(), .WillRepeatedly(), or " + ".RetiresOnSaturation()."); + last_clause_ = kInSequence; + + s.AddExpectation(GetHandle()); + return *this; + } + TypedExpectation& InSequence(const Sequence& s1, const Sequence& s2) { + return InSequence(s1).InSequence(s2); + } + TypedExpectation& InSequence(const Sequence& s1, const Sequence& s2, + const Sequence& s3) { + return InSequence(s1, s2).InSequence(s3); + } + TypedExpectation& InSequence(const Sequence& s1, const Sequence& s2, + const Sequence& s3, const Sequence& s4) { + return InSequence(s1, s2, s3).InSequence(s4); + } + TypedExpectation& InSequence(const Sequence& s1, const Sequence& s2, + const Sequence& s3, const Sequence& s4, + const Sequence& s5) { + return InSequence(s1, s2, s3, s4).InSequence(s5); + } + + // Implements that .After() clause. + TypedExpectation& After(const ExpectationSet& s) { + ExpectSpecProperty(last_clause_ <= kAfter, + ".After() cannot appear after .WillOnce()," + " .WillRepeatedly(), or " + ".RetiresOnSaturation()."); + last_clause_ = kAfter; + + for (ExpectationSet::const_iterator it = s.begin(); it != s.end(); ++it) { + immediate_prerequisites_ += *it; + } + return *this; + } + TypedExpectation& After(const ExpectationSet& s1, const ExpectationSet& s2) { + return After(s1).After(s2); + } + TypedExpectation& After(const ExpectationSet& s1, const ExpectationSet& s2, + const ExpectationSet& s3) { + return After(s1, s2).After(s3); + } + TypedExpectation& After(const ExpectationSet& s1, const ExpectationSet& s2, + const ExpectationSet& s3, const ExpectationSet& s4) { + return After(s1, s2, s3).After(s4); + } + TypedExpectation& After(const ExpectationSet& s1, const ExpectationSet& s2, + const ExpectationSet& s3, const ExpectationSet& s4, + const ExpectationSet& s5) { + return After(s1, s2, s3, s4).After(s5); + } + + // Implements the .WillOnce() clause. + TypedExpectation& WillOnce(const Action& action) { + ExpectSpecProperty(last_clause_ <= kWillOnce, + ".WillOnce() cannot appear after " + ".WillRepeatedly() or .RetiresOnSaturation()."); + last_clause_ = kWillOnce; + + untyped_actions_.push_back(new Action(action)); + if (!cardinality_specified()) { + set_cardinality(Exactly(static_cast(untyped_actions_.size()))); + } + return *this; + } + + // Implements the .WillRepeatedly() clause. + TypedExpectation& WillRepeatedly(const Action& action) { + if (last_clause_ == kWillRepeatedly) { + ExpectSpecProperty(false, + ".WillRepeatedly() cannot appear " + "more than once in an EXPECT_CALL()."); + } else { + ExpectSpecProperty(last_clause_ < kWillRepeatedly, + ".WillRepeatedly() cannot appear " + "after .RetiresOnSaturation()."); + } + last_clause_ = kWillRepeatedly; + repeated_action_specified_ = true; + + repeated_action_ = action; + if (!cardinality_specified()) { + set_cardinality(AtLeast(static_cast(untyped_actions_.size()))); + } + + // Now that no more action clauses can be specified, we check + // whether their count makes sense. + CheckActionCountIfNotDone(); + return *this; + } + + // Implements the .RetiresOnSaturation() clause. + TypedExpectation& RetiresOnSaturation() { + ExpectSpecProperty(last_clause_ < kRetiresOnSaturation, + ".RetiresOnSaturation() cannot appear " + "more than once."); + last_clause_ = kRetiresOnSaturation; + retires_on_saturation_ = true; + + // Now that no more action clauses can be specified, we check + // whether their count makes sense. + CheckActionCountIfNotDone(); + return *this; + } + + // Returns the matchers for the arguments as specified inside the + // EXPECT_CALL() macro. + const ArgumentMatcherTuple& matchers() const { + return matchers_; + } + + // Returns the matcher specified by the .With() clause. + const Matcher& extra_matcher() const { + return extra_matcher_; + } + + // Returns the action specified by the .WillRepeatedly() clause. + const Action& repeated_action() const { return repeated_action_; } + + // If this mock method has an extra matcher (i.e. .With(matcher)), + // describes it to the ostream. + virtual void MaybeDescribeExtraMatcherTo(::std::ostream* os) { + if (extra_matcher_specified_) { + *os << " Expected args: "; + extra_matcher_.DescribeTo(os); + *os << "\n"; + } + } + + private: + template + friend class FunctionMockerBase; + + // Returns an Expectation object that references and co-owns this + // expectation. + virtual Expectation GetHandle() { + return owner_->GetHandleOf(this); + } + + // The following methods will be called only after the EXPECT_CALL() + // statement finishes and when the current thread holds + // g_gmock_mutex. + + // Returns true iff this expectation matches the given arguments. + // L >= g_gmock_mutex + bool Matches(const ArgumentTuple& args) const { + g_gmock_mutex.AssertHeld(); + return TupleMatches(matchers_, args) && extra_matcher_.Matches(args); + } + + // Returns true iff this expectation should handle the given arguments. + // L >= g_gmock_mutex + bool ShouldHandleArguments(const ArgumentTuple& args) const { + g_gmock_mutex.AssertHeld(); + + // In case the action count wasn't checked when the expectation + // was defined (e.g. if this expectation has no WillRepeatedly() + // or RetiresOnSaturation() clause), we check it when the + // expectation is used for the first time. + CheckActionCountIfNotDone(); + return !is_retired() && AllPrerequisitesAreSatisfied() && Matches(args); + } + + // Describes the result of matching the arguments against this + // expectation to the given ostream. + // L >= g_gmock_mutex + void ExplainMatchResultTo(const ArgumentTuple& args, + ::std::ostream* os) const { + g_gmock_mutex.AssertHeld(); + + if (is_retired()) { + *os << " Expected: the expectation is active\n" + << " Actual: it is retired\n"; + } else if (!Matches(args)) { + if (!TupleMatches(matchers_, args)) { + ExplainMatchFailureTupleTo(matchers_, args, os); + } + StringMatchResultListener listener; + if (!extra_matcher_.MatchAndExplain(args, &listener)) { + *os << " Expected args: "; + extra_matcher_.DescribeTo(os); + *os << "\n Actual: don't match"; + + internal::PrintIfNotEmpty(listener.str(), os); + *os << "\n"; + } + } else if (!AllPrerequisitesAreSatisfied()) { + *os << " Expected: all pre-requisites are satisfied\n" + << " Actual: the following immediate pre-requisites " + << "are not satisfied:\n"; + ExpectationSet unsatisfied_prereqs; + FindUnsatisfiedPrerequisites(&unsatisfied_prereqs); + int i = 0; + for (ExpectationSet::const_iterator it = unsatisfied_prereqs.begin(); + it != unsatisfied_prereqs.end(); ++it) { + it->expectation_base()->DescribeLocationTo(os); + *os << "pre-requisite #" << i++ << "\n"; + } + *os << " (end of pre-requisites)\n"; + } else { + // This line is here just for completeness' sake. It will never + // be executed as currently the ExplainMatchResultTo() function + // is called only when the mock function call does NOT match the + // expectation. + *os << "The call matches the expectation.\n"; + } + } + + // Returns the action that should be taken for the current invocation. + // L >= g_gmock_mutex + const Action& GetCurrentAction(const FunctionMockerBase* mocker, + const ArgumentTuple& args) const { + g_gmock_mutex.AssertHeld(); + const int count = call_count(); + Assert(count >= 1, __FILE__, __LINE__, + "call_count() is <= 0 when GetCurrentAction() is " + "called - this should never happen."); + + const int action_count = static_cast(untyped_actions_.size()); + if (action_count > 0 && !repeated_action_specified_ && + count > action_count) { + // If there is at least one WillOnce() and no WillRepeatedly(), + // we warn the user when the WillOnce() clauses ran out. + ::std::stringstream ss; + DescribeLocationTo(&ss); + ss << "Actions ran out in " << source_text() << "...\n" + << "Called " << count << " times, but only " + << action_count << " WillOnce()" + << (action_count == 1 ? " is" : "s are") << " specified - "; + mocker->DescribeDefaultActionTo(args, &ss); + Log(WARNING, ss.str(), 1); + } + + return count <= action_count ? + *static_cast*>(untyped_actions_[count - 1]) : + repeated_action(); + } + + // Given the arguments of a mock function call, if the call will + // over-saturate this expectation, returns the default action; + // otherwise, returns the next action in this expectation. Also + // describes *what* happened to 'what', and explains *why* Google + // Mock does it to 'why'. This method is not const as it calls + // IncrementCallCount(). A return value of NULL means the default + // action. + // L >= g_gmock_mutex + const Action* GetActionForArguments(const FunctionMockerBase* mocker, + const ArgumentTuple& args, + ::std::ostream* what, + ::std::ostream* why) { + g_gmock_mutex.AssertHeld(); + if (IsSaturated()) { + // We have an excessive call. + IncrementCallCount(); + *what << "Mock function called more times than expected - "; + mocker->DescribeDefaultActionTo(args, what); + DescribeCallCountTo(why); + + // TODO(wan@google.com): allow the user to control whether + // unexpected calls should fail immediately or continue using a + // flag --gmock_unexpected_calls_are_fatal. + return NULL; + } + + IncrementCallCount(); + RetireAllPreRequisites(); + + if (retires_on_saturation_ && IsSaturated()) { + Retire(); + } + + // Must be done after IncrementCount()! + *what << "Mock function call matches " << source_text() <<"...\n"; + return &(GetCurrentAction(mocker, args)); + } + + // All the fields below won't change once the EXPECT_CALL() + // statement finishes. + FunctionMockerBase* const owner_; + ArgumentMatcherTuple matchers_; + Matcher extra_matcher_; + Action repeated_action_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(TypedExpectation); +}; // class TypedExpectation + +// A MockSpec object is used by ON_CALL() or EXPECT_CALL() for +// specifying the default behavior of, or expectation on, a mock +// function. + +// Note: class MockSpec really belongs to the ::testing namespace. +// However if we define it in ::testing, MSVC will complain when +// classes in ::testing::internal declare it as a friend class +// template. To workaround this compiler bug, we define MockSpec in +// ::testing::internal and import it into ::testing. + +// Logs a message including file and line number information. +void LogWithLocation(testing::internal::LogSeverity severity, + const char* file, int line, + const string& message); + +template +class MockSpec { + public: + typedef typename internal::Function::ArgumentTuple ArgumentTuple; + typedef typename internal::Function::ArgumentMatcherTuple + ArgumentMatcherTuple; + + // Constructs a MockSpec object, given the function mocker object + // that the spec is associated with. + explicit MockSpec(internal::FunctionMockerBase* function_mocker) + : function_mocker_(function_mocker) {} + + // Adds a new default action spec to the function mocker and returns + // the newly created spec. + internal::OnCallSpec& InternalDefaultActionSetAt( + const char* file, int line, const char* obj, const char* call) { + LogWithLocation(internal::INFO, file, line, + string("ON_CALL(") + obj + ", " + call + ") invoked"); + return function_mocker_->AddNewOnCallSpec(file, line, matchers_); + } + + // Adds a new expectation spec to the function mocker and returns + // the newly created spec. + internal::TypedExpectation& InternalExpectedAt( + const char* file, int line, const char* obj, const char* call) { + const string source_text(string("EXPECT_CALL(") + obj + ", " + call + ")"); + LogWithLocation(internal::INFO, file, line, source_text + " invoked"); + return function_mocker_->AddNewExpectation( + file, line, source_text, matchers_); + } + + private: + template + friend class internal::FunctionMocker; + + void SetMatchers(const ArgumentMatcherTuple& matchers) { + matchers_ = matchers; + } + + // The function mocker that owns this spec. + internal::FunctionMockerBase* const function_mocker_; + // The argument matchers specified in the spec. + ArgumentMatcherTuple matchers_; + + GTEST_DISALLOW_ASSIGN_(MockSpec); +}; // class MockSpec + +// MSVC warns about using 'this' in base member initializer list, so +// we need to temporarily disable the warning. We have to do it for +// the entire class to suppress the warning, even though it's about +// the constructor only. + +#ifdef _MSC_VER +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4355) // Temporarily disables warning 4355. +#endif // _MSV_VER + +// C++ treats the void type specially. For example, you cannot define +// a void-typed variable or pass a void value to a function. +// ActionResultHolder holds a value of type T, where T must be a +// copyable type or void (T doesn't need to be default-constructable). +// It hides the syntactic difference between void and other types, and +// is used to unify the code for invoking both void-returning and +// non-void-returning mock functions. + +// Untyped base class for ActionResultHolder. +class UntypedActionResultHolderBase { + public: + virtual ~UntypedActionResultHolderBase() {} + + // Prints the held value as an action's result to os. + virtual void PrintAsActionResult(::std::ostream* os) const = 0; +}; + +// This generic definition is used when T is not void. +template +class ActionResultHolder : public UntypedActionResultHolderBase { + public: + explicit ActionResultHolder(T a_value) : value_(a_value) {} + + // The compiler-generated copy constructor and assignment operator + // are exactly what we need, so we don't need to define them. + + // Returns the held value and deletes this object. + T GetValueAndDelete() const { + T retval(value_); + delete this; + return retval; + } + + // Prints the held value as an action's result to os. + virtual void PrintAsActionResult(::std::ostream* os) const { + *os << "\n Returns: "; + // T may be a reference type, so we don't use UniversalPrint(). + UniversalPrinter::Print(value_, os); + } + + // Performs the given mock function's default action and returns the + // result in a new-ed ActionResultHolder. + template + static ActionResultHolder* PerformDefaultAction( + const FunctionMockerBase* func_mocker, + const typename Function::ArgumentTuple& args, + const string& call_description) { + return new ActionResultHolder( + func_mocker->PerformDefaultAction(args, call_description)); + } + + // Performs the given action and returns the result in a new-ed + // ActionResultHolder. + template + static ActionResultHolder* + PerformAction(const Action& action, + const typename Function::ArgumentTuple& args) { + return new ActionResultHolder(action.Perform(args)); + } + + private: + T value_; + + // T could be a reference type, so = isn't supported. + GTEST_DISALLOW_ASSIGN_(ActionResultHolder); +}; + +// Specialization for T = void. +template <> +class ActionResultHolder : public UntypedActionResultHolderBase { + public: + void GetValueAndDelete() const { delete this; } + + virtual void PrintAsActionResult(::std::ostream* /* os */) const {} + + // Performs the given mock function's default action and returns NULL; + template + static ActionResultHolder* PerformDefaultAction( + const FunctionMockerBase* func_mocker, + const typename Function::ArgumentTuple& args, + const string& call_description) { + func_mocker->PerformDefaultAction(args, call_description); + return NULL; + } + + // Performs the given action and returns NULL. + template + static ActionResultHolder* PerformAction( + const Action& action, + const typename Function::ArgumentTuple& args) { + action.Perform(args); + return NULL; + } +}; + +// The base of the function mocker class for the given function type. +// We put the methods in this class instead of its child to avoid code +// bloat. +template +class FunctionMockerBase : public UntypedFunctionMockerBase { + public: + typedef typename Function::Result Result; + typedef typename Function::ArgumentTuple ArgumentTuple; + typedef typename Function::ArgumentMatcherTuple ArgumentMatcherTuple; + + FunctionMockerBase() : current_spec_(this) {} + + // The destructor verifies that all expectations on this mock + // function have been satisfied. If not, it will report Google Test + // non-fatal failures for the violations. + // L < g_gmock_mutex + virtual ~FunctionMockerBase() { + MutexLock l(&g_gmock_mutex); + VerifyAndClearExpectationsLocked(); + Mock::UnregisterLocked(this); + ClearDefaultActionsLocked(); + } + + // Returns the ON_CALL spec that matches this mock function with the + // given arguments; returns NULL if no matching ON_CALL is found. + // L = * + const OnCallSpec* FindOnCallSpec( + const ArgumentTuple& args) const { + for (UntypedOnCallSpecs::const_reverse_iterator it + = untyped_on_call_specs_.rbegin(); + it != untyped_on_call_specs_.rend(); ++it) { + const OnCallSpec* spec = static_cast*>(*it); + if (spec->Matches(args)) + return spec; + } + + return NULL; + } + + // Performs the default action of this mock function on the given arguments + // and returns the result. Asserts with a helpful call descrption if there is + // no valid return value. This method doesn't depend on the mutable state of + // this object, and thus can be called concurrently without locking. + // L = * + Result PerformDefaultAction(const ArgumentTuple& args, + const string& call_description) const { + const OnCallSpec* const spec = + this->FindOnCallSpec(args); + if (spec != NULL) { + return spec->GetAction().Perform(args); + } + Assert(DefaultValue::Exists(), "", -1, + call_description + "\n The mock function has no default action " + "set, and its return type has no default value set."); + return DefaultValue::Get(); + } + + // Performs the default action with the given arguments and returns + // the action's result. The call description string will be used in + // the error message to describe the call in the case the default + // action fails. The caller is responsible for deleting the result. + // L = * + virtual UntypedActionResultHolderBase* UntypedPerformDefaultAction( + const void* untyped_args, // must point to an ArgumentTuple + const string& call_description) const { + const ArgumentTuple& args = + *static_cast(untyped_args); + return ResultHolder::PerformDefaultAction(this, args, call_description); + } + + // Performs the given action with the given arguments and returns + // the action's result. The caller is responsible for deleting the + // result. + // L = * + virtual UntypedActionResultHolderBase* UntypedPerformAction( + const void* untyped_action, const void* untyped_args) const { + // Make a copy of the action before performing it, in case the + // action deletes the mock object (and thus deletes itself). + const Action action = *static_cast*>(untyped_action); + const ArgumentTuple& args = + *static_cast(untyped_args); + return ResultHolder::PerformAction(action, args); + } + + // Implements UntypedFunctionMockerBase::ClearDefaultActionsLocked(): + // clears the ON_CALL()s set on this mock function. + // L >= g_gmock_mutex + virtual void ClearDefaultActionsLocked() { + g_gmock_mutex.AssertHeld(); + for (UntypedOnCallSpecs::const_iterator it = + untyped_on_call_specs_.begin(); + it != untyped_on_call_specs_.end(); ++it) { + delete static_cast*>(*it); + } + untyped_on_call_specs_.clear(); + } + + protected: + template + friend class MockSpec; + + typedef ActionResultHolder ResultHolder; + + // Returns the result of invoking this mock function with the given + // arguments. This function can be safely called from multiple + // threads concurrently. + // L < g_gmock_mutex + Result InvokeWith(const ArgumentTuple& args) { + return static_cast( + this->UntypedInvokeWith(&args))->GetValueAndDelete(); + } + + // Adds and returns a default action spec for this mock function. + // L < g_gmock_mutex + OnCallSpec& AddNewOnCallSpec( + const char* file, int line, + const ArgumentMatcherTuple& m) { + Mock::RegisterUseByOnCallOrExpectCall(MockObject(), file, line); + OnCallSpec* const on_call_spec = new OnCallSpec(file, line, m); + untyped_on_call_specs_.push_back(on_call_spec); + return *on_call_spec; + } + + // Adds and returns an expectation spec for this mock function. + // L < g_gmock_mutex + TypedExpectation& AddNewExpectation( + const char* file, + int line, + const string& source_text, + const ArgumentMatcherTuple& m) { + Mock::RegisterUseByOnCallOrExpectCall(MockObject(), file, line); + TypedExpectation* const expectation = + new TypedExpectation(this, file, line, source_text, m); + const linked_ptr untyped_expectation(expectation); + untyped_expectations_.push_back(untyped_expectation); + + // Adds this expectation into the implicit sequence if there is one. + Sequence* const implicit_sequence = g_gmock_implicit_sequence.get(); + if (implicit_sequence != NULL) { + implicit_sequence->AddExpectation(Expectation(untyped_expectation)); + } + + return *expectation; + } + + // The current spec (either default action spec or expectation spec) + // being described on this function mocker. + MockSpec& current_spec() { return current_spec_; } + + private: + template friend class TypedExpectation; + + // Some utilities needed for implementing UntypedInvokeWith(). + + // Describes what default action will be performed for the given + // arguments. + // L = * + void DescribeDefaultActionTo(const ArgumentTuple& args, + ::std::ostream* os) const { + const OnCallSpec* const spec = FindOnCallSpec(args); + + if (spec == NULL) { + *os << (internal::type_equals::value ? + "returning directly.\n" : + "returning default value.\n"); + } else { + *os << "taking default action specified at:\n" + << FormatFileLocation(spec->file(), spec->line()) << "\n"; + } + } + + // Writes a message that the call is uninteresting (i.e. neither + // explicitly expected nor explicitly unexpected) to the given + // ostream. + // L < g_gmock_mutex + virtual void UntypedDescribeUninterestingCall(const void* untyped_args, + ::std::ostream* os) const { + const ArgumentTuple& args = + *static_cast(untyped_args); + *os << "Uninteresting mock function call - "; + DescribeDefaultActionTo(args, os); + *os << " Function call: " << Name(); + UniversalPrint(args, os); + } + + // Returns the expectation that matches the given function arguments + // (or NULL is there's no match); when a match is found, + // untyped_action is set to point to the action that should be + // performed (or NULL if the action is "do default"), and + // is_excessive is modified to indicate whether the call exceeds the + // expected number. + // + // Critical section: We must find the matching expectation and the + // corresponding action that needs to be taken in an ATOMIC + // transaction. Otherwise another thread may call this mock + // method in the middle and mess up the state. + // + // However, performing the action has to be left out of the critical + // section. The reason is that we have no control on what the + // action does (it can invoke an arbitrary user function or even a + // mock function) and excessive locking could cause a dead lock. + // L < g_gmock_mutex + virtual const ExpectationBase* UntypedFindMatchingExpectation( + const void* untyped_args, + const void** untyped_action, bool* is_excessive, + ::std::ostream* what, ::std::ostream* why) { + const ArgumentTuple& args = + *static_cast(untyped_args); + MutexLock l(&g_gmock_mutex); + TypedExpectation* exp = this->FindMatchingExpectationLocked(args); + if (exp == NULL) { // A match wasn't found. + this->FormatUnexpectedCallMessageLocked(args, what, why); + return NULL; + } + + // This line must be done before calling GetActionForArguments(), + // which will increment the call count for *exp and thus affect + // its saturation status. + *is_excessive = exp->IsSaturated(); + const Action* action = exp->GetActionForArguments(this, args, what, why); + if (action != NULL && action->IsDoDefault()) + action = NULL; // Normalize "do default" to NULL. + *untyped_action = action; + return exp; + } + + // Prints the given function arguments to the ostream. + virtual void UntypedPrintArgs(const void* untyped_args, + ::std::ostream* os) const { + const ArgumentTuple& args = + *static_cast(untyped_args); + UniversalPrint(args, os); + } + + // Returns the expectation that matches the arguments, or NULL if no + // expectation matches them. + // L >= g_gmock_mutex + TypedExpectation* FindMatchingExpectationLocked( + const ArgumentTuple& args) const { + g_gmock_mutex.AssertHeld(); + for (typename UntypedExpectations::const_reverse_iterator it = + untyped_expectations_.rbegin(); + it != untyped_expectations_.rend(); ++it) { + TypedExpectation* const exp = + static_cast*>(it->get()); + if (exp->ShouldHandleArguments(args)) { + return exp; + } + } + return NULL; + } + + // Returns a message that the arguments don't match any expectation. + // L >= g_gmock_mutex + void FormatUnexpectedCallMessageLocked(const ArgumentTuple& args, + ::std::ostream* os, + ::std::ostream* why) const { + g_gmock_mutex.AssertHeld(); + *os << "\nUnexpected mock function call - "; + DescribeDefaultActionTo(args, os); + PrintTriedExpectationsLocked(args, why); + } + + // Prints a list of expectations that have been tried against the + // current mock function call. + // L >= g_gmock_mutex + void PrintTriedExpectationsLocked(const ArgumentTuple& args, + ::std::ostream* why) const { + g_gmock_mutex.AssertHeld(); + const int count = static_cast(untyped_expectations_.size()); + *why << "Google Mock tried the following " << count << " " + << (count == 1 ? "expectation, but it didn't match" : + "expectations, but none matched") + << ":\n"; + for (int i = 0; i < count; i++) { + TypedExpectation* const expectation = + static_cast*>(untyped_expectations_[i].get()); + *why << "\n"; + expectation->DescribeLocationTo(why); + if (count > 1) { + *why << "tried expectation #" << i << ": "; + } + *why << expectation->source_text() << "...\n"; + expectation->ExplainMatchResultTo(args, why); + expectation->DescribeCallCountTo(why); + } + } + + // The current spec (either default action spec or expectation spec) + // being described on this function mocker. + MockSpec current_spec_; + + // There is no generally useful and implementable semantics of + // copying a mock object, so copying a mock is usually a user error. + // Thus we disallow copying function mockers. If the user really + // wants to copy a mock object, he should implement his own copy + // operation, for example: + // + // class MockFoo : public Foo { + // public: + // // Defines a copy constructor explicitly. + // MockFoo(const MockFoo& src) {} + // ... + // }; + GTEST_DISALLOW_COPY_AND_ASSIGN_(FunctionMockerBase); +}; // class FunctionMockerBase + +#ifdef _MSC_VER +# pragma warning(pop) // Restores the warning state. +#endif // _MSV_VER + +// Implements methods of FunctionMockerBase. + +// Verifies that all expectations on this mock function have been +// satisfied. Reports one or more Google Test non-fatal failures and +// returns false if not. +// L >= g_gmock_mutex + +// Reports an uninteresting call (whose description is in msg) in the +// manner specified by 'reaction'. +void ReportUninterestingCall(CallReaction reaction, const string& msg); + +} // namespace internal + +// The style guide prohibits "using" statements in a namespace scope +// inside a header file. However, the MockSpec class template is +// meant to be defined in the ::testing namespace. The following line +// is just a trick for working around a bug in MSVC 8.0, which cannot +// handle it if we define MockSpec in ::testing. +using internal::MockSpec; + +// Const(x) is a convenient function for obtaining a const reference +// to x. This is useful for setting expectations on an overloaded +// const mock method, e.g. +// +// class MockFoo : public FooInterface { +// public: +// MOCK_METHOD0(Bar, int()); +// MOCK_CONST_METHOD0(Bar, int&()); +// }; +// +// MockFoo foo; +// // Expects a call to non-const MockFoo::Bar(). +// EXPECT_CALL(foo, Bar()); +// // Expects a call to const MockFoo::Bar(). +// EXPECT_CALL(Const(foo), Bar()); +template +inline const T& Const(const T& x) { return x; } + +// Constructs an Expectation object that references and co-owns exp. +inline Expectation::Expectation(internal::ExpectationBase& exp) // NOLINT + : expectation_base_(exp.GetHandle().expectation_base()) {} + +} // namespace testing + +// A separate macro is required to avoid compile errors when the name +// of the method used in call is a result of macro expansion. +// See CompilesWithMethodNameExpandedFromMacro tests in +// internal/gmock-spec-builders_test.cc for more details. +#define GMOCK_ON_CALL_IMPL_(obj, call) \ + ((obj).gmock_##call).InternalDefaultActionSetAt(__FILE__, __LINE__, \ + #obj, #call) +#define ON_CALL(obj, call) GMOCK_ON_CALL_IMPL_(obj, call) + +#define GMOCK_EXPECT_CALL_IMPL_(obj, call) \ + ((obj).gmock_##call).InternalExpectedAt(__FILE__, __LINE__, #obj, #call) +#define EXPECT_CALL(obj, call) GMOCK_EXPECT_CALL_IMPL_(obj, call) + +#endif // GMOCK_INCLUDE_GMOCK_GMOCK_SPEC_BUILDERS_H_ diff --git a/libwvdrmengine/test/gmock/include/gmock/gmock.h b/libwvdrmengine/test/gmock/include/gmock/gmock.h new file mode 100644 index 00000000..ba9fa286 --- /dev/null +++ b/libwvdrmengine/test/gmock/include/gmock/gmock.h @@ -0,0 +1,93 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This is the main header file a user should include. + +#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_H_ +#define GMOCK_INCLUDE_GMOCK_GMOCK_H_ + +// This file implements the following syntax: +// +// ON_CALL(mock_object.Method(...)) +// .With(...) ? +// .WillByDefault(...); +// +// where With() is optional and WillByDefault() must appear exactly +// once. +// +// EXPECT_CALL(mock_object.Method(...)) +// .With(...) ? +// .Times(...) ? +// .InSequence(...) * +// .WillOnce(...) * +// .WillRepeatedly(...) ? +// .RetiresOnSaturation() ? ; +// +// where all clauses are optional and WillOnce() can be repeated. + +#include "gmock/gmock-actions.h" +#include "gmock/gmock-cardinalities.h" +#include "gmock/gmock-generated-actions.h" +#include "gmock/gmock-generated-function-mockers.h" +#include "gmock/gmock-generated-matchers.h" +#include "gmock/gmock-more-actions.h" +#include "gmock/gmock-generated-nice-strict.h" +#include "gmock/gmock-matchers.h" +#include "gmock/internal/gmock-internal-utils.h" + +namespace testing { + +// Declares Google Mock flags that we want a user to use programmatically. +GMOCK_DECLARE_bool_(catch_leaked_mocks); +GMOCK_DECLARE_string_(verbose); + +// Initializes Google Mock. This must be called before running the +// tests. In particular, it parses the command line for the flags +// that Google Mock recognizes. Whenever a Google Mock flag is seen, +// it is removed from argv, and *argc is decremented. +// +// No value is returned. Instead, the Google Mock flag variables are +// updated. +// +// Since Google Test is needed for Google Mock to work, this function +// also initializes Google Test and parses its flags, if that hasn't +// been done. +void InitGoogleMock(int* argc, char** argv); + +// This overloaded version can be used in Windows programs compiled in +// UNICODE mode. +void InitGoogleMock(int* argc, wchar_t** argv); + +} // namespace testing + +#endif // GMOCK_INCLUDE_GMOCK_GMOCK_H_ diff --git a/libwvdrmengine/test/gmock/include/gmock/internal/gmock-generated-internal-utils.h b/libwvdrmengine/test/gmock/include/gmock/internal/gmock-generated-internal-utils.h new file mode 100644 index 00000000..1b52dceb --- /dev/null +++ b/libwvdrmengine/test/gmock/include/gmock/internal/gmock-generated-internal-utils.h @@ -0,0 +1,277 @@ +// This file was GENERATED by a script. DO NOT EDIT BY HAND!!! + +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file contains template meta-programming utility classes needed +// for implementing Google Mock. + +#ifndef GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_GENERATED_INTERNAL_UTILS_H_ +#define GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_GENERATED_INTERNAL_UTILS_H_ + +#include "gmock/internal/gmock-port.h" + +namespace testing { + +template +class Matcher; + +namespace internal { + +// An IgnoredValue object can be implicitly constructed from ANY value. +// This is used in implementing the IgnoreResult(a) action. +class IgnoredValue { + public: + // This constructor template allows any value to be implicitly + // converted to IgnoredValue. The object has no data member and + // doesn't try to remember anything about the argument. We + // deliberately omit the 'explicit' keyword in order to allow the + // conversion to be implicit. + template + IgnoredValue(const T&) {} +}; + +// MatcherTuple::type is a tuple type where each field is a Matcher +// for the corresponding field in tuple type T. +template +struct MatcherTuple; + +template <> +struct MatcherTuple< ::std::tr1::tuple<> > { + typedef ::std::tr1::tuple< > type; +}; + +template +struct MatcherTuple< ::std::tr1::tuple > { + typedef ::std::tr1::tuple > type; +}; + +template +struct MatcherTuple< ::std::tr1::tuple > { + typedef ::std::tr1::tuple, Matcher > type; +}; + +template +struct MatcherTuple< ::std::tr1::tuple > { + typedef ::std::tr1::tuple, Matcher, Matcher > type; +}; + +template +struct MatcherTuple< ::std::tr1::tuple > { + typedef ::std::tr1::tuple, Matcher, Matcher, + Matcher > type; +}; + +template +struct MatcherTuple< ::std::tr1::tuple > { + typedef ::std::tr1::tuple, Matcher, Matcher, Matcher, + Matcher > type; +}; + +template +struct MatcherTuple< ::std::tr1::tuple > { + typedef ::std::tr1::tuple, Matcher, Matcher, Matcher, + Matcher, Matcher > type; +}; + +template +struct MatcherTuple< ::std::tr1::tuple > { + typedef ::std::tr1::tuple, Matcher, Matcher, Matcher, + Matcher, Matcher, Matcher > type; +}; + +template +struct MatcherTuple< ::std::tr1::tuple > { + typedef ::std::tr1::tuple, Matcher, Matcher, Matcher, + Matcher, Matcher, Matcher, Matcher > type; +}; + +template +struct MatcherTuple< ::std::tr1::tuple > { + typedef ::std::tr1::tuple, Matcher, Matcher, Matcher, + Matcher, Matcher, Matcher, Matcher, Matcher > type; +}; + +template +struct MatcherTuple< ::std::tr1::tuple > { + typedef ::std::tr1::tuple, Matcher, Matcher, Matcher, + Matcher, Matcher, Matcher, Matcher, Matcher, + Matcher > type; +}; + +// Template struct Function, where F must be a function type, contains +// the following typedefs: +// +// Result: the function's return type. +// ArgumentN: the type of the N-th argument, where N starts with 1. +// ArgumentTuple: the tuple type consisting of all parameters of F. +// ArgumentMatcherTuple: the tuple type consisting of Matchers for all +// parameters of F. +// MakeResultVoid: the function type obtained by substituting void +// for the return type of F. +// MakeResultIgnoredValue: +// the function type obtained by substituting Something +// for the return type of F. +template +struct Function; + +template +struct Function { + typedef R Result; + typedef ::std::tr1::tuple<> ArgumentTuple; + typedef typename MatcherTuple::type ArgumentMatcherTuple; + typedef void MakeResultVoid(); + typedef IgnoredValue MakeResultIgnoredValue(); +}; + +template +struct Function + : Function { + typedef A1 Argument1; + typedef ::std::tr1::tuple ArgumentTuple; + typedef typename MatcherTuple::type ArgumentMatcherTuple; + typedef void MakeResultVoid(A1); + typedef IgnoredValue MakeResultIgnoredValue(A1); +}; + +template +struct Function + : Function { + typedef A2 Argument2; + typedef ::std::tr1::tuple ArgumentTuple; + typedef typename MatcherTuple::type ArgumentMatcherTuple; + typedef void MakeResultVoid(A1, A2); + typedef IgnoredValue MakeResultIgnoredValue(A1, A2); +}; + +template +struct Function + : Function { + typedef A3 Argument3; + typedef ::std::tr1::tuple ArgumentTuple; + typedef typename MatcherTuple::type ArgumentMatcherTuple; + typedef void MakeResultVoid(A1, A2, A3); + typedef IgnoredValue MakeResultIgnoredValue(A1, A2, A3); +}; + +template +struct Function + : Function { + typedef A4 Argument4; + typedef ::std::tr1::tuple ArgumentTuple; + typedef typename MatcherTuple::type ArgumentMatcherTuple; + typedef void MakeResultVoid(A1, A2, A3, A4); + typedef IgnoredValue MakeResultIgnoredValue(A1, A2, A3, A4); +}; + +template +struct Function + : Function { + typedef A5 Argument5; + typedef ::std::tr1::tuple ArgumentTuple; + typedef typename MatcherTuple::type ArgumentMatcherTuple; + typedef void MakeResultVoid(A1, A2, A3, A4, A5); + typedef IgnoredValue MakeResultIgnoredValue(A1, A2, A3, A4, A5); +}; + +template +struct Function + : Function { + typedef A6 Argument6; + typedef ::std::tr1::tuple ArgumentTuple; + typedef typename MatcherTuple::type ArgumentMatcherTuple; + typedef void MakeResultVoid(A1, A2, A3, A4, A5, A6); + typedef IgnoredValue MakeResultIgnoredValue(A1, A2, A3, A4, A5, A6); +}; + +template +struct Function + : Function { + typedef A7 Argument7; + typedef ::std::tr1::tuple ArgumentTuple; + typedef typename MatcherTuple::type ArgumentMatcherTuple; + typedef void MakeResultVoid(A1, A2, A3, A4, A5, A6, A7); + typedef IgnoredValue MakeResultIgnoredValue(A1, A2, A3, A4, A5, A6, A7); +}; + +template +struct Function + : Function { + typedef A8 Argument8; + typedef ::std::tr1::tuple ArgumentTuple; + typedef typename MatcherTuple::type ArgumentMatcherTuple; + typedef void MakeResultVoid(A1, A2, A3, A4, A5, A6, A7, A8); + typedef IgnoredValue MakeResultIgnoredValue(A1, A2, A3, A4, A5, A6, A7, A8); +}; + +template +struct Function + : Function { + typedef A9 Argument9; + typedef ::std::tr1::tuple ArgumentTuple; + typedef typename MatcherTuple::type ArgumentMatcherTuple; + typedef void MakeResultVoid(A1, A2, A3, A4, A5, A6, A7, A8, A9); + typedef IgnoredValue MakeResultIgnoredValue(A1, A2, A3, A4, A5, A6, A7, A8, + A9); +}; + +template +struct Function + : Function { + typedef A10 Argument10; + typedef ::std::tr1::tuple ArgumentTuple; + typedef typename MatcherTuple::type ArgumentMatcherTuple; + typedef void MakeResultVoid(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10); + typedef IgnoredValue MakeResultIgnoredValue(A1, A2, A3, A4, A5, A6, A7, A8, + A9, A10); +}; + +} // namespace internal + +} // namespace testing + +#endif // GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_GENERATED_INTERNAL_UTILS_H_ diff --git a/libwvdrmengine/test/gmock/include/gmock/internal/gmock-generated-internal-utils.h.pump b/libwvdrmengine/test/gmock/include/gmock/internal/gmock-generated-internal-utils.h.pump new file mode 100644 index 00000000..821e474e --- /dev/null +++ b/libwvdrmengine/test/gmock/include/gmock/internal/gmock-generated-internal-utils.h.pump @@ -0,0 +1,136 @@ +$$ -*- mode: c++; -*- +$$ This is a Pump source file. Please use Pump to convert it to +$$ gmock-generated-function-mockers.h. +$$ +$var n = 10 $$ The maximum arity we support. +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file contains template meta-programming utility classes needed +// for implementing Google Mock. + +#ifndef GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_GENERATED_INTERNAL_UTILS_H_ +#define GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_GENERATED_INTERNAL_UTILS_H_ + +#include "gmock/internal/gmock-port.h" + +namespace testing { + +template +class Matcher; + +namespace internal { + +// An IgnoredValue object can be implicitly constructed from ANY value. +// This is used in implementing the IgnoreResult(a) action. +class IgnoredValue { + public: + // This constructor template allows any value to be implicitly + // converted to IgnoredValue. The object has no data member and + // doesn't try to remember anything about the argument. We + // deliberately omit the 'explicit' keyword in order to allow the + // conversion to be implicit. + template + IgnoredValue(const T&) {} +}; + +// MatcherTuple::type is a tuple type where each field is a Matcher +// for the corresponding field in tuple type T. +template +struct MatcherTuple; + + +$range i 0..n +$for i [[ +$range j 1..i +$var typename_As = [[$for j, [[typename A$j]]]] +$var As = [[$for j, [[A$j]]]] +$var matcher_As = [[$for j, [[Matcher]]]] +template <$typename_As> +struct MatcherTuple< ::std::tr1::tuple<$As> > { + typedef ::std::tr1::tuple<$matcher_As > type; +}; + + +]] +// Template struct Function, where F must be a function type, contains +// the following typedefs: +// +// Result: the function's return type. +// ArgumentN: the type of the N-th argument, where N starts with 1. +// ArgumentTuple: the tuple type consisting of all parameters of F. +// ArgumentMatcherTuple: the tuple type consisting of Matchers for all +// parameters of F. +// MakeResultVoid: the function type obtained by substituting void +// for the return type of F. +// MakeResultIgnoredValue: +// the function type obtained by substituting Something +// for the return type of F. +template +struct Function; + +template +struct Function { + typedef R Result; + typedef ::std::tr1::tuple<> ArgumentTuple; + typedef typename MatcherTuple::type ArgumentMatcherTuple; + typedef void MakeResultVoid(); + typedef IgnoredValue MakeResultIgnoredValue(); +}; + + +$range i 1..n +$for i [[ +$range j 1..i +$var typename_As = [[$for j [[, typename A$j]]]] +$var As = [[$for j, [[A$j]]]] +$var matcher_As = [[$for j, [[Matcher]]]] +$range k 1..i-1 +$var prev_As = [[$for k, [[A$k]]]] +template +struct Function + : Function { + typedef A$i Argument$i; + typedef ::std::tr1::tuple<$As> ArgumentTuple; + typedef typename MatcherTuple::type ArgumentMatcherTuple; + typedef void MakeResultVoid($As); + typedef IgnoredValue MakeResultIgnoredValue($As); +}; + + +]] +} // namespace internal + +} // namespace testing + +#endif // GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_GENERATED_INTERNAL_UTILS_H_ diff --git a/libwvdrmengine/test/gmock/include/gmock/internal/gmock-internal-utils.h b/libwvdrmengine/test/gmock/include/gmock/internal/gmock-internal-utils.h new file mode 100644 index 00000000..f0fd8682 --- /dev/null +++ b/libwvdrmengine/test/gmock/include/gmock/internal/gmock-internal-utils.h @@ -0,0 +1,463 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file defines some utilities useful for implementing Google +// Mock. They are subject to change without notice, so please DO NOT +// USE THEM IN USER CODE. + +#ifndef GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_INTERNAL_UTILS_H_ +#define GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_INTERNAL_UTILS_H_ + +#include +#include // NOLINT +#include + +#include "gmock/internal/gmock-generated-internal-utils.h" +#include "gmock/internal/gmock-port.h" +#include "gtest/gtest.h" + +namespace testing { +namespace internal { + +// Converts an identifier name to a space-separated list of lower-case +// words. Each maximum substring of the form [A-Za-z][a-z]*|\d+ is +// treated as one word. For example, both "FooBar123" and +// "foo_bar_123" are converted to "foo bar 123". +string ConvertIdentifierNameToWords(const char* id_name); + +// PointeeOf::type is the type of a value pointed to by a +// Pointer, which can be either a smart pointer or a raw pointer. The +// following default implementation is for the case where Pointer is a +// smart pointer. +template +struct PointeeOf { + // Smart pointer classes define type element_type as the type of + // their pointees. + typedef typename Pointer::element_type type; +}; +// This specialization is for the raw pointer case. +template +struct PointeeOf { typedef T type; }; // NOLINT + +// GetRawPointer(p) returns the raw pointer underlying p when p is a +// smart pointer, or returns p itself when p is already a raw pointer. +// The following default implementation is for the smart pointer case. +template +inline typename Pointer::element_type* GetRawPointer(const Pointer& p) { + return p.get(); +} +// This overloaded version is for the raw pointer case. +template +inline Element* GetRawPointer(Element* p) { return p; } + +// This comparator allows linked_ptr to be stored in sets. +template +struct LinkedPtrLessThan { + bool operator()(const ::testing::internal::linked_ptr& lhs, + const ::testing::internal::linked_ptr& rhs) const { + return lhs.get() < rhs.get(); + } +}; + +// Symbian compilation can be done with wchar_t being either a native +// type or a typedef. Using Google Mock with OpenC without wchar_t +// should require the definition of _STLP_NO_WCHAR_T. +// +// MSVC treats wchar_t as a native type usually, but treats it as the +// same as unsigned short when the compiler option /Zc:wchar_t- is +// specified. It defines _NATIVE_WCHAR_T_DEFINED symbol when wchar_t +// is a native type. +#if (GTEST_OS_SYMBIAN && defined(_STLP_NO_WCHAR_T)) || \ + (defined(_MSC_VER) && !defined(_NATIVE_WCHAR_T_DEFINED)) +// wchar_t is a typedef. +#else +# define GMOCK_WCHAR_T_IS_NATIVE_ 1 +#endif + +// signed wchar_t and unsigned wchar_t are NOT in the C++ standard. +// Using them is a bad practice and not portable. So DON'T use them. +// +// Still, Google Mock is designed to work even if the user uses signed +// wchar_t or unsigned wchar_t (obviously, assuming the compiler +// supports them). +// +// To gcc, +// wchar_t == signed wchar_t != unsigned wchar_t == unsigned int +#ifdef __GNUC__ +// signed/unsigned wchar_t are valid types. +# define GMOCK_HAS_SIGNED_WCHAR_T_ 1 +#endif + +// In what follows, we use the term "kind" to indicate whether a type +// is bool, an integer type (excluding bool), a floating-point type, +// or none of them. This categorization is useful for determining +// when a matcher argument type can be safely converted to another +// type in the implementation of SafeMatcherCast. +enum TypeKind { + kBool, kInteger, kFloatingPoint, kOther +}; + +// KindOf::value is the kind of type T. +template struct KindOf { + enum { value = kOther }; // The default kind. +}; + +// This macro declares that the kind of 'type' is 'kind'. +#define GMOCK_DECLARE_KIND_(type, kind) \ + template <> struct KindOf { enum { value = kind }; } + +GMOCK_DECLARE_KIND_(bool, kBool); + +// All standard integer types. +GMOCK_DECLARE_KIND_(char, kInteger); +GMOCK_DECLARE_KIND_(signed char, kInteger); +GMOCK_DECLARE_KIND_(unsigned char, kInteger); +GMOCK_DECLARE_KIND_(short, kInteger); // NOLINT +GMOCK_DECLARE_KIND_(unsigned short, kInteger); // NOLINT +GMOCK_DECLARE_KIND_(int, kInteger); +GMOCK_DECLARE_KIND_(unsigned int, kInteger); +GMOCK_DECLARE_KIND_(long, kInteger); // NOLINT +GMOCK_DECLARE_KIND_(unsigned long, kInteger); // NOLINT + +#if GMOCK_WCHAR_T_IS_NATIVE_ +GMOCK_DECLARE_KIND_(wchar_t, kInteger); +#endif + +// Non-standard integer types. +GMOCK_DECLARE_KIND_(Int64, kInteger); +GMOCK_DECLARE_KIND_(UInt64, kInteger); + +// All standard floating-point types. +GMOCK_DECLARE_KIND_(float, kFloatingPoint); +GMOCK_DECLARE_KIND_(double, kFloatingPoint); +GMOCK_DECLARE_KIND_(long double, kFloatingPoint); + +#undef GMOCK_DECLARE_KIND_ + +// Evaluates to the kind of 'type'. +#define GMOCK_KIND_OF_(type) \ + static_cast< ::testing::internal::TypeKind>( \ + ::testing::internal::KindOf::value) + +// Evaluates to true iff integer type T is signed. +#define GMOCK_IS_SIGNED_(T) (static_cast(-1) < 0) + +// LosslessArithmeticConvertibleImpl::value +// is true iff arithmetic type From can be losslessly converted to +// arithmetic type To. +// +// It's the user's responsibility to ensure that both From and To are +// raw (i.e. has no CV modifier, is not a pointer, and is not a +// reference) built-in arithmetic types, kFromKind is the kind of +// From, and kToKind is the kind of To; the value is +// implementation-defined when the above pre-condition is violated. +template +struct LosslessArithmeticConvertibleImpl : public false_type {}; + +// Converting bool to bool is lossless. +template <> +struct LosslessArithmeticConvertibleImpl + : public true_type {}; // NOLINT + +// Converting bool to any integer type is lossless. +template +struct LosslessArithmeticConvertibleImpl + : public true_type {}; // NOLINT + +// Converting bool to any floating-point type is lossless. +template +struct LosslessArithmeticConvertibleImpl + : public true_type {}; // NOLINT + +// Converting an integer to bool is lossy. +template +struct LosslessArithmeticConvertibleImpl + : public false_type {}; // NOLINT + +// Converting an integer to another non-bool integer is lossless iff +// the target type's range encloses the source type's range. +template +struct LosslessArithmeticConvertibleImpl + : public bool_constant< + // When converting from a smaller size to a larger size, we are + // fine as long as we are not converting from signed to unsigned. + ((sizeof(From) < sizeof(To)) && + (!GMOCK_IS_SIGNED_(From) || GMOCK_IS_SIGNED_(To))) || + // When converting between the same size, the signedness must match. + ((sizeof(From) == sizeof(To)) && + (GMOCK_IS_SIGNED_(From) == GMOCK_IS_SIGNED_(To)))> {}; // NOLINT + +#undef GMOCK_IS_SIGNED_ + +// Converting an integer to a floating-point type may be lossy, since +// the format of a floating-point number is implementation-defined. +template +struct LosslessArithmeticConvertibleImpl + : public false_type {}; // NOLINT + +// Converting a floating-point to bool is lossy. +template +struct LosslessArithmeticConvertibleImpl + : public false_type {}; // NOLINT + +// Converting a floating-point to an integer is lossy. +template +struct LosslessArithmeticConvertibleImpl + : public false_type {}; // NOLINT + +// Converting a floating-point to another floating-point is lossless +// iff the target type is at least as big as the source type. +template +struct LosslessArithmeticConvertibleImpl< + kFloatingPoint, From, kFloatingPoint, To> + : public bool_constant {}; // NOLINT + +// LosslessArithmeticConvertible::value is true iff arithmetic +// type From can be losslessly converted to arithmetic type To. +// +// It's the user's responsibility to ensure that both From and To are +// raw (i.e. has no CV modifier, is not a pointer, and is not a +// reference) built-in arithmetic types; the value is +// implementation-defined when the above pre-condition is violated. +template +struct LosslessArithmeticConvertible + : public LosslessArithmeticConvertibleImpl< + GMOCK_KIND_OF_(From), From, GMOCK_KIND_OF_(To), To> {}; // NOLINT + +// This interface knows how to report a Google Mock failure (either +// non-fatal or fatal). +class FailureReporterInterface { + public: + // The type of a failure (either non-fatal or fatal). + enum FailureType { + NONFATAL, FATAL + }; + + virtual ~FailureReporterInterface() {} + + // Reports a failure that occurred at the given source file location. + virtual void ReportFailure(FailureType type, const char* file, int line, + const string& message) = 0; +}; + +// Returns the failure reporter used by Google Mock. +FailureReporterInterface* GetFailureReporter(); + +// Asserts that condition is true; aborts the process with the given +// message if condition is false. We cannot use LOG(FATAL) or CHECK() +// as Google Mock might be used to mock the log sink itself. We +// inline this function to prevent it from showing up in the stack +// trace. +inline void Assert(bool condition, const char* file, int line, + const string& msg) { + if (!condition) { + GetFailureReporter()->ReportFailure(FailureReporterInterface::FATAL, + file, line, msg); + } +} +inline void Assert(bool condition, const char* file, int line) { + Assert(condition, file, line, "Assertion failed."); +} + +// Verifies that condition is true; generates a non-fatal failure if +// condition is false. +inline void Expect(bool condition, const char* file, int line, + const string& msg) { + if (!condition) { + GetFailureReporter()->ReportFailure(FailureReporterInterface::NONFATAL, + file, line, msg); + } +} +inline void Expect(bool condition, const char* file, int line) { + Expect(condition, file, line, "Expectation failed."); +} + +// Severity level of a log. +enum LogSeverity { + INFO = 0, + WARNING = 1 +}; + +// Valid values for the --gmock_verbose flag. + +// All logs (informational and warnings) are printed. +const char kInfoVerbosity[] = "info"; +// Only warnings are printed. +const char kWarningVerbosity[] = "warning"; +// No logs are printed. +const char kErrorVerbosity[] = "error"; + +// Returns true iff a log with the given severity is visible according +// to the --gmock_verbose flag. +bool LogIsVisible(LogSeverity severity); + +// Prints the given message to stdout iff 'severity' >= the level +// specified by the --gmock_verbose flag. If stack_frames_to_skip >= +// 0, also prints the stack trace excluding the top +// stack_frames_to_skip frames. In opt mode, any positive +// stack_frames_to_skip is treated as 0, since we don't know which +// function calls will be inlined by the compiler and need to be +// conservative. +void Log(LogSeverity severity, const string& message, int stack_frames_to_skip); + +// TODO(wan@google.com): group all type utilities together. + +// Type traits. + +// is_reference::value is non-zero iff T is a reference type. +template struct is_reference : public false_type {}; +template struct is_reference : public true_type {}; + +// type_equals::value is non-zero iff T1 and T2 are the same type. +template struct type_equals : public false_type {}; +template struct type_equals : public true_type {}; + +// remove_reference::type removes the reference from type T, if any. +template struct remove_reference { typedef T type; }; // NOLINT +template struct remove_reference { typedef T type; }; // NOLINT + +// Invalid() returns an invalid value of type T. This is useful +// when a value of type T is needed for compilation, but the statement +// will not really be executed (or we don't care if the statement +// crashes). +template +inline T Invalid() { + return *static_cast::type*>(NULL); +} +template <> +inline void Invalid() {} + +// Given a raw type (i.e. having no top-level reference or const +// modifier) RawContainer that's either an STL-style container or a +// native array, class StlContainerView has the +// following members: +// +// - type is a type that provides an STL-style container view to +// (i.e. implements the STL container concept for) RawContainer; +// - const_reference is a type that provides a reference to a const +// RawContainer; +// - ConstReference(raw_container) returns a const reference to an STL-style +// container view to raw_container, which is a RawContainer. +// - Copy(raw_container) returns an STL-style container view of a +// copy of raw_container, which is a RawContainer. +// +// This generic version is used when RawContainer itself is already an +// STL-style container. +template +class StlContainerView { + public: + typedef RawContainer type; + typedef const type& const_reference; + + static const_reference ConstReference(const RawContainer& container) { + // Ensures that RawContainer is not a const type. + testing::StaticAssertTypeEq(); + return container; + } + static type Copy(const RawContainer& container) { return container; } +}; + +// This specialization is used when RawContainer is a native array type. +template +class StlContainerView { + public: + typedef GTEST_REMOVE_CONST_(Element) RawElement; + typedef internal::NativeArray type; + // NativeArray can represent a native array either by value or by + // reference (selected by a constructor argument), so 'const type' + // can be used to reference a const native array. We cannot + // 'typedef const type& const_reference' here, as that would mean + // ConstReference() has to return a reference to a local variable. + typedef const type const_reference; + + static const_reference ConstReference(const Element (&array)[N]) { + // Ensures that Element is not a const type. + testing::StaticAssertTypeEq(); +#if GTEST_OS_SYMBIAN + // The Nokia Symbian compiler confuses itself in template instantiation + // for this call without the cast to Element*: + // function call '[testing::internal::NativeArray].NativeArray( + // {lval} const char *[4], long, testing::internal::RelationToSource)' + // does not match + // 'testing::internal::NativeArray::NativeArray( + // char *const *, unsigned int, testing::internal::RelationToSource)' + // (instantiating: 'testing::internal::ContainsMatcherImpl + // ::Matches(const char * (&)[4]) const') + // (instantiating: 'testing::internal::StlContainerView:: + // ConstReference(const char * (&)[4])') + // (and though the N parameter type is mismatched in the above explicit + // conversion of it doesn't help - only the conversion of the array). + return type(const_cast(&array[0]), N, kReference); +#else + return type(array, N, kReference); +#endif // GTEST_OS_SYMBIAN + } + static type Copy(const Element (&array)[N]) { +#if GTEST_OS_SYMBIAN + return type(const_cast(&array[0]), N, kCopy); +#else + return type(array, N, kCopy); +#endif // GTEST_OS_SYMBIAN + } +}; + +// This specialization is used when RawContainer is a native array +// represented as a (pointer, size) tuple. +template +class StlContainerView< ::std::tr1::tuple > { + public: + typedef GTEST_REMOVE_CONST_( + typename internal::PointeeOf::type) RawElement; + typedef internal::NativeArray type; + typedef const type const_reference; + + static const_reference ConstReference( + const ::std::tr1::tuple& array) { + using ::std::tr1::get; + return type(get<0>(array), get<1>(array), kReference); + } + static type Copy(const ::std::tr1::tuple& array) { + using ::std::tr1::get; + return type(get<0>(array), get<1>(array), kCopy); + } +}; + +// The following specialization prevents the user from instantiating +// StlContainer with a reference type. +template class StlContainerView; + +} // namespace internal +} // namespace testing + +#endif // GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_INTERNAL_UTILS_H_ diff --git a/libwvdrmengine/test/gmock/include/gmock/internal/gmock-port.h b/libwvdrmengine/test/gmock/include/gmock/internal/gmock-port.h new file mode 100644 index 00000000..3b9cc479 --- /dev/null +++ b/libwvdrmengine/test/gmock/include/gmock/internal/gmock-port.h @@ -0,0 +1,78 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: vadimb@google.com (Vadim Berman) +// +// Low-level types and utilities for porting Google Mock to various +// platforms. They are subject to change without notice. DO NOT USE +// THEM IN USER CODE. + +#ifndef GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_PORT_H_ +#define GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_PORT_H_ + +#include +#include +#include + +// Most of the types needed for porting Google Mock are also required +// for Google Test and are defined in gtest-port.h. +#include "gtest/internal/gtest-linked_ptr.h" +#include "gtest/internal/gtest-port.h" + +// To avoid conditional compilation everywhere, we make it +// gmock-port.h's responsibility to #include the header implementing +// tr1/tuple. gmock-port.h does this via gtest-port.h, which is +// guaranteed to pull in the tuple header. + +// For MS Visual C++, check the compiler version. At least VS 2003 is +// required to compile Google Mock. +#if defined(_MSC_VER) && _MSC_VER < 1310 +# error "At least Visual C++ 2003 (7.1) is required to compile Google Mock." +#endif + +// Macro for referencing flags. This is public as we want the user to +// use this syntax to reference Google Mock flags. +#define GMOCK_FLAG(name) FLAGS_gmock_##name + +// Macros for declaring flags. +#define GMOCK_DECLARE_bool_(name) extern bool GMOCK_FLAG(name) +#define GMOCK_DECLARE_int32_(name) \ + extern ::testing::internal::Int32 GMOCK_FLAG(name) +#define GMOCK_DECLARE_string_(name) \ + extern ::testing::internal::String GMOCK_FLAG(name) + +// Macros for defining flags. +#define GMOCK_DEFINE_bool_(name, default_val, doc) \ + bool GMOCK_FLAG(name) = (default_val) +#define GMOCK_DEFINE_int32_(name, default_val, doc) \ + ::testing::internal::Int32 GMOCK_FLAG(name) = (default_val) +#define GMOCK_DEFINE_string_(name, default_val, doc) \ + ::testing::internal::String GMOCK_FLAG(name) = (default_val) + +#endif // GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_PORT_H_ diff --git a/libwvdrmengine/test/gmock/scripts/fuse_gmock_files.py b/libwvdrmengine/test/gmock/scripts/fuse_gmock_files.py new file mode 100755 index 00000000..fc0baf79 --- /dev/null +++ b/libwvdrmengine/test/gmock/scripts/fuse_gmock_files.py @@ -0,0 +1,240 @@ +#!/usr/bin/env python +# +# Copyright 2009, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""fuse_gmock_files.py v0.1.0 +Fuses Google Mock and Google Test source code into two .h files and a .cc file. + +SYNOPSIS + fuse_gmock_files.py [GMOCK_ROOT_DIR] OUTPUT_DIR + + Scans GMOCK_ROOT_DIR for Google Mock and Google Test source + code, assuming Google Test is in the GMOCK_ROOT_DIR/gtest + sub-directory, and generates three files: + OUTPUT_DIR/gtest/gtest.h, OUTPUT_DIR/gmock/gmock.h, and + OUTPUT_DIR/gmock-gtest-all.cc. Then you can build your tests + by adding OUTPUT_DIR to the include search path and linking + with OUTPUT_DIR/gmock-gtest-all.cc. These three files contain + everything you need to use Google Mock. Hence you can + "install" Google Mock by copying them to wherever you want. + + GMOCK_ROOT_DIR can be omitted and defaults to the parent + directory of the directory holding this script. + +EXAMPLES + ./fuse_gmock_files.py fused_gmock + ./fuse_gmock_files.py path/to/unpacked/gmock fused_gmock + +This tool is experimental. In particular, it assumes that there is no +conditional inclusion of Google Mock or Google Test headers. Please +report any problems to googlemock@googlegroups.com. You can read +http://code.google.com/p/googlemock/wiki/CookBook for more +information. +""" + +__author__ = 'wan@google.com (Zhanyong Wan)' + +import os +import re +import sets +import sys + +# We assume that this file is in the scripts/ directory in the Google +# Mock root directory. +DEFAULT_GMOCK_ROOT_DIR = os.path.join(os.path.dirname(__file__), '..') + +# We need to call into gtest/scripts/fuse_gtest_files.py. +sys.path.append(os.path.join(DEFAULT_GMOCK_ROOT_DIR, 'gtest/scripts')) +import fuse_gtest_files +gtest = fuse_gtest_files + +# Regex for matching '#include "gmock/..."'. +INCLUDE_GMOCK_FILE_REGEX = re.compile(r'^\s*#\s*include\s*"(gmock/.+)"') + +# Where to find the source seed files. +GMOCK_H_SEED = 'include/gmock/gmock.h' +GMOCK_ALL_CC_SEED = 'src/gmock-all.cc' + +# Where to put the generated files. +GTEST_H_OUTPUT = 'gtest/gtest.h' +GMOCK_H_OUTPUT = 'gmock/gmock.h' +GMOCK_GTEST_ALL_CC_OUTPUT = 'gmock-gtest-all.cc' + + +def GetGTestRootDir(gmock_root): + """Returns the root directory of Google Test.""" + + return os.path.join(gmock_root, 'gtest') + + +def ValidateGMockRootDir(gmock_root): + """Makes sure gmock_root points to a valid gmock root directory. + + The function aborts the program on failure. + """ + + gtest.ValidateGTestRootDir(GetGTestRootDir(gmock_root)) + gtest.VerifyFileExists(gmock_root, GMOCK_H_SEED) + gtest.VerifyFileExists(gmock_root, GMOCK_ALL_CC_SEED) + + +def ValidateOutputDir(output_dir): + """Makes sure output_dir points to a valid output directory. + + The function aborts the program on failure. + """ + + gtest.VerifyOutputFile(output_dir, gtest.GTEST_H_OUTPUT) + gtest.VerifyOutputFile(output_dir, GMOCK_H_OUTPUT) + gtest.VerifyOutputFile(output_dir, GMOCK_GTEST_ALL_CC_OUTPUT) + + +def FuseGMockH(gmock_root, output_dir): + """Scans folder gmock_root to generate gmock/gmock.h in output_dir.""" + + output_file = file(os.path.join(output_dir, GMOCK_H_OUTPUT), 'w') + processed_files = sets.Set() # Holds all gmock headers we've processed. + + def ProcessFile(gmock_header_path): + """Processes the given gmock header file.""" + + # We don't process the same header twice. + if gmock_header_path in processed_files: + return + + processed_files.add(gmock_header_path) + + # Reads each line in the given gmock header. + for line in file(os.path.join(gmock_root, gmock_header_path), 'r'): + m = INCLUDE_GMOCK_FILE_REGEX.match(line) + if m: + # It's '#include "gmock/..."' - let's process it recursively. + ProcessFile('include/' + m.group(1)) + else: + m = gtest.INCLUDE_GTEST_FILE_REGEX.match(line) + if m: + # It's '#include "gtest/foo.h"'. We translate it to + # "gtest/gtest.h", regardless of what foo is, since all + # gtest headers are fused into gtest/gtest.h. + + # There is no need to #include gtest.h twice. + if not gtest.GTEST_H_SEED in processed_files: + processed_files.add(gtest.GTEST_H_SEED) + output_file.write('#include "%s"\n' % (gtest.GTEST_H_OUTPUT,)) + else: + # Otherwise we copy the line unchanged to the output file. + output_file.write(line) + + ProcessFile(GMOCK_H_SEED) + output_file.close() + + +def FuseGMockAllCcToFile(gmock_root, output_file): + """Scans folder gmock_root to fuse gmock-all.cc into output_file.""" + + processed_files = sets.Set() + + def ProcessFile(gmock_source_file): + """Processes the given gmock source file.""" + + # We don't process the same #included file twice. + if gmock_source_file in processed_files: + return + + processed_files.add(gmock_source_file) + + # Reads each line in the given gmock source file. + for line in file(os.path.join(gmock_root, gmock_source_file), 'r'): + m = INCLUDE_GMOCK_FILE_REGEX.match(line) + if m: + # It's '#include "gmock/foo.h"'. We treat it as '#include + # "gmock/gmock.h"', as all other gmock headers are being fused + # into gmock.h and cannot be #included directly. + + # There is no need to #include "gmock/gmock.h" more than once. + if not GMOCK_H_SEED in processed_files: + processed_files.add(GMOCK_H_SEED) + output_file.write('#include "%s"\n' % (GMOCK_H_OUTPUT,)) + else: + m = gtest.INCLUDE_GTEST_FILE_REGEX.match(line) + if m: + # It's '#include "gtest/..."'. + # There is no need to #include gtest.h as it has been + # #included by gtest-all.cc. + pass + else: + m = gtest.INCLUDE_SRC_FILE_REGEX.match(line) + if m: + # It's '#include "src/foo"' - let's process it recursively. + ProcessFile(m.group(1)) + else: + # Otherwise we copy the line unchanged to the output file. + output_file.write(line) + + ProcessFile(GMOCK_ALL_CC_SEED) + + +def FuseGMockGTestAllCc(gmock_root, output_dir): + """Scans folder gmock_root to generate gmock-gtest-all.cc in output_dir.""" + + output_file = file(os.path.join(output_dir, GMOCK_GTEST_ALL_CC_OUTPUT), 'w') + # First, fuse gtest-all.cc into gmock-gtest-all.cc. + gtest.FuseGTestAllCcToFile(GetGTestRootDir(gmock_root), output_file) + # Next, append fused gmock-all.cc to gmock-gtest-all.cc. + FuseGMockAllCcToFile(gmock_root, output_file) + output_file.close() + + +def FuseGMock(gmock_root, output_dir): + """Fuses gtest.h, gmock.h, and gmock-gtest-all.h.""" + + ValidateGMockRootDir(gmock_root) + ValidateOutputDir(output_dir) + + gtest.FuseGTestH(GetGTestRootDir(gmock_root), output_dir) + FuseGMockH(gmock_root, output_dir) + FuseGMockGTestAllCc(gmock_root, output_dir) + + +def main(): + argc = len(sys.argv) + if argc == 2: + # fuse_gmock_files.py OUTPUT_DIR + FuseGMock(DEFAULT_GMOCK_ROOT_DIR, sys.argv[1]) + elif argc == 3: + # fuse_gmock_files.py GMOCK_ROOT_DIR OUTPUT_DIR + FuseGMock(sys.argv[1], sys.argv[2]) + else: + print __doc__ + sys.exit(1) + + +if __name__ == '__main__': + main() diff --git a/libwvdrmengine/test/gmock/scripts/generator/COPYING b/libwvdrmengine/test/gmock/scripts/generator/COPYING new file mode 100644 index 00000000..87ea0636 --- /dev/null +++ b/libwvdrmengine/test/gmock/scripts/generator/COPYING @@ -0,0 +1,203 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [2007] Neal Norwitz + Portions Copyright [2007] Google Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/libwvdrmengine/test/gmock/scripts/generator/README b/libwvdrmengine/test/gmock/scripts/generator/README new file mode 100644 index 00000000..d6f95974 --- /dev/null +++ b/libwvdrmengine/test/gmock/scripts/generator/README @@ -0,0 +1,35 @@ + +The Google Mock class generator is an application that is part of cppclean. +For more information about cppclean, see the README.cppclean file or +visit http://code.google.com/p/cppclean/ + +cppclean requires Python 2.3.5 or later. If you don't have Python installed +on your system, you will also need to install it. You can download Python +from: http://www.python.org/download/releases/ + +To use the Google Mock class generator, you need to call it +on the command line passing the header file and class for which you want +to generate a Google Mock class. + +Make sure to install the scripts somewhere in your path. Then you can +run the program. + + gmock_gen.py header-file.h [ClassName]... + +If no ClassNames are specified, all classes in the file are emitted. + +To change the indentation from the default of 2, set INDENT in +the environment. For example to use an indent of 4 spaces: + +INDENT=4 gmock_gen.py header-file.h ClassName + +This version was made from SVN revision 281 in the cppclean repository. + +Known Limitations +----------------- +Not all code will be generated properly. For example, when mocking templated +classes, the template information is lost. You will need to add the template +information manually. + +Not all permutations of using multiple pointers/references will be rendered +properly. These will also have to be fixed manually. diff --git a/libwvdrmengine/test/gmock/scripts/generator/README.cppclean b/libwvdrmengine/test/gmock/scripts/generator/README.cppclean new file mode 100644 index 00000000..65431b61 --- /dev/null +++ b/libwvdrmengine/test/gmock/scripts/generator/README.cppclean @@ -0,0 +1,115 @@ +Goal: +----- + CppClean attempts to find problems in C++ source that slow development + in large code bases, for example various forms of unused code. + Unused code can be unused functions, methods, data members, types, etc + to unnecessary #include directives. Unnecessary #includes can cause + considerable extra compiles increasing the edit-compile-run cycle. + + The project home page is: http://code.google.com/p/cppclean/ + + +Features: +--------- + * Find and print C++ language constructs: classes, methods, functions, etc. + * Find classes with virtual methods, no virtual destructor, and no bases + * Find global/static data that are potential problems when using threads + * Unnecessary forward class declarations + * Unnecessary function declarations + * Undeclared function definitions + * (planned) Find unnecessary header files #included + - No direct reference to anything in the header + - Header is unnecessary if classes were forward declared instead + * (planned) Source files that reference headers not directly #included, + ie, files that rely on a transitive #include from another header + * (planned) Unused members (private, protected, & public) methods and data + * (planned) Store AST in a SQL database so relationships can be queried + +AST is Abstract Syntax Tree, a representation of parsed source code. +http://en.wikipedia.org/wiki/Abstract_syntax_tree + + +System Requirements: +-------------------- + * Python 2.4 or later (2.3 probably works too) + * Works on Windows (untested), Mac OS X, and Unix + + +How to Run: +----------- + For all examples, it is assumed that cppclean resides in a directory called + /cppclean. + + To print warnings for classes with virtual methods, no virtual destructor and + no base classes: + + /cppclean/run.sh nonvirtual_dtors.py file1.h file2.h file3.cc ... + + To print all the functions defined in header file(s): + + /cppclean/run.sh functions.py file1.h file2.h ... + + All the commands take multiple files on the command line. Other programs + include: find_warnings, headers, methods, and types. Some other programs + are available, but used primarily for debugging. + + run.sh is a simple wrapper that sets PYTHONPATH to /cppclean and then + runs the program in /cppclean/cpp/PROGRAM.py. There is currently + no equivalent for Windows. Contributions for a run.bat file + would be greatly appreciated. + + +How to Configure: +----------------- + You can add a siteheaders.py file in /cppclean/cpp to configure where + to look for other headers (typically -I options passed to a compiler). + Currently two values are supported: _TRANSITIVE and GetIncludeDirs. + _TRANSITIVE should be set to a boolean value (True or False) indicating + whether to transitively process all header files. The default is False. + + GetIncludeDirs is a function that takes a single argument and returns + a sequence of directories to include. This can be a generator or + return a static list. + + def GetIncludeDirs(filename): + return ['/some/path/with/other/headers'] + + # Here is a more complicated example. + def GetIncludeDirs(filename): + yield '/path1' + yield os.path.join('/path2', os.path.dirname(filename)) + yield '/path3' + + +How to Test: +------------ + For all examples, it is assumed that cppclean resides in a directory called + /cppclean. The tests require + + cd /cppclean + make test + # To generate expected results after a change: + make expected + + +Current Status: +--------------- + The parser works pretty well for header files, parsing about 99% of Google's + header files. Anything which inspects structure of C++ source files should + work reasonably well. Function bodies are not transformed to an AST, + but left as tokens. Much work is still needed on finding unused header files + and storing an AST in a database. + + +Non-goals: +---------- + * Parsing all valid C++ source + * Handling invalid C++ source gracefully + * Compiling to machine code (or anything beyond an AST) + + +Contact: +-------- + If you used cppclean, I would love to hear about your experiences + cppclean@googlegroups.com. Even if you don't use cppclean, I'd like to + hear from you. :-) (You can contact me directly at: nnorwitz@gmail.com) diff --git a/libwvdrmengine/test/gmock/scripts/generator/cpp/__init__.py b/libwvdrmengine/test/gmock/scripts/generator/cpp/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/libwvdrmengine/test/gmock/scripts/generator/cpp/ast.py b/libwvdrmengine/test/gmock/scripts/generator/cpp/ast.py new file mode 100755 index 00000000..6f61f877 --- /dev/null +++ b/libwvdrmengine/test/gmock/scripts/generator/cpp/ast.py @@ -0,0 +1,1723 @@ +#!/usr/bin/env python +# +# Copyright 2007 Neal Norwitz +# Portions Copyright 2007 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Generate an Abstract Syntax Tree (AST) for C++.""" + +__author__ = 'nnorwitz@google.com (Neal Norwitz)' + + +# TODO: +# * Tokens should never be exported, need to convert to Nodes +# (return types, parameters, etc.) +# * Handle static class data for templatized classes +# * Handle casts (both C++ and C-style) +# * Handle conditions and loops (if/else, switch, for, while/do) +# +# TODO much, much later: +# * Handle #define +# * exceptions + + +try: + # Python 3.x + import builtins +except ImportError: + # Python 2.x + import __builtin__ as builtins + +import sys +import traceback + +from cpp import keywords +from cpp import tokenize +from cpp import utils + + +if not hasattr(builtins, 'reversed'): + # Support Python 2.3 and earlier. + def reversed(seq): + for i in range(len(seq)-1, -1, -1): + yield seq[i] + +if not hasattr(builtins, 'next'): + # Support Python 2.5 and earlier. + def next(obj): + return obj.next() + + +VISIBILITY_PUBLIC, VISIBILITY_PROTECTED, VISIBILITY_PRIVATE = range(3) + +FUNCTION_NONE = 0x00 +FUNCTION_CONST = 0x01 +FUNCTION_VIRTUAL = 0x02 +FUNCTION_PURE_VIRTUAL = 0x04 +FUNCTION_CTOR = 0x08 +FUNCTION_DTOR = 0x10 +FUNCTION_ATTRIBUTE = 0x20 +FUNCTION_UNKNOWN_ANNOTATION = 0x40 +FUNCTION_THROW = 0x80 + +""" +These are currently unused. Should really handle these properly at some point. + +TYPE_MODIFIER_INLINE = 0x010000 +TYPE_MODIFIER_EXTERN = 0x020000 +TYPE_MODIFIER_STATIC = 0x040000 +TYPE_MODIFIER_CONST = 0x080000 +TYPE_MODIFIER_REGISTER = 0x100000 +TYPE_MODIFIER_VOLATILE = 0x200000 +TYPE_MODIFIER_MUTABLE = 0x400000 + +TYPE_MODIFIER_MAP = { + 'inline': TYPE_MODIFIER_INLINE, + 'extern': TYPE_MODIFIER_EXTERN, + 'static': TYPE_MODIFIER_STATIC, + 'const': TYPE_MODIFIER_CONST, + 'register': TYPE_MODIFIER_REGISTER, + 'volatile': TYPE_MODIFIER_VOLATILE, + 'mutable': TYPE_MODIFIER_MUTABLE, + } +""" + +_INTERNAL_TOKEN = 'internal' +_NAMESPACE_POP = 'ns-pop' + + +# TODO(nnorwitz): use this as a singleton for templated_types, etc +# where we don't want to create a new empty dict each time. It is also const. +class _NullDict(object): + __contains__ = lambda self: False + keys = values = items = iterkeys = itervalues = iteritems = lambda self: () + + +# TODO(nnorwitz): move AST nodes into a separate module. +class Node(object): + """Base AST node.""" + + def __init__(self, start, end): + self.start = start + self.end = end + + def IsDeclaration(self): + """Returns bool if this node is a declaration.""" + return False + + def IsDefinition(self): + """Returns bool if this node is a definition.""" + return False + + def IsExportable(self): + """Returns bool if this node exportable from a header file.""" + return False + + def Requires(self, node): + """Does this AST node require the definition of the node passed in?""" + return False + + def XXX__str__(self): + return self._StringHelper(self.__class__.__name__, '') + + def _StringHelper(self, name, suffix): + if not utils.DEBUG: + return '%s(%s)' % (name, suffix) + return '%s(%d, %d, %s)' % (name, self.start, self.end, suffix) + + def __repr__(self): + return str(self) + + +class Define(Node): + def __init__(self, start, end, name, definition): + Node.__init__(self, start, end) + self.name = name + self.definition = definition + + def __str__(self): + value = '%s %s' % (self.name, self.definition) + return self._StringHelper(self.__class__.__name__, value) + + +class Include(Node): + def __init__(self, start, end, filename, system): + Node.__init__(self, start, end) + self.filename = filename + self.system = system + + def __str__(self): + fmt = '"%s"' + if self.system: + fmt = '<%s>' + return self._StringHelper(self.__class__.__name__, fmt % self.filename) + + +class Goto(Node): + def __init__(self, start, end, label): + Node.__init__(self, start, end) + self.label = label + + def __str__(self): + return self._StringHelper(self.__class__.__name__, str(self.label)) + + +class Expr(Node): + def __init__(self, start, end, expr): + Node.__init__(self, start, end) + self.expr = expr + + def Requires(self, node): + # TODO(nnorwitz): impl. + return False + + def __str__(self): + return self._StringHelper(self.__class__.__name__, str(self.expr)) + + +class Return(Expr): + pass + + +class Delete(Expr): + pass + + +class Friend(Expr): + def __init__(self, start, end, expr, namespace): + Expr.__init__(self, start, end, expr) + self.namespace = namespace[:] + + +class Using(Node): + def __init__(self, start, end, names): + Node.__init__(self, start, end) + self.names = names + + def __str__(self): + return self._StringHelper(self.__class__.__name__, str(self.names)) + + +class Parameter(Node): + def __init__(self, start, end, name, parameter_type, default): + Node.__init__(self, start, end) + self.name = name + self.type = parameter_type + self.default = default + + def Requires(self, node): + # TODO(nnorwitz): handle namespaces, etc. + return self.type.name == node.name + + def __str__(self): + name = str(self.type) + suffix = '%s %s' % (name, self.name) + if self.default: + suffix += ' = ' + ''.join([d.name for d in self.default]) + return self._StringHelper(self.__class__.__name__, suffix) + + +class _GenericDeclaration(Node): + def __init__(self, start, end, name, namespace): + Node.__init__(self, start, end) + self.name = name + self.namespace = namespace[:] + + def FullName(self): + prefix = '' + if self.namespace and self.namespace[-1]: + prefix = '::'.join(self.namespace) + '::' + return prefix + self.name + + def _TypeStringHelper(self, suffix): + if self.namespace: + names = [n or '' for n in self.namespace] + suffix += ' in ' + '::'.join(names) + return self._StringHelper(self.__class__.__name__, suffix) + + +# TODO(nnorwitz): merge with Parameter in some way? +class VariableDeclaration(_GenericDeclaration): + def __init__(self, start, end, name, var_type, initial_value, namespace): + _GenericDeclaration.__init__(self, start, end, name, namespace) + self.type = var_type + self.initial_value = initial_value + + def Requires(self, node): + # TODO(nnorwitz): handle namespaces, etc. + return self.type.name == node.name + + def ToString(self): + """Return a string that tries to reconstitute the variable decl.""" + suffix = '%s %s' % (self.type, self.name) + if self.initial_value: + suffix += ' = ' + self.initial_value + return suffix + + def __str__(self): + return self._StringHelper(self.__class__.__name__, self.ToString()) + + +class Typedef(_GenericDeclaration): + def __init__(self, start, end, name, alias, namespace): + _GenericDeclaration.__init__(self, start, end, name, namespace) + self.alias = alias + + def IsDefinition(self): + return True + + def IsExportable(self): + return True + + def Requires(self, node): + # TODO(nnorwitz): handle namespaces, etc. + name = node.name + for token in self.alias: + if token is not None and name == token.name: + return True + return False + + def __str__(self): + suffix = '%s, %s' % (self.name, self.alias) + return self._TypeStringHelper(suffix) + + +class _NestedType(_GenericDeclaration): + def __init__(self, start, end, name, fields, namespace): + _GenericDeclaration.__init__(self, start, end, name, namespace) + self.fields = fields + + def IsDefinition(self): + return True + + def IsExportable(self): + return True + + def __str__(self): + suffix = '%s, {%s}' % (self.name, self.fields) + return self._TypeStringHelper(suffix) + + +class Union(_NestedType): + pass + + +class Enum(_NestedType): + pass + + +class Class(_GenericDeclaration): + def __init__(self, start, end, name, bases, templated_types, body, namespace): + _GenericDeclaration.__init__(self, start, end, name, namespace) + self.bases = bases + self.body = body + self.templated_types = templated_types + + def IsDeclaration(self): + return self.bases is None and self.body is None + + def IsDefinition(self): + return not self.IsDeclaration() + + def IsExportable(self): + return not self.IsDeclaration() + + def Requires(self, node): + # TODO(nnorwitz): handle namespaces, etc. + if self.bases: + for token_list in self.bases: + # TODO(nnorwitz): bases are tokens, do name comparision. + for token in token_list: + if token.name == node.name: + return True + # TODO(nnorwitz): search in body too. + return False + + def __str__(self): + name = self.name + if self.templated_types: + name += '<%s>' % self.templated_types + suffix = '%s, %s, %s' % (name, self.bases, self.body) + return self._TypeStringHelper(suffix) + + +class Struct(Class): + pass + + +class Function(_GenericDeclaration): + def __init__(self, start, end, name, return_type, parameters, + modifiers, templated_types, body, namespace): + _GenericDeclaration.__init__(self, start, end, name, namespace) + converter = TypeConverter(namespace) + self.return_type = converter.CreateReturnType(return_type) + self.parameters = converter.ToParameters(parameters) + self.modifiers = modifiers + self.body = body + self.templated_types = templated_types + + def IsDeclaration(self): + return self.body is None + + def IsDefinition(self): + return self.body is not None + + def IsExportable(self): + if self.return_type and 'static' in self.return_type.modifiers: + return False + return None not in self.namespace + + def Requires(self, node): + if self.parameters: + # TODO(nnorwitz): parameters are tokens, do name comparision. + for p in self.parameters: + if p.name == node.name: + return True + # TODO(nnorwitz): search in body too. + return False + + def __str__(self): + # TODO(nnorwitz): add templated_types. + suffix = ('%s %s(%s), 0x%02x, %s' % + (self.return_type, self.name, self.parameters, + self.modifiers, self.body)) + return self._TypeStringHelper(suffix) + + +class Method(Function): + def __init__(self, start, end, name, in_class, return_type, parameters, + modifiers, templated_types, body, namespace): + Function.__init__(self, start, end, name, return_type, parameters, + modifiers, templated_types, body, namespace) + # TODO(nnorwitz): in_class could also be a namespace which can + # mess up finding functions properly. + self.in_class = in_class + + +class Type(_GenericDeclaration): + """Type used for any variable (eg class, primitive, struct, etc).""" + + def __init__(self, start, end, name, templated_types, modifiers, + reference, pointer, array): + """ + Args: + name: str name of main type + templated_types: [Class (Type?)] template type info between <> + modifiers: [str] type modifiers (keywords) eg, const, mutable, etc. + reference, pointer, array: bools + """ + _GenericDeclaration.__init__(self, start, end, name, []) + self.templated_types = templated_types + if not name and modifiers: + self.name = modifiers.pop() + self.modifiers = modifiers + self.reference = reference + self.pointer = pointer + self.array = array + + def __str__(self): + prefix = '' + if self.modifiers: + prefix = ' '.join(self.modifiers) + ' ' + name = str(self.name) + if self.templated_types: + name += '<%s>' % self.templated_types + suffix = prefix + name + if self.reference: + suffix += '&' + if self.pointer: + suffix += '*' + if self.array: + suffix += '[]' + return self._TypeStringHelper(suffix) + + # By definition, Is* are always False. A Type can only exist in + # some sort of variable declaration, parameter, or return value. + def IsDeclaration(self): + return False + + def IsDefinition(self): + return False + + def IsExportable(self): + return False + + +class TypeConverter(object): + + def __init__(self, namespace_stack): + self.namespace_stack = namespace_stack + + def _GetTemplateEnd(self, tokens, start): + count = 1 + end = start + while 1: + token = tokens[end] + end += 1 + if token.name == '<': + count += 1 + elif token.name == '>': + count -= 1 + if count == 0: + break + return tokens[start:end-1], end + + def ToType(self, tokens): + """Convert [Token,...] to [Class(...), ] useful for base classes. + For example, code like class Foo : public Bar { ... }; + the "Bar" portion gets converted to an AST. + + Returns: + [Class(...), ...] + """ + result = [] + name_tokens = [] + reference = pointer = array = False + + def AddType(templated_types): + # Partition tokens into name and modifier tokens. + names = [] + modifiers = [] + for t in name_tokens: + if keywords.IsKeyword(t.name): + modifiers.append(t.name) + else: + names.append(t.name) + name = ''.join(names) + result.append(Type(name_tokens[0].start, name_tokens[-1].end, + name, templated_types, modifiers, + reference, pointer, array)) + del name_tokens[:] + + i = 0 + end = len(tokens) + while i < end: + token = tokens[i] + if token.name == '<': + new_tokens, new_end = self._GetTemplateEnd(tokens, i+1) + AddType(self.ToType(new_tokens)) + # If there is a comma after the template, we need to consume + # that here otherwise it becomes part of the name. + i = new_end + reference = pointer = array = False + elif token.name == ',': + AddType([]) + reference = pointer = array = False + elif token.name == '*': + pointer = True + elif token.name == '&': + reference = True + elif token.name == '[': + pointer = True + elif token.name == ']': + pass + else: + name_tokens.append(token) + i += 1 + + if name_tokens: + # No '<' in the tokens, just a simple name and no template. + AddType([]) + return result + + def DeclarationToParts(self, parts, needs_name_removed): + name = None + default = [] + if needs_name_removed: + # Handle default (initial) values properly. + for i, t in enumerate(parts): + if t.name == '=': + default = parts[i+1:] + name = parts[i-1].name + if name == ']' and parts[i-2].name == '[': + name = parts[i-3].name + i -= 1 + parts = parts[:i-1] + break + else: + if parts[-1].token_type == tokenize.NAME: + name = parts.pop().name + else: + # TODO(nnorwitz): this is a hack that happens for code like + # Register(Foo); where it thinks this is a function call + # but it's actually a declaration. + name = '???' + modifiers = [] + type_name = [] + other_tokens = [] + templated_types = [] + i = 0 + end = len(parts) + while i < end: + p = parts[i] + if keywords.IsKeyword(p.name): + modifiers.append(p.name) + elif p.name == '<': + templated_tokens, new_end = self._GetTemplateEnd(parts, i+1) + templated_types = self.ToType(templated_tokens) + i = new_end - 1 + # Don't add a spurious :: to data members being initialized. + next_index = i + 1 + if next_index < end and parts[next_index].name == '::': + i += 1 + elif p.name in ('[', ']', '='): + # These are handled elsewhere. + other_tokens.append(p) + elif p.name not in ('*', '&', '>'): + # Ensure that names have a space between them. + if (type_name and type_name[-1].token_type == tokenize.NAME and + p.token_type == tokenize.NAME): + type_name.append(tokenize.Token(tokenize.SYNTAX, ' ', 0, 0)) + type_name.append(p) + else: + other_tokens.append(p) + i += 1 + type_name = ''.join([t.name for t in type_name]) + return name, type_name, templated_types, modifiers, default, other_tokens + + def ToParameters(self, tokens): + if not tokens: + return [] + + result = [] + name = type_name = '' + type_modifiers = [] + pointer = reference = array = False + first_token = None + default = [] + + def AddParameter(): + if default: + del default[0] # Remove flag. + end = type_modifiers[-1].end + parts = self.DeclarationToParts(type_modifiers, True) + (name, type_name, templated_types, modifiers, + unused_default, unused_other_tokens) = parts + parameter_type = Type(first_token.start, first_token.end, + type_name, templated_types, modifiers, + reference, pointer, array) + p = Parameter(first_token.start, end, name, + parameter_type, default) + result.append(p) + + template_count = 0 + for s in tokens: + if not first_token: + first_token = s + if s.name == '<': + template_count += 1 + elif s.name == '>': + template_count -= 1 + if template_count > 0: + type_modifiers.append(s) + continue + + if s.name == ',': + AddParameter() + name = type_name = '' + type_modifiers = [] + pointer = reference = array = False + first_token = None + default = [] + elif s.name == '*': + pointer = True + elif s.name == '&': + reference = True + elif s.name == '[': + array = True + elif s.name == ']': + pass # Just don't add to type_modifiers. + elif s.name == '=': + # Got a default value. Add any value (None) as a flag. + default.append(None) + elif default: + default.append(s) + else: + type_modifiers.append(s) + AddParameter() + return result + + def CreateReturnType(self, return_type_seq): + if not return_type_seq: + return None + start = return_type_seq[0].start + end = return_type_seq[-1].end + _, name, templated_types, modifiers, default, other_tokens = \ + self.DeclarationToParts(return_type_seq, False) + names = [n.name for n in other_tokens] + reference = '&' in names + pointer = '*' in names + array = '[' in names + return Type(start, end, name, templated_types, modifiers, + reference, pointer, array) + + def GetTemplateIndices(self, names): + # names is a list of strings. + start = names.index('<') + end = len(names) - 1 + while end > 0: + if names[end] == '>': + break + end -= 1 + return start, end+1 + +class AstBuilder(object): + def __init__(self, token_stream, filename, in_class='', visibility=None, + namespace_stack=[]): + self.tokens = token_stream + self.filename = filename + # TODO(nnorwitz): use a better data structure (deque) for the queue. + # Switching directions of the "queue" improved perf by about 25%. + # Using a deque should be even better since we access from both sides. + self.token_queue = [] + self.namespace_stack = namespace_stack[:] + self.in_class = in_class + if in_class is None: + self.in_class_name_only = None + else: + self.in_class_name_only = in_class.split('::')[-1] + self.visibility = visibility + self.in_function = False + self.current_token = None + # Keep the state whether we are currently handling a typedef or not. + self._handling_typedef = False + + self.converter = TypeConverter(self.namespace_stack) + + def HandleError(self, msg, token): + printable_queue = list(reversed(self.token_queue[-20:])) + sys.stderr.write('Got %s in %s @ %s %s\n' % + (msg, self.filename, token, printable_queue)) + + def Generate(self): + while 1: + token = self._GetNextToken() + if not token: + break + + # Get the next token. + self.current_token = token + + # Dispatch on the next token type. + if token.token_type == _INTERNAL_TOKEN: + if token.name == _NAMESPACE_POP: + self.namespace_stack.pop() + continue + + try: + result = self._GenerateOne(token) + if result is not None: + yield result + except: + self.HandleError('exception', token) + raise + + def _CreateVariable(self, pos_token, name, type_name, type_modifiers, + ref_pointer_name_seq, templated_types, value=None): + reference = '&' in ref_pointer_name_seq + pointer = '*' in ref_pointer_name_seq + array = '[' in ref_pointer_name_seq + var_type = Type(pos_token.start, pos_token.end, type_name, + templated_types, type_modifiers, + reference, pointer, array) + return VariableDeclaration(pos_token.start, pos_token.end, + name, var_type, value, self.namespace_stack) + + def _GenerateOne(self, token): + if token.token_type == tokenize.NAME: + if (keywords.IsKeyword(token.name) and + not keywords.IsBuiltinType(token.name)): + method = getattr(self, 'handle_' + token.name) + return method() + elif token.name == self.in_class_name_only: + # The token name is the same as the class, must be a ctor if + # there is a paren. Otherwise, it's the return type. + # Peek ahead to get the next token to figure out which. + next = self._GetNextToken() + self._AddBackToken(next) + if next.token_type == tokenize.SYNTAX and next.name == '(': + return self._GetMethod([token], FUNCTION_CTOR, None, True) + # Fall through--handle like any other method. + + # Handle data or function declaration/definition. + syntax = tokenize.SYNTAX + temp_tokens, last_token = \ + self._GetVarTokensUpTo(syntax, '(', ';', '{', '[') + temp_tokens.insert(0, token) + if last_token.name == '(': + # If there is an assignment before the paren, + # this is an expression, not a method. + expr = bool([e for e in temp_tokens if e.name == '=']) + if expr: + new_temp = self._GetTokensUpTo(tokenize.SYNTAX, ';') + temp_tokens.append(last_token) + temp_tokens.extend(new_temp) + last_token = tokenize.Token(tokenize.SYNTAX, ';', 0, 0) + + if last_token.name == '[': + # Handle array, this isn't a method, unless it's an operator. + # TODO(nnorwitz): keep the size somewhere. + # unused_size = self._GetTokensUpTo(tokenize.SYNTAX, ']') + temp_tokens.append(last_token) + if temp_tokens[-2].name == 'operator': + temp_tokens.append(self._GetNextToken()) + else: + temp_tokens2, last_token = \ + self._GetVarTokensUpTo(tokenize.SYNTAX, ';') + temp_tokens.extend(temp_tokens2) + + if last_token.name == ';': + # Handle data, this isn't a method. + parts = self.converter.DeclarationToParts(temp_tokens, True) + (name, type_name, templated_types, modifiers, default, + unused_other_tokens) = parts + + t0 = temp_tokens[0] + names = [t.name for t in temp_tokens] + if templated_types: + start, end = self.converter.GetTemplateIndices(names) + names = names[:start] + names[end:] + default = ''.join([t.name for t in default]) + return self._CreateVariable(t0, name, type_name, modifiers, + names, templated_types, default) + if last_token.name == '{': + self._AddBackTokens(temp_tokens[1:]) + self._AddBackToken(last_token) + method_name = temp_tokens[0].name + method = getattr(self, 'handle_' + method_name, None) + if not method: + # Must be declaring a variable. + # TODO(nnorwitz): handle the declaration. + return None + return method() + return self._GetMethod(temp_tokens, 0, None, False) + elif token.token_type == tokenize.SYNTAX: + if token.name == '~' and self.in_class: + # Must be a dtor (probably not in method body). + token = self._GetNextToken() + # self.in_class can contain A::Name, but the dtor will only + # be Name. Make sure to compare against the right value. + if (token.token_type == tokenize.NAME and + token.name == self.in_class_name_only): + return self._GetMethod([token], FUNCTION_DTOR, None, True) + # TODO(nnorwitz): handle a lot more syntax. + elif token.token_type == tokenize.PREPROCESSOR: + # TODO(nnorwitz): handle more preprocessor directives. + # token starts with a #, so remove it and strip whitespace. + name = token.name[1:].lstrip() + if name.startswith('include'): + # Remove "include". + name = name[7:].strip() + assert name + # Handle #include \ "header-on-second-line.h". + if name.startswith('\\'): + name = name[1:].strip() + assert name[0] in '<"', token + assert name[-1] in '>"', token + system = name[0] == '<' + filename = name[1:-1] + return Include(token.start, token.end, filename, system) + if name.startswith('define'): + # Remove "define". + name = name[6:].strip() + assert name + value = '' + for i, c in enumerate(name): + if c.isspace(): + value = name[i:].lstrip() + name = name[:i] + break + return Define(token.start, token.end, name, value) + if name.startswith('if') and name[2:3].isspace(): + condition = name[3:].strip() + if condition.startswith('0') or condition.startswith('(0)'): + self._SkipIf0Blocks() + return None + + def _GetTokensUpTo(self, expected_token_type, expected_token): + return self._GetVarTokensUpTo(expected_token_type, expected_token)[0] + + def _GetVarTokensUpTo(self, expected_token_type, *expected_tokens): + last_token = self._GetNextToken() + tokens = [] + while (last_token.token_type != expected_token_type or + last_token.name not in expected_tokens): + tokens.append(last_token) + last_token = self._GetNextToken() + return tokens, last_token + + # TODO(nnorwitz): remove _IgnoreUpTo() it shouldn't be necesary. + def _IgnoreUpTo(self, token_type, token): + unused_tokens = self._GetTokensUpTo(token_type, token) + + def _SkipIf0Blocks(self): + count = 1 + while 1: + token = self._GetNextToken() + if token.token_type != tokenize.PREPROCESSOR: + continue + + name = token.name[1:].lstrip() + if name.startswith('endif'): + count -= 1 + if count == 0: + break + elif name.startswith('if'): + count += 1 + + def _GetMatchingChar(self, open_paren, close_paren, GetNextToken=None): + if GetNextToken is None: + GetNextToken = self._GetNextToken + # Assumes the current token is open_paren and we will consume + # and return up to the close_paren. + count = 1 + token = GetNextToken() + while 1: + if token.token_type == tokenize.SYNTAX: + if token.name == open_paren: + count += 1 + elif token.name == close_paren: + count -= 1 + if count == 0: + break + yield token + token = GetNextToken() + yield token + + def _GetParameters(self): + return self._GetMatchingChar('(', ')') + + def GetScope(self): + return self._GetMatchingChar('{', '}') + + def _GetNextToken(self): + if self.token_queue: + return self.token_queue.pop() + return next(self.tokens) + + def _AddBackToken(self, token): + if token.whence == tokenize.WHENCE_STREAM: + token.whence = tokenize.WHENCE_QUEUE + self.token_queue.insert(0, token) + else: + assert token.whence == tokenize.WHENCE_QUEUE, token + self.token_queue.append(token) + + def _AddBackTokens(self, tokens): + if tokens: + if tokens[-1].whence == tokenize.WHENCE_STREAM: + for token in tokens: + token.whence = tokenize.WHENCE_QUEUE + self.token_queue[:0] = reversed(tokens) + else: + assert tokens[-1].whence == tokenize.WHENCE_QUEUE, tokens + self.token_queue.extend(reversed(tokens)) + + def GetName(self, seq=None): + """Returns ([tokens], next_token_info).""" + GetNextToken = self._GetNextToken + if seq is not None: + it = iter(seq) + GetNextToken = lambda: next(it) + next_token = GetNextToken() + tokens = [] + last_token_was_name = False + while (next_token.token_type == tokenize.NAME or + (next_token.token_type == tokenize.SYNTAX and + next_token.name in ('::', '<'))): + # Two NAMEs in a row means the identifier should terminate. + # It's probably some sort of variable declaration. + if last_token_was_name and next_token.token_type == tokenize.NAME: + break + last_token_was_name = next_token.token_type == tokenize.NAME + tokens.append(next_token) + # Handle templated names. + if next_token.name == '<': + tokens.extend(self._GetMatchingChar('<', '>', GetNextToken)) + last_token_was_name = True + next_token = GetNextToken() + return tokens, next_token + + def GetMethod(self, modifiers, templated_types): + return_type_and_name = self._GetTokensUpTo(tokenize.SYNTAX, '(') + assert len(return_type_and_name) >= 1 + return self._GetMethod(return_type_and_name, modifiers, templated_types, + False) + + def _GetMethod(self, return_type_and_name, modifiers, templated_types, + get_paren): + template_portion = None + if get_paren: + token = self._GetNextToken() + assert token.token_type == tokenize.SYNTAX, token + if token.name == '<': + # Handle templatized dtors. + template_portion = [token] + template_portion.extend(self._GetMatchingChar('<', '>')) + token = self._GetNextToken() + assert token.token_type == tokenize.SYNTAX, token + assert token.name == '(', token + + name = return_type_and_name.pop() + # Handle templatized ctors. + if name.name == '>': + index = 1 + while return_type_and_name[index].name != '<': + index += 1 + template_portion = return_type_and_name[index:] + [name] + del return_type_and_name[index:] + name = return_type_and_name.pop() + elif name.name == ']': + rt = return_type_and_name + assert rt[-1].name == '[', return_type_and_name + assert rt[-2].name == 'operator', return_type_and_name + name_seq = return_type_and_name[-2:] + del return_type_and_name[-2:] + name = tokenize.Token(tokenize.NAME, 'operator[]', + name_seq[0].start, name.end) + # Get the open paren so _GetParameters() below works. + unused_open_paren = self._GetNextToken() + + # TODO(nnorwitz): store template_portion. + return_type = return_type_and_name + indices = name + if return_type: + indices = return_type[0] + + # Force ctor for templatized ctors. + if name.name == self.in_class and not modifiers: + modifiers |= FUNCTION_CTOR + parameters = list(self._GetParameters()) + del parameters[-1] # Remove trailing ')'. + + # Handling operator() is especially weird. + if name.name == 'operator' and not parameters: + token = self._GetNextToken() + assert token.name == '(', token + parameters = list(self._GetParameters()) + del parameters[-1] # Remove trailing ')'. + + token = self._GetNextToken() + while token.token_type == tokenize.NAME: + modifier_token = token + token = self._GetNextToken() + if modifier_token.name == 'const': + modifiers |= FUNCTION_CONST + elif modifier_token.name == '__attribute__': + # TODO(nnorwitz): handle more __attribute__ details. + modifiers |= FUNCTION_ATTRIBUTE + assert token.name == '(', token + # Consume everything between the (parens). + unused_tokens = list(self._GetMatchingChar('(', ')')) + token = self._GetNextToken() + elif modifier_token.name == 'throw': + modifiers |= FUNCTION_THROW + assert token.name == '(', token + # Consume everything between the (parens). + unused_tokens = list(self._GetMatchingChar('(', ')')) + token = self._GetNextToken() + elif modifier_token.name == modifier_token.name.upper(): + # HACK(nnorwitz): assume that all upper-case names + # are some macro we aren't expanding. + modifiers |= FUNCTION_UNKNOWN_ANNOTATION + else: + self.HandleError('unexpected token', modifier_token) + + assert token.token_type == tokenize.SYNTAX, token + # Handle ctor initializers. + if token.name == ':': + # TODO(nnorwitz): anything else to handle for initializer list? + while token.name != ';' and token.name != '{': + token = self._GetNextToken() + + # Handle pointer to functions that are really data but look + # like method declarations. + if token.name == '(': + if parameters[0].name == '*': + # name contains the return type. + name = parameters.pop() + # parameters contains the name of the data. + modifiers = [p.name for p in parameters] + # Already at the ( to open the parameter list. + function_parameters = list(self._GetMatchingChar('(', ')')) + del function_parameters[-1] # Remove trailing ')'. + # TODO(nnorwitz): store the function_parameters. + token = self._GetNextToken() + assert token.token_type == tokenize.SYNTAX, token + assert token.name == ';', token + return self._CreateVariable(indices, name.name, indices.name, + modifiers, '', None) + # At this point, we got something like: + # return_type (type::*name_)(params); + # This is a data member called name_ that is a function pointer. + # With this code: void (sq_type::*field_)(string&); + # We get: name=void return_type=[] parameters=sq_type ... field_ + # TODO(nnorwitz): is return_type always empty? + # TODO(nnorwitz): this isn't even close to being correct. + # Just put in something so we don't crash and can move on. + real_name = parameters[-1] + modifiers = [p.name for p in self._GetParameters()] + del modifiers[-1] # Remove trailing ')'. + return self._CreateVariable(indices, real_name.name, indices.name, + modifiers, '', None) + + if token.name == '{': + body = list(self.GetScope()) + del body[-1] # Remove trailing '}'. + else: + body = None + if token.name == '=': + token = self._GetNextToken() + assert token.token_type == tokenize.CONSTANT, token + assert token.name == '0', token + modifiers |= FUNCTION_PURE_VIRTUAL + token = self._GetNextToken() + + if token.name == '[': + # TODO(nnorwitz): store tokens and improve parsing. + # template char (&ASH(T (&seq)[N]))[N]; + tokens = list(self._GetMatchingChar('[', ']')) + token = self._GetNextToken() + + assert token.name == ';', (token, return_type_and_name, parameters) + + # Looks like we got a method, not a function. + if len(return_type) > 2 and return_type[-1].name == '::': + return_type, in_class = \ + self._GetReturnTypeAndClassName(return_type) + return Method(indices.start, indices.end, name.name, in_class, + return_type, parameters, modifiers, templated_types, + body, self.namespace_stack) + return Function(indices.start, indices.end, name.name, return_type, + parameters, modifiers, templated_types, body, + self.namespace_stack) + + def _GetReturnTypeAndClassName(self, token_seq): + # Splitting the return type from the class name in a method + # can be tricky. For example, Return::Type::Is::Hard::To::Find(). + # Where is the return type and where is the class name? + # The heuristic used is to pull the last name as the class name. + # This includes all the templated type info. + # TODO(nnorwitz): if there is only One name like in the + # example above, punt and assume the last bit is the class name. + + # Ignore a :: prefix, if exists so we can find the first real name. + i = 0 + if token_seq[0].name == '::': + i = 1 + # Ignore a :: suffix, if exists. + end = len(token_seq) - 1 + if token_seq[end-1].name == '::': + end -= 1 + + # Make a copy of the sequence so we can append a sentinel + # value. This is required for GetName will has to have some + # terminating condition beyond the last name. + seq_copy = token_seq[i:end] + seq_copy.append(tokenize.Token(tokenize.SYNTAX, '', 0, 0)) + names = [] + while i < end: + # Iterate through the sequence parsing out each name. + new_name, next = self.GetName(seq_copy[i:]) + assert new_name, 'Got empty new_name, next=%s' % next + # We got a pointer or ref. Add it to the name. + if next and next.token_type == tokenize.SYNTAX: + new_name.append(next) + names.append(new_name) + i += len(new_name) + + # Now that we have the names, it's time to undo what we did. + + # Remove the sentinel value. + names[-1].pop() + # Flatten the token sequence for the return type. + return_type = [e for seq in names[:-1] for e in seq] + # The class name is the last name. + class_name = names[-1] + return return_type, class_name + + def handle_bool(self): + pass + + def handle_char(self): + pass + + def handle_int(self): + pass + + def handle_long(self): + pass + + def handle_short(self): + pass + + def handle_double(self): + pass + + def handle_float(self): + pass + + def handle_void(self): + pass + + def handle_wchar_t(self): + pass + + def handle_unsigned(self): + pass + + def handle_signed(self): + pass + + def _GetNestedType(self, ctor): + name = None + name_tokens, token = self.GetName() + if name_tokens: + name = ''.join([t.name for t in name_tokens]) + + # Handle forward declarations. + if token.token_type == tokenize.SYNTAX and token.name == ';': + return ctor(token.start, token.end, name, None, + self.namespace_stack) + + if token.token_type == tokenize.NAME and self._handling_typedef: + self._AddBackToken(token) + return ctor(token.start, token.end, name, None, + self.namespace_stack) + + # Must be the type declaration. + fields = list(self._GetMatchingChar('{', '}')) + del fields[-1] # Remove trailing '}'. + if token.token_type == tokenize.SYNTAX and token.name == '{': + next = self._GetNextToken() + new_type = ctor(token.start, token.end, name, fields, + self.namespace_stack) + # A name means this is an anonymous type and the name + # is the variable declaration. + if next.token_type != tokenize.NAME: + return new_type + name = new_type + token = next + + # Must be variable declaration using the type prefixed with keyword. + assert token.token_type == tokenize.NAME, token + return self._CreateVariable(token, token.name, name, [], '', None) + + def handle_struct(self): + # Special case the handling typedef/aliasing of structs here. + # It would be a pain to handle in the class code. + name_tokens, var_token = self.GetName() + if name_tokens: + next_token = self._GetNextToken() + is_syntax = (var_token.token_type == tokenize.SYNTAX and + var_token.name[0] in '*&') + is_variable = (var_token.token_type == tokenize.NAME and + next_token.name == ';') + variable = var_token + if is_syntax and not is_variable: + variable = next_token + temp = self._GetNextToken() + if temp.token_type == tokenize.SYNTAX and temp.name == '(': + # Handle methods declared to return a struct. + t0 = name_tokens[0] + struct = tokenize.Token(tokenize.NAME, 'struct', + t0.start-7, t0.start-2) + type_and_name = [struct] + type_and_name.extend(name_tokens) + type_and_name.extend((var_token, next_token)) + return self._GetMethod(type_and_name, 0, None, False) + assert temp.name == ';', (temp, name_tokens, var_token) + if is_syntax or (is_variable and not self._handling_typedef): + modifiers = ['struct'] + type_name = ''.join([t.name for t in name_tokens]) + position = name_tokens[0] + return self._CreateVariable(position, variable.name, type_name, + modifiers, var_token.name, None) + name_tokens.extend((var_token, next_token)) + self._AddBackTokens(name_tokens) + else: + self._AddBackToken(var_token) + return self._GetClass(Struct, VISIBILITY_PUBLIC, None) + + def handle_union(self): + return self._GetNestedType(Union) + + def handle_enum(self): + return self._GetNestedType(Enum) + + def handle_auto(self): + # TODO(nnorwitz): warn about using auto? Probably not since it + # will be reclaimed and useful for C++0x. + pass + + def handle_register(self): + pass + + def handle_const(self): + pass + + def handle_inline(self): + pass + + def handle_extern(self): + pass + + def handle_static(self): + pass + + def handle_virtual(self): + # What follows must be a method. + token = token2 = self._GetNextToken() + if token.name == 'inline': + # HACK(nnorwitz): handle inline dtors by ignoring 'inline'. + token2 = self._GetNextToken() + if token2.token_type == tokenize.SYNTAX and token2.name == '~': + return self.GetMethod(FUNCTION_VIRTUAL + FUNCTION_DTOR, None) + assert token.token_type == tokenize.NAME or token.name == '::', token + return_type_and_name = self._GetTokensUpTo(tokenize.SYNTAX, '(') + return_type_and_name.insert(0, token) + if token2 is not token: + return_type_and_name.insert(1, token2) + return self._GetMethod(return_type_and_name, FUNCTION_VIRTUAL, + None, False) + + def handle_volatile(self): + pass + + def handle_mutable(self): + pass + + def handle_public(self): + assert self.in_class + self.visibility = VISIBILITY_PUBLIC + + def handle_protected(self): + assert self.in_class + self.visibility = VISIBILITY_PROTECTED + + def handle_private(self): + assert self.in_class + self.visibility = VISIBILITY_PRIVATE + + def handle_friend(self): + tokens = self._GetTokensUpTo(tokenize.SYNTAX, ';') + assert tokens + t0 = tokens[0] + return Friend(t0.start, t0.end, tokens, self.namespace_stack) + + def handle_static_cast(self): + pass + + def handle_const_cast(self): + pass + + def handle_dynamic_cast(self): + pass + + def handle_reinterpret_cast(self): + pass + + def handle_new(self): + pass + + def handle_delete(self): + tokens = self._GetTokensUpTo(tokenize.SYNTAX, ';') + assert tokens + return Delete(tokens[0].start, tokens[0].end, tokens) + + def handle_typedef(self): + token = self._GetNextToken() + if (token.token_type == tokenize.NAME and + keywords.IsKeyword(token.name)): + # Token must be struct/enum/union/class. + method = getattr(self, 'handle_' + token.name) + self._handling_typedef = True + tokens = [method()] + self._handling_typedef = False + else: + tokens = [token] + + # Get the remainder of the typedef up to the semi-colon. + tokens.extend(self._GetTokensUpTo(tokenize.SYNTAX, ';')) + + # TODO(nnorwitz): clean all this up. + assert tokens + name = tokens.pop() + indices = name + if tokens: + indices = tokens[0] + if not indices: + indices = token + if name.name == ')': + # HACK(nnorwitz): Handle pointers to functions "properly". + if (len(tokens) >= 4 and + tokens[1].name == '(' and tokens[2].name == '*'): + tokens.append(name) + name = tokens[3] + elif name.name == ']': + # HACK(nnorwitz): Handle arrays properly. + if len(tokens) >= 2: + tokens.append(name) + name = tokens[1] + new_type = tokens + if tokens and isinstance(tokens[0], tokenize.Token): + new_type = self.converter.ToType(tokens)[0] + return Typedef(indices.start, indices.end, name.name, + new_type, self.namespace_stack) + + def handle_typeid(self): + pass # Not needed yet. + + def handle_typename(self): + pass # Not needed yet. + + def _GetTemplatedTypes(self): + result = {} + tokens = list(self._GetMatchingChar('<', '>')) + len_tokens = len(tokens) - 1 # Ignore trailing '>'. + i = 0 + while i < len_tokens: + key = tokens[i].name + i += 1 + if keywords.IsKeyword(key) or key == ',': + continue + type_name = default = None + if i < len_tokens: + i += 1 + if tokens[i-1].name == '=': + assert i < len_tokens, '%s %s' % (i, tokens) + default, unused_next_token = self.GetName(tokens[i:]) + i += len(default) + else: + if tokens[i-1].name != ',': + # We got something like: Type variable. + # Re-adjust the key (variable) and type_name (Type). + key = tokens[i-1].name + type_name = tokens[i-2] + + result[key] = (type_name, default) + return result + + def handle_template(self): + token = self._GetNextToken() + assert token.token_type == tokenize.SYNTAX, token + assert token.name == '<', token + templated_types = self._GetTemplatedTypes() + # TODO(nnorwitz): for now, just ignore the template params. + token = self._GetNextToken() + if token.token_type == tokenize.NAME: + if token.name == 'class': + return self._GetClass(Class, VISIBILITY_PRIVATE, templated_types) + elif token.name == 'struct': + return self._GetClass(Struct, VISIBILITY_PUBLIC, templated_types) + elif token.name == 'friend': + return self.handle_friend() + self._AddBackToken(token) + tokens, last = self._GetVarTokensUpTo(tokenize.SYNTAX, '(', ';') + tokens.append(last) + self._AddBackTokens(tokens) + if last.name == '(': + return self.GetMethod(FUNCTION_NONE, templated_types) + # Must be a variable definition. + return None + + def handle_true(self): + pass # Nothing to do. + + def handle_false(self): + pass # Nothing to do. + + def handle_asm(self): + pass # Not needed yet. + + def handle_class(self): + return self._GetClass(Class, VISIBILITY_PRIVATE, None) + + def _GetBases(self): + # Get base classes. + bases = [] + while 1: + token = self._GetNextToken() + assert token.token_type == tokenize.NAME, token + # TODO(nnorwitz): store kind of inheritance...maybe. + if token.name not in ('public', 'protected', 'private'): + # If inheritance type is not specified, it is private. + # Just put the token back so we can form a name. + # TODO(nnorwitz): it would be good to warn about this. + self._AddBackToken(token) + else: + # Check for virtual inheritance. + token = self._GetNextToken() + if token.name != 'virtual': + self._AddBackToken(token) + else: + # TODO(nnorwitz): store that we got virtual for this base. + pass + base, next_token = self.GetName() + bases_ast = self.converter.ToType(base) + assert len(bases_ast) == 1, bases_ast + bases.append(bases_ast[0]) + assert next_token.token_type == tokenize.SYNTAX, next_token + if next_token.name == '{': + token = next_token + break + # Support multiple inheritance. + assert next_token.name == ',', next_token + return bases, token + + def _GetClass(self, class_type, visibility, templated_types): + class_name = None + class_token = self._GetNextToken() + if class_token.token_type != tokenize.NAME: + assert class_token.token_type == tokenize.SYNTAX, class_token + token = class_token + else: + # Skip any macro (e.g. storage class specifiers) after the + # 'class' keyword. + next_token = self._GetNextToken() + if next_token.token_type == tokenize.NAME: + self._AddBackToken(next_token) + else: + self._AddBackTokens([class_token, next_token]) + name_tokens, token = self.GetName() + class_name = ''.join([t.name for t in name_tokens]) + bases = None + if token.token_type == tokenize.SYNTAX: + if token.name == ';': + # Forward declaration. + return class_type(class_token.start, class_token.end, + class_name, None, templated_types, None, + self.namespace_stack) + if token.name in '*&': + # Inline forward declaration. Could be method or data. + name_token = self._GetNextToken() + next_token = self._GetNextToken() + if next_token.name == ';': + # Handle data + modifiers = ['class'] + return self._CreateVariable(class_token, name_token.name, + class_name, + modifiers, token.name, None) + else: + # Assume this is a method. + tokens = (class_token, token, name_token, next_token) + self._AddBackTokens(tokens) + return self.GetMethod(FUNCTION_NONE, None) + if token.name == ':': + bases, token = self._GetBases() + + body = None + if token.token_type == tokenize.SYNTAX and token.name == '{': + assert token.token_type == tokenize.SYNTAX, token + assert token.name == '{', token + + ast = AstBuilder(self.GetScope(), self.filename, class_name, + visibility, self.namespace_stack) + body = list(ast.Generate()) + + if not self._handling_typedef: + token = self._GetNextToken() + if token.token_type != tokenize.NAME: + assert token.token_type == tokenize.SYNTAX, token + assert token.name == ';', token + else: + new_class = class_type(class_token.start, class_token.end, + class_name, bases, None, + body, self.namespace_stack) + + modifiers = [] + return self._CreateVariable(class_token, + token.name, new_class, + modifiers, token.name, None) + else: + if not self._handling_typedef: + self.HandleError('non-typedef token', token) + self._AddBackToken(token) + + return class_type(class_token.start, class_token.end, class_name, + bases, None, body, self.namespace_stack) + + def handle_namespace(self): + token = self._GetNextToken() + # Support anonymous namespaces. + name = None + if token.token_type == tokenize.NAME: + name = token.name + token = self._GetNextToken() + self.namespace_stack.append(name) + assert token.token_type == tokenize.SYNTAX, token + # Create an internal token that denotes when the namespace is complete. + internal_token = tokenize.Token(_INTERNAL_TOKEN, _NAMESPACE_POP, + None, None) + internal_token.whence = token.whence + if token.name == '=': + # TODO(nnorwitz): handle aliasing namespaces. + name, next_token = self.GetName() + assert next_token.name == ';', next_token + self._AddBackToken(internal_token) + else: + assert token.name == '{', token + tokens = list(self.GetScope()) + # Replace the trailing } with the internal namespace pop token. + tokens[-1] = internal_token + # Handle namespace with nothing in it. + self._AddBackTokens(tokens) + return None + + def handle_using(self): + tokens = self._GetTokensUpTo(tokenize.SYNTAX, ';') + assert tokens + return Using(tokens[0].start, tokens[0].end, tokens) + + def handle_explicit(self): + assert self.in_class + # Nothing much to do. + # TODO(nnorwitz): maybe verify the method name == class name. + # This must be a ctor. + return self.GetMethod(FUNCTION_CTOR, None) + + def handle_this(self): + pass # Nothing to do. + + def handle_operator(self): + # Pull off the next token(s?) and make that part of the method name. + pass + + def handle_sizeof(self): + pass + + def handle_case(self): + pass + + def handle_switch(self): + pass + + def handle_default(self): + token = self._GetNextToken() + assert token.token_type == tokenize.SYNTAX + assert token.name == ':' + + def handle_if(self): + pass + + def handle_else(self): + pass + + def handle_return(self): + tokens = self._GetTokensUpTo(tokenize.SYNTAX, ';') + if not tokens: + return Return(self.current_token.start, self.current_token.end, None) + return Return(tokens[0].start, tokens[0].end, tokens) + + def handle_goto(self): + tokens = self._GetTokensUpTo(tokenize.SYNTAX, ';') + assert len(tokens) == 1, str(tokens) + return Goto(tokens[0].start, tokens[0].end, tokens[0].name) + + def handle_try(self): + pass # Not needed yet. + + def handle_catch(self): + pass # Not needed yet. + + def handle_throw(self): + pass # Not needed yet. + + def handle_while(self): + pass + + def handle_do(self): + pass + + def handle_for(self): + pass + + def handle_break(self): + self._IgnoreUpTo(tokenize.SYNTAX, ';') + + def handle_continue(self): + self._IgnoreUpTo(tokenize.SYNTAX, ';') + + +def BuilderFromSource(source, filename): + """Utility method that returns an AstBuilder from source code. + + Args: + source: 'C++ source code' + filename: 'file1' + + Returns: + AstBuilder + """ + return AstBuilder(tokenize.GetTokens(source), filename) + + +def PrintIndentifiers(filename, should_print): + """Prints all identifiers for a C++ source file. + + Args: + filename: 'file1' + should_print: predicate with signature: bool Function(token) + """ + source = utils.ReadFile(filename, False) + if source is None: + sys.stderr.write('Unable to find: %s\n' % filename) + return + + #print('Processing %s' % actual_filename) + builder = BuilderFromSource(source, filename) + try: + for node in builder.Generate(): + if should_print(node): + print(node.name) + except KeyboardInterrupt: + return + except: + pass + + +def PrintAllIndentifiers(filenames, should_print): + """Prints all identifiers for each C++ source file in filenames. + + Args: + filenames: ['file1', 'file2', ...] + should_print: predicate with signature: bool Function(token) + """ + for path in filenames: + PrintIndentifiers(path, should_print) + + +def main(argv): + for filename in argv[1:]: + source = utils.ReadFile(filename) + if source is None: + continue + + print('Processing %s' % filename) + builder = BuilderFromSource(source, filename) + try: + entire_ast = filter(None, builder.Generate()) + except KeyboardInterrupt: + return + except: + # Already printed a warning, print the traceback and continue. + traceback.print_exc() + else: + if utils.DEBUG: + for ast in entire_ast: + print(ast) + + +if __name__ == '__main__': + main(sys.argv) diff --git a/libwvdrmengine/test/gmock/scripts/generator/cpp/gmock_class.py b/libwvdrmengine/test/gmock/scripts/generator/cpp/gmock_class.py new file mode 100755 index 00000000..645c295b --- /dev/null +++ b/libwvdrmengine/test/gmock/scripts/generator/cpp/gmock_class.py @@ -0,0 +1,192 @@ +#!/usr/bin/env python +# +# Copyright 2008 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Generate Google Mock classes from base classes. + +This program will read in a C++ source file and output the Google Mock +classes for the specified classes. If no class is specified, all +classes in the source file are emitted. + +Usage: + gmock_class.py header-file.h [ClassName]... + +Output is sent to stdout. +""" + +__author__ = 'nnorwitz@google.com (Neal Norwitz)' + + +import os +import re +import sys + +from cpp import ast +from cpp import utils + +# Preserve compatibility with Python 2.3. +try: + _dummy = set +except NameError: + import sets + set = sets.Set + +_VERSION = (1, 0, 1) # The version of this script. +# How many spaces to indent. Can set me with the INDENT environment variable. +_INDENT = 2 + + +def _GenerateMethods(output_lines, source, class_node): + function_type = ast.FUNCTION_VIRTUAL | ast.FUNCTION_PURE_VIRTUAL + ctor_or_dtor = ast.FUNCTION_CTOR | ast.FUNCTION_DTOR + indent = ' ' * _INDENT + + for node in class_node.body: + # We only care about virtual functions. + if (isinstance(node, ast.Function) and + node.modifiers & function_type and + not node.modifiers & ctor_or_dtor): + # Pick out all the elements we need from the original function. + const = '' + if node.modifiers & ast.FUNCTION_CONST: + const = 'CONST_' + return_type = 'void' + if node.return_type: + # Add modifiers like 'const'. + modifiers = '' + if node.return_type.modifiers: + modifiers = ' '.join(node.return_type.modifiers) + ' ' + return_type = modifiers + node.return_type.name + template_args = [arg.name for arg in node.return_type.templated_types] + if template_args: + return_type += '<' + ', '.join(template_args) + '>' + if len(template_args) > 1: + for line in [ + '// The following line won\'t really compile, as the return', + '// type has multiple template arguments. To fix it, use a', + '// typedef for the return type.']: + output_lines.append(indent + line) + if node.return_type.pointer: + return_type += '*' + if node.return_type.reference: + return_type += '&' + mock_method_macro = 'MOCK_%sMETHOD%d' % (const, len(node.parameters)) + args = '' + if node.parameters: + # Get the full text of the parameters from the start + # of the first parameter to the end of the last parameter. + start = node.parameters[0].start + end = node.parameters[-1].end + # Remove // comments. + args_strings = re.sub(r'//.*', '', source[start:end]) + # Condense multiple spaces and eliminate newlines putting the + # parameters together on a single line. Ensure there is a + # space in an argument which is split by a newline without + # intervening whitespace, e.g.: int\nBar + args = re.sub(' +', ' ', args_strings.replace('\n', ' ')) + + # Create the mock method definition. + output_lines.extend(['%s%s(%s,' % (indent, mock_method_macro, node.name), + '%s%s(%s));' % (indent*3, return_type, args)]) + + +def _GenerateMocks(filename, source, ast_list, desired_class_names): + processed_class_names = set() + lines = [] + for node in ast_list: + if (isinstance(node, ast.Class) and node.body and + # desired_class_names being None means that all classes are selected. + (not desired_class_names or node.name in desired_class_names)): + class_name = node.name + processed_class_names.add(class_name) + class_node = node + # Add namespace before the class. + if class_node.namespace: + lines.extend(['namespace %s {' % n for n in class_node.namespace]) # } + lines.append('') + + # Add the class prolog. + lines.append('class Mock%s : public %s {' % (class_name, class_name)) # } + lines.append('%spublic:' % (' ' * (_INDENT // 2))) + + # Add all the methods. + _GenerateMethods(lines, source, class_node) + + # Close the class. + if lines: + # If there are no virtual methods, no need for a public label. + if len(lines) == 2: + del lines[-1] + + # Only close the class if there really is a class. + lines.append('};') + lines.append('') # Add an extra newline. + + # Close the namespace. + if class_node.namespace: + for i in range(len(class_node.namespace)-1, -1, -1): + lines.append('} // namespace %s' % class_node.namespace[i]) + lines.append('') # Add an extra newline. + + if desired_class_names: + missing_class_name_list = list(desired_class_names - processed_class_names) + if missing_class_name_list: + missing_class_name_list.sort() + sys.stderr.write('Class(es) not found in %s: %s\n' % + (filename, ', '.join(missing_class_name_list))) + elif not processed_class_names: + sys.stderr.write('No class found in %s\n' % filename) + + return lines + + +def main(argv=sys.argv): + if len(argv) < 2: + sys.stderr.write('Google Mock Class Generator v%s\n\n' % + '.'.join(map(str, _VERSION))) + sys.stderr.write(__doc__) + return 1 + + global _INDENT + try: + _INDENT = int(os.environ['INDENT']) + except KeyError: + pass + except: + sys.stderr.write('Unable to use indent of %s\n' % os.environ.get('INDENT')) + + filename = argv[1] + desired_class_names = None # None means all classes in the source file. + if len(argv) >= 3: + desired_class_names = set(argv[2:]) + source = utils.ReadFile(filename) + if source is None: + return 1 + + builder = ast.BuilderFromSource(source, filename) + try: + entire_ast = filter(None, builder.Generate()) + except KeyboardInterrupt: + return + except: + # An error message was already printed since we couldn't parse. + pass + else: + lines = _GenerateMocks(filename, source, entire_ast, desired_class_names) + sys.stdout.write('\n'.join(lines)) + + +if __name__ == '__main__': + main(sys.argv) diff --git a/libwvdrmengine/test/gmock/scripts/generator/cpp/keywords.py b/libwvdrmengine/test/gmock/scripts/generator/cpp/keywords.py new file mode 100755 index 00000000..f694450e --- /dev/null +++ b/libwvdrmengine/test/gmock/scripts/generator/cpp/keywords.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python +# +# Copyright 2007 Neal Norwitz +# Portions Copyright 2007 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""C++ keywords and helper utilities for determining keywords.""" + +__author__ = 'nnorwitz@google.com (Neal Norwitz)' + + +try: + # Python 3.x + import builtins +except ImportError: + # Python 2.x + import __builtin__ as builtins + + +if not hasattr(builtins, 'set'): + # Nominal support for Python 2.3. + from sets import Set as set + + +TYPES = set('bool char int long short double float void wchar_t unsigned signed'.split()) +TYPE_MODIFIERS = set('auto register const inline extern static virtual volatile mutable'.split()) +ACCESS = set('public protected private friend'.split()) + +CASTS = set('static_cast const_cast dynamic_cast reinterpret_cast'.split()) + +OTHERS = set('true false asm class namespace using explicit this operator sizeof'.split()) +OTHER_TYPES = set('new delete typedef struct union enum typeid typename template'.split()) + +CONTROL = set('case switch default if else return goto'.split()) +EXCEPTION = set('try catch throw'.split()) +LOOP = set('while do for break continue'.split()) + +ALL = TYPES | TYPE_MODIFIERS | ACCESS | CASTS | OTHERS | OTHER_TYPES | CONTROL | EXCEPTION | LOOP + + +def IsKeyword(token): + return token in ALL + +def IsBuiltinType(token): + if token in ('virtual', 'inline'): + # These only apply to methods, they can't be types by themselves. + return False + return token in TYPES or token in TYPE_MODIFIERS diff --git a/libwvdrmengine/test/gmock/scripts/generator/cpp/tokenize.py b/libwvdrmengine/test/gmock/scripts/generator/cpp/tokenize.py new file mode 100755 index 00000000..28c33452 --- /dev/null +++ b/libwvdrmengine/test/gmock/scripts/generator/cpp/tokenize.py @@ -0,0 +1,287 @@ +#!/usr/bin/env python +# +# Copyright 2007 Neal Norwitz +# Portions Copyright 2007 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tokenize C++ source code.""" + +__author__ = 'nnorwitz@google.com (Neal Norwitz)' + + +try: + # Python 3.x + import builtins +except ImportError: + # Python 2.x + import __builtin__ as builtins + + +import sys + +from cpp import utils + + +if not hasattr(builtins, 'set'): + # Nominal support for Python 2.3. + from sets import Set as set + + +# Add $ as a valid identifier char since so much code uses it. +_letters = 'abcdefghijklmnopqrstuvwxyz' +VALID_IDENTIFIER_CHARS = set(_letters + _letters.upper() + '_0123456789$') +HEX_DIGITS = set('0123456789abcdefABCDEF') +INT_OR_FLOAT_DIGITS = set('01234567890eE-+') + + +# C++0x string preffixes. +_STR_PREFIXES = set(('R', 'u8', 'u8R', 'u', 'uR', 'U', 'UR', 'L', 'LR')) + + +# Token types. +UNKNOWN = 'UNKNOWN' +SYNTAX = 'SYNTAX' +CONSTANT = 'CONSTANT' +NAME = 'NAME' +PREPROCESSOR = 'PREPROCESSOR' + +# Where the token originated from. This can be used for backtracking. +# It is always set to WHENCE_STREAM in this code. +WHENCE_STREAM, WHENCE_QUEUE = range(2) + + +class Token(object): + """Data container to represent a C++ token. + + Tokens can be identifiers, syntax char(s), constants, or + pre-processor directives. + + start contains the index of the first char of the token in the source + end contains the index of the last char of the token in the source + """ + + def __init__(self, token_type, name, start, end): + self.token_type = token_type + self.name = name + self.start = start + self.end = end + self.whence = WHENCE_STREAM + + def __str__(self): + if not utils.DEBUG: + return 'Token(%r)' % self.name + return 'Token(%r, %s, %s)' % (self.name, self.start, self.end) + + __repr__ = __str__ + + +def _GetString(source, start, i): + i = source.find('"', i+1) + while source[i-1] == '\\': + # Count the trailing backslashes. + backslash_count = 1 + j = i - 2 + while source[j] == '\\': + backslash_count += 1 + j -= 1 + # When trailing backslashes are even, they escape each other. + if (backslash_count % 2) == 0: + break + i = source.find('"', i+1) + return i + 1 + + +def _GetChar(source, start, i): + # NOTE(nnorwitz): may not be quite correct, should be good enough. + i = source.find("'", i+1) + while source[i-1] == '\\': + # Need to special case '\\'. + if (i - 2) > start and source[i-2] == '\\': + break + i = source.find("'", i+1) + # Try to handle unterminated single quotes (in a #if 0 block). + if i < 0: + i = start + return i + 1 + + +def GetTokens(source): + """Returns a sequence of Tokens. + + Args: + source: string of C++ source code. + + Yields: + Token that represents the next token in the source. + """ + # Cache various valid character sets for speed. + valid_identifier_chars = VALID_IDENTIFIER_CHARS + hex_digits = HEX_DIGITS + int_or_float_digits = INT_OR_FLOAT_DIGITS + int_or_float_digits2 = int_or_float_digits | set('.') + + # Only ignore errors while in a #if 0 block. + ignore_errors = False + count_ifs = 0 + + i = 0 + end = len(source) + while i < end: + # Skip whitespace. + while i < end and source[i].isspace(): + i += 1 + if i >= end: + return + + token_type = UNKNOWN + start = i + c = source[i] + if c.isalpha() or c == '_': # Find a string token. + token_type = NAME + while source[i] in valid_identifier_chars: + i += 1 + # String and character constants can look like a name if + # they are something like L"". + if (source[i] == "'" and (i - start) == 1 and + source[start:i] in 'uUL'): + # u, U, and L are valid C++0x character preffixes. + token_type = CONSTANT + i = _GetChar(source, start, i) + elif source[i] == "'" and source[start:i] in _STR_PREFIXES: + token_type = CONSTANT + i = _GetString(source, start, i) + elif c == '/' and source[i+1] == '/': # Find // comments. + i = source.find('\n', i) + if i == -1: # Handle EOF. + i = end + continue + elif c == '/' and source[i+1] == '*': # Find /* comments. */ + i = source.find('*/', i) + 2 + continue + elif c in ':+-<>&|*=': # : or :: (plus other chars). + token_type = SYNTAX + i += 1 + new_ch = source[i] + if new_ch == c: + i += 1 + elif c == '-' and new_ch == '>': + i += 1 + elif new_ch == '=': + i += 1 + elif c in '()[]{}~!?^%;/.,': # Handle single char tokens. + token_type = SYNTAX + i += 1 + if c == '.' and source[i].isdigit(): + token_type = CONSTANT + i += 1 + while source[i] in int_or_float_digits: + i += 1 + # Handle float suffixes. + for suffix in ('l', 'f'): + if suffix == source[i:i+1].lower(): + i += 1 + break + elif c.isdigit(): # Find integer. + token_type = CONSTANT + if c == '0' and source[i+1] in 'xX': + # Handle hex digits. + i += 2 + while source[i] in hex_digits: + i += 1 + else: + while source[i] in int_or_float_digits2: + i += 1 + # Handle integer (and float) suffixes. + for suffix in ('ull', 'll', 'ul', 'l', 'f', 'u'): + size = len(suffix) + if suffix == source[i:i+size].lower(): + i += size + break + elif c == '"': # Find string. + token_type = CONSTANT + i = _GetString(source, start, i) + elif c == "'": # Find char. + token_type = CONSTANT + i = _GetChar(source, start, i) + elif c == '#': # Find pre-processor command. + token_type = PREPROCESSOR + got_if = source[i:i+3] == '#if' and source[i+3:i+4].isspace() + if got_if: + count_ifs += 1 + elif source[i:i+6] == '#endif': + count_ifs -= 1 + if count_ifs == 0: + ignore_errors = False + + # TODO(nnorwitz): handle preprocessor statements (\ continuations). + while 1: + i1 = source.find('\n', i) + i2 = source.find('//', i) + i3 = source.find('/*', i) + i4 = source.find('"', i) + # NOTE(nnorwitz): doesn't handle comments in #define macros. + # Get the first important symbol (newline, comment, EOF/end). + i = min([x for x in (i1, i2, i3, i4, end) if x != -1]) + + # Handle #include "dir//foo.h" properly. + if source[i] == '"': + i = source.find('"', i+1) + 1 + assert i > 0 + continue + # Keep going if end of the line and the line ends with \. + if not (i == i1 and source[i-1] == '\\'): + if got_if: + condition = source[start+4:i].lstrip() + if (condition.startswith('0') or + condition.startswith('(0)')): + ignore_errors = True + break + i += 1 + elif c == '\\': # Handle \ in code. + # This is different from the pre-processor \ handling. + i += 1 + continue + elif ignore_errors: + # The tokenizer seems to be in pretty good shape. This + # raise is conditionally disabled so that bogus code + # in an #if 0 block can be handled. Since we will ignore + # it anyways, this is probably fine. So disable the + # exception and return the bogus char. + i += 1 + else: + sys.stderr.write('Got invalid token in %s @ %d token:%s: %r\n' % + ('?', i, c, source[i-10:i+10])) + raise RuntimeError('unexpected token') + + if i <= 0: + print('Invalid index, exiting now.') + return + yield Token(token_type, source[start:i], start, i) + + +if __name__ == '__main__': + def main(argv): + """Driver mostly for testing purposes.""" + for filename in argv[1:]: + source = utils.ReadFile(filename) + if source is None: + continue + + for token in GetTokens(source): + print('%-12s: %s' % (token.token_type, token.name)) + # print('\r%6.2f%%' % (100.0 * index / token.end),) + sys.stdout.write('\n') + + + main(sys.argv) diff --git a/libwvdrmengine/test/gmock/scripts/generator/cpp/utils.py b/libwvdrmengine/test/gmock/scripts/generator/cpp/utils.py new file mode 100755 index 00000000..eab36eec --- /dev/null +++ b/libwvdrmengine/test/gmock/scripts/generator/cpp/utils.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python +# +# Copyright 2007 Neal Norwitz +# Portions Copyright 2007 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Generic utilities for C++ parsing.""" + +__author__ = 'nnorwitz@google.com (Neal Norwitz)' + + +import sys + + +# Set to True to see the start/end token indices. +DEBUG = True + + +def ReadFile(filename, print_error=True): + """Returns the contents of a file.""" + try: + fp = open(filename) + try: + return fp.read() + finally: + fp.close() + except IOError: + if print_error: + print('Error reading %s: %s' % (filename, sys.exc_info()[1])) + return None diff --git a/libwvdrmengine/test/gmock/scripts/generator/gmock_gen.py b/libwvdrmengine/test/gmock/scripts/generator/gmock_gen.py new file mode 100755 index 00000000..8cc0d135 --- /dev/null +++ b/libwvdrmengine/test/gmock/scripts/generator/gmock_gen.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python +# +# Copyright 2008 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Driver for starting up Google Mock class generator.""" + +__author__ = 'nnorwitz@google.com (Neal Norwitz)' + +import os +import sys + +if __name__ == '__main__': + # Add the directory of this script to the path so we can import gmock_class. + sys.path.append(os.path.dirname(__file__)) + + from cpp import gmock_class + # Fix the docstring in case they require the usage. + gmock_class.__doc__ = gmock_class.__doc__.replace('gmock_class.py', __file__) + gmock_class.main() diff --git a/libwvdrmengine/test/gmock/scripts/gmock-config.in b/libwvdrmengine/test/gmock/scripts/gmock-config.in new file mode 100755 index 00000000..2baefe94 --- /dev/null +++ b/libwvdrmengine/test/gmock/scripts/gmock-config.in @@ -0,0 +1,303 @@ +#!/bin/sh + +# These variables are automatically filled in by the configure script. +name="@PACKAGE_TARNAME@" +version="@PACKAGE_VERSION@" + +show_usage() +{ + echo "Usage: gmock-config [OPTIONS...]" +} + +show_help() +{ + show_usage + cat <<\EOF + +The `gmock-config' script provides access to the necessary compile and linking +flags to connect with Google C++ Mocking Framework, both in a build prior to +installation, and on the system proper after installation. The installation +overrides may be issued in combination with any other queries, but will only +affect installation queries if called on a built but not installed gmock. The +installation queries may not be issued with any other types of queries, and +only one installation query may be made at a time. The version queries and +compiler flag queries may be combined as desired but not mixed. Different +version queries are always combined with logical "and" semantics, and only the +last of any particular query is used while all previous ones ignored. All +versions must be specified as a sequence of numbers separated by periods. +Compiler flag queries output the union of the sets of flags when combined. + + Examples: + gmock-config --min-version=1.0 || echo "Insufficient Google Mock version." + + g++ $(gmock-config --cppflags --cxxflags) -o foo.o -c foo.cpp + g++ $(gmock-config --ldflags --libs) -o foo foo.o + + # When using a built but not installed Google Mock: + g++ $(../../my_gmock_build/scripts/gmock-config ...) ... + + # When using an installed Google Mock, but with installation overrides: + export GMOCK_PREFIX="/opt" + g++ $(gmock-config --libdir="/opt/lib64" ...) ... + + Help: + --usage brief usage information + --help display this help message + + Installation Overrides: + --prefix= overrides the installation prefix + --exec-prefix= overrides the executable installation prefix + --libdir= overrides the library installation prefix + --includedir= overrides the header file installation prefix + + Installation Queries: + --prefix installation prefix + --exec-prefix executable installation prefix + --libdir library installation directory + --includedir header file installation directory + --version the version of the Google Mock installation + + Version Queries: + --min-version=VERSION return 0 if the version is at least VERSION + --exact-version=VERSION return 0 if the version is exactly VERSION + --max-version=VERSION return 0 if the version is at most VERSION + + Compilation Flag Queries: + --cppflags compile flags specific to the C-like preprocessors + --cxxflags compile flags appropriate for C++ programs + --ldflags linker flags + --libs libraries for linking + +EOF +} + +# This function bounds our version with a min and a max. It uses some clever +# POSIX-compliant variable expansion to portably do all the work in the shell +# and avoid any dependency on a particular "sed" or "awk" implementation. +# Notable is that it will only ever compare the first 3 components of versions. +# Further components will be cleanly stripped off. All versions must be +# unadorned, so "v1.0" will *not* work. The minimum version must be in $1, and +# the max in $2. TODO(chandlerc@google.com): If this ever breaks, we should +# investigate expanding this via autom4te from AS_VERSION_COMPARE rather than +# continuing to maintain our own shell version. +check_versions() +{ + major_version=${version%%.*} + minor_version="0" + point_version="0" + if test "${version#*.}" != "${version}"; then + minor_version=${version#*.} + minor_version=${minor_version%%.*} + fi + if test "${version#*.*.}" != "${version}"; then + point_version=${version#*.*.} + point_version=${point_version%%.*} + fi + + min_version="$1" + min_major_version=${min_version%%.*} + min_minor_version="0" + min_point_version="0" + if test "${min_version#*.}" != "${min_version}"; then + min_minor_version=${min_version#*.} + min_minor_version=${min_minor_version%%.*} + fi + if test "${min_version#*.*.}" != "${min_version}"; then + min_point_version=${min_version#*.*.} + min_point_version=${min_point_version%%.*} + fi + + max_version="$2" + max_major_version=${max_version%%.*} + max_minor_version="0" + max_point_version="0" + if test "${max_version#*.}" != "${max_version}"; then + max_minor_version=${max_version#*.} + max_minor_version=${max_minor_version%%.*} + fi + if test "${max_version#*.*.}" != "${max_version}"; then + max_point_version=${max_version#*.*.} + max_point_version=${max_point_version%%.*} + fi + + test $(($major_version)) -lt $(($min_major_version)) && exit 1 + if test $(($major_version)) -eq $(($min_major_version)); then + test $(($minor_version)) -lt $(($min_minor_version)) && exit 1 + if test $(($minor_version)) -eq $(($min_minor_version)); then + test $(($point_version)) -lt $(($min_point_version)) && exit 1 + fi + fi + + test $(($major_version)) -gt $(($max_major_version)) && exit 1 + if test $(($major_version)) -eq $(($max_major_version)); then + test $(($minor_version)) -gt $(($max_minor_version)) && exit 1 + if test $(($minor_version)) -eq $(($max_minor_version)); then + test $(($point_version)) -gt $(($max_point_version)) && exit 1 + fi + fi + + exit 0 +} + +# Show the usage line when no arguments are specified. +if test $# -eq 0; then + show_usage + exit 1 +fi + +while test $# -gt 0; do + case $1 in + --usage) show_usage; exit 0;; + --help) show_help; exit 0;; + + # Installation overrides + --prefix=*) GMOCK_PREFIX=${1#--prefix=};; + --exec-prefix=*) GMOCK_EXEC_PREFIX=${1#--exec-prefix=};; + --libdir=*) GMOCK_LIBDIR=${1#--libdir=};; + --includedir=*) GMOCK_INCLUDEDIR=${1#--includedir=};; + + # Installation queries + --prefix|--exec-prefix|--libdir|--includedir|--version) + if test -n "${do_query}"; then + show_usage + exit 1 + fi + do_query=${1#--} + ;; + + # Version checking + --min-version=*) + do_check_versions=yes + min_version=${1#--min-version=} + ;; + --max-version=*) + do_check_versions=yes + max_version=${1#--max-version=} + ;; + --exact-version=*) + do_check_versions=yes + exact_version=${1#--exact-version=} + ;; + + # Compiler flag output + --cppflags) echo_cppflags=yes;; + --cxxflags) echo_cxxflags=yes;; + --ldflags) echo_ldflags=yes;; + --libs) echo_libs=yes;; + + # Everything else is an error + *) show_usage; exit 1;; + esac + shift +done + +# These have defaults filled in by the configure script but can also be +# overridden by environment variables or command line parameters. +prefix="${GMOCK_PREFIX:-@prefix@}" +exec_prefix="${GMOCK_EXEC_PREFIX:-@exec_prefix@}" +libdir="${GMOCK_LIBDIR:-@libdir@}" +includedir="${GMOCK_INCLUDEDIR:-@includedir@}" + +# We try and detect if our binary is not located at its installed location. If +# it's not, we provide variables pointing to the source and build tree rather +# than to the install tree. We also locate Google Test using the configured +# gtest-config script rather than searching the PATH and our bindir for one. +# This allows building against a just-built gmock rather than an installed +# gmock. +bindir="@bindir@" +this_relative_bindir=`dirname $0` +this_bindir=`cd ${this_relative_bindir}; pwd -P` +if test "${this_bindir}" = "${this_bindir%${bindir}}"; then + # The path to the script doesn't end in the bindir sequence from Autoconf, + # assume that we are in a build tree. + build_dir=`dirname ${this_bindir}` + src_dir=`cd ${this_bindir}/@top_srcdir@; pwd -P` + + # TODO(chandlerc@google.com): This is a dangerous dependency on libtool, we + # should work to remove it, and/or remove libtool altogether, replacing it + # with direct references to the library and a link path. + gmock_libs="${build_dir}/lib/libgmock.la" + gmock_ldflags="" + + # We provide hooks to include from either the source or build dir, where the + # build dir is always preferred. This will potentially allow us to write + # build rules for generated headers and have them automatically be preferred + # over provided versions. + gmock_cppflags="-I${build_dir}/include -I${src_dir}/include" + gmock_cxxflags="" + + # Directly invoke the gtest-config script used during the build process. + gtest_config="@GTEST_CONFIG@" +else + # We're using an installed gmock, although it may be staged under some + # prefix. Assume (as our own libraries do) that we can resolve the prefix, + # and are present in the dynamic link paths. + gmock_ldflags="-L${libdir}" + gmock_libs="-l${name}" + gmock_cppflags="-I${includedir}" + gmock_cxxflags="" + + # We also prefer any gtest-config script installed in our prefix. Lacking + # one, we look in the PATH for one. + gtest_config="${bindir}/gtest-config" + if test ! -x "${gtest_config}"; then + gtest_config=`which gtest-config` + fi +fi + +# Ensure that we have located a Google Test to link against. +if ! test -x "${gtest_config}"; then + echo "Unable to locate Google Test, check your Google Mock configuration" \ + "and installation" >&2 + exit 1 +elif ! "${gtest_config}" "--exact-version=@GTEST_VERSION@"; then + echo "The Google Test found is not the same version as Google Mock was " \ + "built against" >&2 + exit 1 +fi + +# Add the necessary Google Test bits into the various flag variables +gmock_cppflags="${gmock_cppflags} `${gtest_config} --cppflags`" +gmock_cxxflags="${gmock_cxxflags} `${gtest_config} --cxxflags`" +gmock_ldflags="${gmock_ldflags} `${gtest_config} --ldflags`" +gmock_libs="${gmock_libs} `${gtest_config} --libs`" + +# Do an installation query if requested. +if test -n "$do_query"; then + case $do_query in + prefix) echo $prefix; exit 0;; + exec-prefix) echo $exec_prefix; exit 0;; + libdir) echo $libdir; exit 0;; + includedir) echo $includedir; exit 0;; + version) echo $version; exit 0;; + *) show_usage; exit 1;; + esac +fi + +# Do a version check if requested. +if test "$do_check_versions" = "yes"; then + # Make sure we didn't receive a bad combination of parameters. + test "$echo_cppflags" = "yes" && show_usage && exit 1 + test "$echo_cxxflags" = "yes" && show_usage && exit 1 + test "$echo_ldflags" = "yes" && show_usage && exit 1 + test "$echo_libs" = "yes" && show_usage && exit 1 + + if test "$exact_version" != ""; then + check_versions $exact_version $exact_version + # unreachable + else + check_versions ${min_version:-0.0.0} ${max_version:-9999.9999.9999} + # unreachable + fi +fi + +# Do the output in the correct order so that these can be used in-line of +# a compiler invocation. +output="" +test "$echo_cppflags" = "yes" && output="$output $gmock_cppflags" +test "$echo_cxxflags" = "yes" && output="$output $gmock_cxxflags" +test "$echo_ldflags" = "yes" && output="$output $gmock_ldflags" +test "$echo_libs" = "yes" && output="$output $gmock_libs" +echo $output + +exit 0 diff --git a/libwvdrmengine/test/gmock/scripts/gmock_doctor.py b/libwvdrmengine/test/gmock/scripts/gmock_doctor.py new file mode 100755 index 00000000..61bbea62 --- /dev/null +++ b/libwvdrmengine/test/gmock/scripts/gmock_doctor.py @@ -0,0 +1,621 @@ +#!/usr/bin/env python +# +# Copyright 2008, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Converts compiler's errors in code using Google Mock to plain English.""" + +__author__ = 'wan@google.com (Zhanyong Wan)' + +import re +import sys + +_VERSION = '1.0.3' + +_EMAIL = 'googlemock@googlegroups.com' + +_COMMON_GMOCK_SYMBOLS = [ + # Matchers + '_', + 'A', + 'AddressSatisfies', + 'AllOf', + 'An', + 'AnyOf', + 'ContainerEq', + 'Contains', + 'ContainsRegex', + 'DoubleEq', + 'ElementsAre', + 'ElementsAreArray', + 'EndsWith', + 'Eq', + 'Field', + 'FloatEq', + 'Ge', + 'Gt', + 'HasSubstr', + 'IsInitializedProto', + 'Le', + 'Lt', + 'MatcherCast', + 'Matches', + 'MatchesRegex', + 'NanSensitiveDoubleEq', + 'NanSensitiveFloatEq', + 'Ne', + 'Not', + 'NotNull', + 'Pointee', + 'Property', + 'Ref', + 'ResultOf', + 'SafeMatcherCast', + 'StartsWith', + 'StrCaseEq', + 'StrCaseNe', + 'StrEq', + 'StrNe', + 'Truly', + 'TypedEq', + 'Value', + + # Actions + 'Assign', + 'ByRef', + 'DeleteArg', + 'DoAll', + 'DoDefault', + 'IgnoreResult', + 'Invoke', + 'InvokeArgument', + 'InvokeWithoutArgs', + 'Return', + 'ReturnNew', + 'ReturnNull', + 'ReturnRef', + 'SaveArg', + 'SetArgReferee', + 'SetArgPointee', + 'SetArgumentPointee', + 'SetArrayArgument', + 'SetErrnoAndReturn', + 'Throw', + 'WithArg', + 'WithArgs', + 'WithoutArgs', + + # Cardinalities + 'AnyNumber', + 'AtLeast', + 'AtMost', + 'Between', + 'Exactly', + + # Sequences + 'InSequence', + 'Sequence', + + # Misc + 'DefaultValue', + 'Mock', + ] + +# Regex for matching source file path and line number in the compiler's errors. +_GCC_FILE_LINE_RE = r'(?P.*):(?P\d+):(\d+:)?\s+' +_CLANG_FILE_LINE_RE = r'(?P.*):(?P\d+):(?P\d+):\s+' +_CLANG_NON_GMOCK_FILE_LINE_RE = ( + r'(?P.*[/\\^](?!gmock-)[^/\\]+):(?P\d+):(?P\d+):\s+') + + +def _FindAllMatches(regex, s): + """Generates all matches of regex in string s.""" + + r = re.compile(regex) + return r.finditer(s) + + +def _GenericDiagnoser(short_name, long_name, diagnoses, msg): + """Diagnoses the given disease by pattern matching. + + Can provide different diagnoses for different patterns. + + Args: + short_name: Short name of the disease. + long_name: Long name of the disease. + diagnoses: A list of pairs (regex, pattern for formatting the diagnosis + for matching regex). + msg: Compiler's error messages. + Yields: + Tuples of the form + (short name of disease, long name of disease, diagnosis). + """ + for regex, diagnosis in diagnoses: + if re.search(regex, msg): + diagnosis = '%(file)s:%(line)s:' + diagnosis + for m in _FindAllMatches(regex, msg): + yield (short_name, long_name, diagnosis % m.groupdict()) + + +def _NeedToReturnReferenceDiagnoser(msg): + """Diagnoses the NRR disease, given the error messages by the compiler.""" + + gcc_regex = (r'In member function \'testing::internal::ReturnAction.*\n' + + _GCC_FILE_LINE_RE + r'instantiated from here\n' + r'.*gmock-actions\.h.*error: creating array with negative size') + clang_regex = (r'error:.*array.*negative.*\r?\n' + r'(.*\n)*?' + + _CLANG_NON_GMOCK_FILE_LINE_RE + + r'note: in instantiation of function template specialization ' + r'\'testing::internal::ReturnAction<(?P.*)>' + r'::operator Action<.*>\' requested here') + diagnosis = """ +You are using a Return() action in a function that returns a reference to +%(type)s. Please use ReturnRef() instead.""" + return _GenericDiagnoser('NRR', 'Need to Return Reference', + [(clang_regex, diagnosis), + (gcc_regex, diagnosis % {'type': 'a type'})], + msg) + + +def _NeedToReturnSomethingDiagnoser(msg): + """Diagnoses the NRS disease, given the error messages by the compiler.""" + + gcc_regex = (_GCC_FILE_LINE_RE + r'(instantiated from here\n.' + r'*gmock.*actions\.h.*error: void value not ignored)' + r'|(error: control reaches end of non-void function)') + clang_regex1 = (_CLANG_FILE_LINE_RE + + r'error: cannot initialize return object ' + r'of type \'Result\' \(aka \'(?P.*)\'\) ' + r'with an rvalue of type \'void\'') + clang_regex2 = (_CLANG_FILE_LINE_RE + + r'error: cannot initialize return object ' + r'of type \'(?P.*)\' ' + r'with an rvalue of type \'void\'') + diagnosis = """ +You are using an action that returns void, but it needs to return +%(return_type)s. Please tell it *what* to return. Perhaps you can use +the pattern DoAll(some_action, Return(some_value))?""" + return _GenericDiagnoser( + 'NRS', + 'Need to Return Something', + [(gcc_regex, diagnosis % {'return_type': '*something*'}), + (clang_regex1, diagnosis), + (clang_regex2, diagnosis)], + msg) + + +def _NeedToReturnNothingDiagnoser(msg): + """Diagnoses the NRN disease, given the error messages by the compiler.""" + + gcc_regex = (_GCC_FILE_LINE_RE + r'instantiated from here\n' + r'.*gmock-actions\.h.*error: instantiation of ' + r'\'testing::internal::ReturnAction::Impl::value_\' ' + r'as type \'void\'') + clang_regex1 = (r'error: field has incomplete type ' + r'\'Result\' \(aka \'void\'\)(\r)?\n' + r'(.*\n)*?' + + _CLANG_NON_GMOCK_FILE_LINE_RE + r'note: in instantiation ' + r'of function template specialization ' + r'\'testing::internal::ReturnAction<(?P.*)>' + r'::operator Action\' requested here') + clang_regex2 = (r'error: field has incomplete type ' + r'\'Result\' \(aka \'void\'\)(\r)?\n' + r'(.*\n)*?' + + _CLANG_NON_GMOCK_FILE_LINE_RE + r'note: in instantiation ' + r'of function template specialization ' + r'\'testing::internal::DoBothAction<.*>' + r'::operator Action<(?P.*) \(.*\)>\' ' + r'requested here') + diagnosis = """ +You are using an action that returns %(return_type)s, but it needs to return +void. Please use a void-returning action instead. + +All actions but the last in DoAll(...) must return void. Perhaps you need +to re-arrange the order of actions in a DoAll(), if you are using one?""" + return _GenericDiagnoser( + 'NRN', + 'Need to Return Nothing', + [(gcc_regex, diagnosis % {'return_type': '*something*'}), + (clang_regex1, diagnosis), + (clang_regex2, diagnosis)], + msg) + + +def _IncompleteByReferenceArgumentDiagnoser(msg): + """Diagnoses the IBRA disease, given the error messages by the compiler.""" + + gcc_regex = (_GCC_FILE_LINE_RE + r'instantiated from here\n' + r'.*gtest-printers\.h.*error: invalid application of ' + r'\'sizeof\' to incomplete type \'(?P.*)\'') + + clang_regex = (r'.*gtest-printers\.h.*error: invalid application of ' + r'\'sizeof\' to an incomplete type ' + r'\'(?P.*)( const)?\'\r?\n' + r'(.*\n)*?' + + _CLANG_NON_GMOCK_FILE_LINE_RE + + r'note: in instantiation of member function ' + r'\'testing::internal2::TypeWithoutFormatter<.*>::' + r'PrintValue\' requested here') + diagnosis = """ +In order to mock this function, Google Mock needs to see the definition +of type "%(type)s" - declaration alone is not enough. Either #include +the header that defines it, or change the argument to be passed +by pointer.""" + + return _GenericDiagnoser('IBRA', 'Incomplete By-Reference Argument Type', + [(gcc_regex, diagnosis), + (clang_regex, diagnosis)], + msg) + + +def _OverloadedFunctionMatcherDiagnoser(msg): + """Diagnoses the OFM disease, given the error messages by the compiler.""" + + gcc_regex = (_GCC_FILE_LINE_RE + r'error: no matching function for ' + r'call to \'Truly\(\)') + clang_regex = (_CLANG_FILE_LINE_RE + r'error: no matching function for ' + r'call to \'Truly') + diagnosis = """ +The argument you gave to Truly() is an overloaded function. Please tell +your compiler which overloaded version you want to use. + +For example, if you want to use the version whose signature is + bool Foo(int n); +you should write + Truly(static_cast(Foo))""" + return _GenericDiagnoser('OFM', 'Overloaded Function Matcher', + [(gcc_regex, diagnosis), + (clang_regex, diagnosis)], + msg) + + +def _OverloadedFunctionActionDiagnoser(msg): + """Diagnoses the OFA disease, given the error messages by the compiler.""" + + gcc_regex = (_GCC_FILE_LINE_RE + r'error: no matching function for call to ' + r'\'Invoke\(') + clang_regex = (_CLANG_FILE_LINE_RE + r'error: no matching ' + r'function for call to \'Invoke\'\r?\n' + r'(.*\n)*?' + r'.*\bgmock-\w+-actions\.h:\d+:\d+:\s+' + r'note: candidate template ignored:\s+' + r'couldn\'t infer template argument \'FunctionImpl\'') + diagnosis = """ +Function you are passing to Invoke is overloaded. Please tell your compiler +which overloaded version you want to use. + +For example, if you want to use the version whose signature is + bool MyFunction(int n, double x); +you should write something like + Invoke(static_cast(MyFunction))""" + return _GenericDiagnoser('OFA', 'Overloaded Function Action', + [(gcc_regex, diagnosis), + (clang_regex, diagnosis)], + msg) + + +def _OverloadedMethodActionDiagnoser(msg): + """Diagnoses the OMA disease, given the error messages by the compiler.""" + + gcc_regex = (_GCC_FILE_LINE_RE + r'error: no matching function for ' + r'call to \'Invoke\(.+, \)') + clang_regex = (_CLANG_FILE_LINE_RE + r'error: no matching function ' + r'for call to \'Invoke\'\r?\n' + r'(.*\n)*?' + r'.*\bgmock-\w+-actions\.h:\d+:\d+: ' + r'note: candidate function template not viable: ' + r'requires 1 argument, but 2 were provided') + diagnosis = """ +The second argument you gave to Invoke() is an overloaded method. Please +tell your compiler which overloaded version you want to use. + +For example, if you want to use the version whose signature is + class Foo { + ... + bool Bar(int n, double x); + }; +you should write something like + Invoke(foo, static_cast(&Foo::Bar))""" + return _GenericDiagnoser('OMA', 'Overloaded Method Action', + [(gcc_regex, diagnosis), + (clang_regex, diagnosis)], + msg) + + +def _MockObjectPointerDiagnoser(msg): + """Diagnoses the MOP disease, given the error messages by the compiler.""" + + gcc_regex = (_GCC_FILE_LINE_RE + r'error: request for member ' + r'\'gmock_(?P.+)\' in \'(?P.+)\', ' + r'which is of non-class type \'(.*::)*(?P.+)\*\'') + clang_regex = (_CLANG_FILE_LINE_RE + r'error: member reference type ' + r'\'(?P.*?) *\' is a pointer; ' + r'maybe you meant to use \'->\'\?') + diagnosis = """ +The first argument to ON_CALL() and EXPECT_CALL() must be a mock *object*, +not a *pointer* to it. Please write '*(%(mock_object)s)' instead of +'%(mock_object)s' as your first argument. + +For example, given the mock class: + + class %(class_name)s : public ... { + ... + MOCK_METHOD0(%(method)s, ...); + }; + +and the following mock instance: + + %(class_name)s* mock_ptr = ... + +you should use the EXPECT_CALL like this: + + EXPECT_CALL(*mock_ptr, %(method)s(...));""" + + return _GenericDiagnoser( + 'MOP', + 'Mock Object Pointer', + [(gcc_regex, diagnosis), + (clang_regex, diagnosis % {'mock_object': 'mock_object', + 'method': 'method', + 'class_name': '%(class_name)s'})], + msg) + + +def _NeedToUseSymbolDiagnoser(msg): + """Diagnoses the NUS disease, given the error messages by the compiler.""" + + gcc_regex = (_GCC_FILE_LINE_RE + r'error: \'(?P.+)\' ' + r'(was not declared in this scope|has not been declared)') + clang_regex = (_CLANG_FILE_LINE_RE + + r'error: (use of undeclared identifier|unknown type name|' + r'no template named) \'(?P[^\']+)\'') + diagnosis = """ +'%(symbol)s' is defined by Google Mock in the testing namespace. +Did you forget to write + using testing::%(symbol)s; +?""" + for m in (list(_FindAllMatches(gcc_regex, msg)) + + list(_FindAllMatches(clang_regex, msg))): + symbol = m.groupdict()['symbol'] + if symbol in _COMMON_GMOCK_SYMBOLS: + yield ('NUS', 'Need to Use Symbol', diagnosis % m.groupdict()) + + +def _NeedToUseReturnNullDiagnoser(msg): + """Diagnoses the NRNULL disease, given the error messages by the compiler.""" + + gcc_regex = ('instantiated from \'testing::internal::ReturnAction' + '::operator testing::Action\(\) const.*\n' + + _GCC_FILE_LINE_RE + r'instantiated from here\n' + r'.*error: no matching function for call to \'ImplicitCast_\(' + r'long int&\)') + clang_regex = (r'\bgmock-actions.h:.* error: no matching function for ' + r'call to \'ImplicitCast_\'\r?\n' + r'(.*\n)*?' + + _CLANG_NON_GMOCK_FILE_LINE_RE + r'note: in instantiation ' + r'of function template specialization ' + r'\'testing::internal::ReturnAction<(int|long)>::operator ' + r'Action<(?P.*)\(\)>\' requested here') + diagnosis = """ +You are probably calling Return(NULL) and the compiler isn't sure how to turn +NULL into %(type)s. Use ReturnNull() instead. +Note: the line number may be off; please fix all instances of Return(NULL).""" + return _GenericDiagnoser( + 'NRNULL', 'Need to use ReturnNull', + [(clang_regex, diagnosis), + (gcc_regex, diagnosis % {'type': 'the right type'})], + msg) + + +def _TypeInTemplatedBaseDiagnoser(msg): + """Diagnoses the TTB disease, given the error messages by the compiler.""" + + # This version works when the type is used as the mock function's return + # type. + gcc_4_3_1_regex_type_in_retval = ( + r'In member function \'int .*\n' + _GCC_FILE_LINE_RE + + r'error: a function call cannot appear in a constant-expression') + gcc_4_4_0_regex_type_in_retval = ( + r'error: a function call cannot appear in a constant-expression' + + _GCC_FILE_LINE_RE + r'error: template argument 1 is invalid\n') + # This version works when the type is used as the mock function's sole + # parameter type. + gcc_regex_type_of_sole_param = ( + _GCC_FILE_LINE_RE + + r'error: \'(?P.+)\' was not declared in this scope\n' + r'.*error: template argument 1 is invalid\n') + # This version works when the type is used as a parameter of a mock + # function that has multiple parameters. + gcc_regex_type_of_a_param = ( + r'error: expected `;\' before \'::\' token\n' + + _GCC_FILE_LINE_RE + + r'error: \'(?P.+)\' was not declared in this scope\n' + r'.*error: template argument 1 is invalid\n' + r'.*error: \'.+\' was not declared in this scope') + clang_regex_type_of_retval_or_sole_param = ( + _CLANG_FILE_LINE_RE + + r'error: use of undeclared identifier \'(?P.*)\'\n' + r'(.*\n)*?' + r'(?P=file):(?P=line):\d+: error: ' + r'non-friend class member \'Result\' cannot have a qualified name' + ) + clang_regex_type_of_a_param = ( + _CLANG_FILE_LINE_RE + + r'error: C\+\+ requires a type specifier for all declarations\n' + r'(.*\n)*?' + r'(?P=file):(?P=line):(?P=column): error: ' + r'C\+\+ requires a type specifier for all declarations' + ) + + diagnosis = """ +In a mock class template, types or typedefs defined in the base class +template are *not* automatically visible. This is how C++ works. Before +you can use a type or typedef named %(type)s defined in base class Base, you +need to make it visible. One way to do it is: + + typedef typename Base::%(type)s %(type)s;""" + + return _GenericDiagnoser( + 'TTB', 'Type in Template Base', + [(gcc_4_3_1_regex_type_in_retval, diagnosis % {'type': 'Foo'}), + (gcc_4_4_0_regex_type_in_retval, diagnosis % {'type': 'Foo'}), + (gcc_regex_type_of_sole_param, diagnosis), + (gcc_regex_type_of_a_param, diagnosis), + (clang_regex_type_of_retval_or_sole_param, diagnosis), + (clang_regex_type_of_a_param, diagnosis % {'type': 'Foo'})], + msg) + + +def _WrongMockMethodMacroDiagnoser(msg): + """Diagnoses the WMM disease, given the error messages by the compiler.""" + + gcc_regex = (_GCC_FILE_LINE_RE + + r'.*this_method_does_not_take_(?P\d+)_argument.*\n' + r'.*\n' + r'.*candidates are.*FunctionMocker<[^>]+A(?P\d+)\)>') + clang_regex = (_CLANG_NON_GMOCK_FILE_LINE_RE + + r'error:.*array.*negative.*r?\n' + r'(.*\n)*?' + r'(?P=file):(?P=line):(?P=column): error: too few arguments ' + r'to function call, expected (?P\d+), ' + r'have (?P\d+)') + diagnosis = """ +You are using MOCK_METHOD%(wrong_args)s to define a mock method that has +%(args)s arguments. Use MOCK_METHOD%(args)s (or MOCK_CONST_METHOD%(args)s, +MOCK_METHOD%(args)s_T, MOCK_CONST_METHOD%(args)s_T as appropriate) instead.""" + return _GenericDiagnoser('WMM', 'Wrong MOCK_METHODn Macro', + [(gcc_regex, diagnosis), + (clang_regex, diagnosis)], + msg) + + +def _WrongParenPositionDiagnoser(msg): + """Diagnoses the WPP disease, given the error messages by the compiler.""" + + gcc_regex = (_GCC_FILE_LINE_RE + + r'error:.*testing::internal::MockSpec<.* has no member named \'' + r'(?P\w+)\'') + clang_regex = (_CLANG_NON_GMOCK_FILE_LINE_RE + + r'error: no member named \'(?P\w+)\' in ' + r'\'testing::internal::MockSpec<.*>\'') + diagnosis = """ +The closing parenthesis of ON_CALL or EXPECT_CALL should be *before* +".%(method)s". For example, you should write: + EXPECT_CALL(my_mock, Foo(_)).%(method)s(...); +instead of: + EXPECT_CALL(my_mock, Foo(_).%(method)s(...));""" + return _GenericDiagnoser('WPP', 'Wrong Parenthesis Position', + [(gcc_regex, diagnosis), + (clang_regex, diagnosis)], + msg) + + +_DIAGNOSERS = [ + _IncompleteByReferenceArgumentDiagnoser, + _MockObjectPointerDiagnoser, + _NeedToReturnNothingDiagnoser, + _NeedToReturnReferenceDiagnoser, + _NeedToReturnSomethingDiagnoser, + _NeedToUseReturnNullDiagnoser, + _NeedToUseSymbolDiagnoser, + _OverloadedFunctionActionDiagnoser, + _OverloadedFunctionMatcherDiagnoser, + _OverloadedMethodActionDiagnoser, + _TypeInTemplatedBaseDiagnoser, + _WrongMockMethodMacroDiagnoser, + _WrongParenPositionDiagnoser, + ] + + +def Diagnose(msg): + """Generates all possible diagnoses given the compiler error message.""" + + msg = re.sub(r'\x1b\[[^m]*m', '', msg) # Strips all color formatting. + # Assuming the string is using the UTF-8 encoding, replaces the left and + # the right single quote characters with apostrophes. + msg = re.sub(r'(\xe2\x80\x98|\xe2\x80\x99)', "'", msg) + + diagnoses = [] + for diagnoser in _DIAGNOSERS: + for diag in diagnoser(msg): + diagnosis = '[%s - %s]\n%s' % diag + if not diagnosis in diagnoses: + diagnoses.append(diagnosis) + return diagnoses + + +def main(): + print ('Google Mock Doctor v%s - ' + 'diagnoses problems in code using Google Mock.' % _VERSION) + + if sys.stdin.isatty(): + print ('Please copy and paste the compiler errors here. Press c-D when ' + 'you are done:') + else: + print 'Waiting for compiler errors on stdin . . .' + + msg = sys.stdin.read().strip() + diagnoses = Diagnose(msg) + count = len(diagnoses) + if not count: + print (""" +Your compiler complained: +8<------------------------------------------------------------ +%s +------------------------------------------------------------>8 + +Uh-oh, I'm not smart enough to figure out what the problem is. :-( +However... +If you send your source code and the compiler's error messages to +%s, you can be helped and I can get smarter -- +win-win for us!""" % (msg, _EMAIL)) + else: + print '------------------------------------------------------------' + print 'Your code appears to have the following', + if count > 1: + print '%s diseases:' % (count,) + else: + print 'disease:' + i = 0 + for d in diagnoses: + i += 1 + if count > 1: + print '\n#%s:' % (i,) + print d + print (""" +How did I do? If you think I'm wrong or unhelpful, please send your +source code and the compiler's error messages to %s. +Then you can be helped and I can get smarter -- I promise I won't be upset!""" % + _EMAIL) + + +if __name__ == '__main__': + main() diff --git a/libwvdrmengine/test/gmock/src/Android.mk b/libwvdrmengine/test/gmock/src/Android.mk new file mode 100644 index 00000000..2091c16d --- /dev/null +++ b/libwvdrmengine/test/gmock/src/Android.mk @@ -0,0 +1,103 @@ +# Copyright 2013 Google Inc. All Rights Reserved. + +# gMock builds 2 libraries: libgmock and libgmock_main. libgmock +# contains most of the code and libgmock_main just +# provide a common main to run the test. (i.e. If you link against +# libgmock_main you shouldn't provide a main() entry point.) +# +# We build these 2 libraries for the target device and for the host if +# it is running linux and using ASTL. + +LOCAL_PATH := $(call my-dir) + +libgmock_target_includes := \ + $(LOCAL_PATH)/.. \ + $(LOCAL_PATH)/../include \ + external/gtest/include \ + +libgmock_host_includes := \ + $(LOCAL_PATH)/.. \ + $(LOCAL_PATH)/../include \ + external/gtest/include \ + +####################################################################### +# gmock lib host + +include $(CLEAR_VARS) + +LOCAL_CPP_EXTENSION := .cc + +LOCAL_SRC_FILES := gmock-all.cc + +LOCAL_C_INCLUDES := $(libgmock_host_includes) + +LOCAL_CFLAGS += -O0 + +LOCAL_MODULE := libgmock_host + +include $(BUILD_HOST_STATIC_LIBRARY) + +####################################################################### +# gmock_main lib host + +include $(CLEAR_VARS) + +LOCAL_CPP_EXTENSION := .cc + +LOCAL_SRC_FILES := gmock_main.cc + +LOCAL_C_INCLUDES := $(libgmock_host_includes) + +LOCAL_CFLAGS += -O0 + +LOCAL_MODULE := libgmock_main_host + +include $(BUILD_HOST_STATIC_LIBRARY) + +####################################################################### +# gmock lib target + +include $(CLEAR_VARS) + +ifeq ($(TARGET_ARCH), arm) + LOCAL_SDK_VERSION := 8 +else +# NDK support of other archs (ie. x86 and mips) are only available after android-9 + LOCAL_SDK_VERSION := 9 +endif + +LOCAL_NDK_STL_VARIANT := stlport_static + +LOCAL_CPP_EXTENSION := .cc + +LOCAL_SRC_FILES := gmock-all.cc + +LOCAL_C_INCLUDES := $(libgmock_target_includes) + +LOCAL_MODULE := libgmock + +include $(BUILD_STATIC_LIBRARY) + +####################################################################### +# gmock_main lib target + +include $(CLEAR_VARS) + +ifeq ($(TARGET_ARCH), arm) + LOCAL_SDK_VERSION := 8 +else +# NDK support of other archs (ie. x86 and mips) are only available after android-9 + LOCAL_SDK_VERSION := 9 +endif + +LOCAL_NDK_STL_VARIANT := stlport_static + +LOCAL_CPP_EXTENSION := .cc + +LOCAL_SRC_FILES := gmock_main.cc + +LOCAL_C_INCLUDES := $(libgmock_target_includes) + +LOCAL_MODULE := libgmock_main + +include $(BUILD_STATIC_LIBRARY) diff --git a/libwvdrmengine/test/gmock/src/gmock-all.cc b/libwvdrmengine/test/gmock/src/gmock-all.cc new file mode 100644 index 00000000..7aebce7a --- /dev/null +++ b/libwvdrmengine/test/gmock/src/gmock-all.cc @@ -0,0 +1,47 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) +// +// Google C++ Mocking Framework (Google Mock) +// +// This file #includes all Google Mock implementation .cc files. The +// purpose is to allow a user to build Google Mock by compiling this +// file alone. + +// This line ensures that gmock.h can be compiled on its own, even +// when it's fused. +#include "gmock/gmock.h" + +// The following lines pull in the real gmock *.cc files. +#include "src/gmock-cardinalities.cc" +#include "src/gmock-internal-utils.cc" +#include "src/gmock-matchers.cc" +#include "src/gmock-spec-builders.cc" +#include "src/gmock.cc" diff --git a/libwvdrmengine/test/gmock/src/gmock-cardinalities.cc b/libwvdrmengine/test/gmock/src/gmock-cardinalities.cc new file mode 100644 index 00000000..1a7902b4 --- /dev/null +++ b/libwvdrmengine/test/gmock/src/gmock-cardinalities.cc @@ -0,0 +1,155 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file implements cardinalities. + +#include "gmock/gmock-cardinalities.h" + +#include +#include // NOLINT +#include +#include +#include "gmock/internal/gmock-internal-utils.h" +#include "gtest/gtest.h" + +namespace testing { + +namespace { + +// Implements the Between(m, n) cardinality. +class BetweenCardinalityImpl : public CardinalityInterface { + public: + BetweenCardinalityImpl(int min, int max) + : min_(min >= 0 ? min : 0), + max_(max >= min_ ? max : min_) { + std::stringstream ss; + if (min < 0) { + ss << "The invocation lower bound must be >= 0, " + << "but is actually " << min << "."; + internal::Expect(false, __FILE__, __LINE__, ss.str()); + } else if (max < 0) { + ss << "The invocation upper bound must be >= 0, " + << "but is actually " << max << "."; + internal::Expect(false, __FILE__, __LINE__, ss.str()); + } else if (min > max) { + ss << "The invocation upper bound (" << max + << ") must be >= the invocation lower bound (" << min + << ")."; + internal::Expect(false, __FILE__, __LINE__, ss.str()); + } + } + + // Conservative estimate on the lower/upper bound of the number of + // calls allowed. + virtual int ConservativeLowerBound() const { return min_; } + virtual int ConservativeUpperBound() const { return max_; } + + virtual bool IsSatisfiedByCallCount(int call_count) const { + return min_ <= call_count && call_count <= max_ ; + } + + virtual bool IsSaturatedByCallCount(int call_count) const { + return call_count >= max_; + } + + virtual void DescribeTo(::std::ostream* os) const; + private: + const int min_; + const int max_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(BetweenCardinalityImpl); +}; + +// Formats "n times" in a human-friendly way. +inline internal::string FormatTimes(int n) { + if (n == 1) { + return "once"; + } else if (n == 2) { + return "twice"; + } else { + std::stringstream ss; + ss << n << " times"; + return ss.str(); + } +} + +// Describes the Between(m, n) cardinality in human-friendly text. +void BetweenCardinalityImpl::DescribeTo(::std::ostream* os) const { + if (min_ == 0) { + if (max_ == 0) { + *os << "never called"; + } else if (max_ == INT_MAX) { + *os << "called any number of times"; + } else { + *os << "called at most " << FormatTimes(max_); + } + } else if (min_ == max_) { + *os << "called " << FormatTimes(min_); + } else if (max_ == INT_MAX) { + *os << "called at least " << FormatTimes(min_); + } else { + // 0 < min_ < max_ < INT_MAX + *os << "called between " << min_ << " and " << max_ << " times"; + } +} + +} // Unnamed namespace + +// Describes the given call count to an ostream. +void Cardinality::DescribeActualCallCountTo(int actual_call_count, + ::std::ostream* os) { + if (actual_call_count > 0) { + *os << "called " << FormatTimes(actual_call_count); + } else { + *os << "never called"; + } +} + +// Creates a cardinality that allows at least n calls. +Cardinality AtLeast(int n) { return Between(n, INT_MAX); } + +// Creates a cardinality that allows at most n calls. +Cardinality AtMost(int n) { return Between(0, n); } + +// Creates a cardinality that allows any number of calls. +Cardinality AnyNumber() { return AtLeast(0); } + +// Creates a cardinality that allows between min and max calls. +Cardinality Between(int min, int max) { + return Cardinality(new BetweenCardinalityImpl(min, max)); +} + +// Creates a cardinality that allows exactly n calls. +Cardinality Exactly(int n) { return Between(n, n); } + +} // namespace testing diff --git a/libwvdrmengine/test/gmock/src/gmock-internal-utils.cc b/libwvdrmengine/test/gmock/src/gmock-internal-utils.cc new file mode 100644 index 00000000..dd38132a --- /dev/null +++ b/libwvdrmengine/test/gmock/src/gmock-internal-utils.cc @@ -0,0 +1,173 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file defines some utilities useful for implementing Google +// Mock. They are subject to change without notice, so please DO NOT +// USE THEM IN USER CODE. + +#include "gmock/internal/gmock-internal-utils.h" + +#include +#include // NOLINT +#include +#include "gmock/gmock.h" +#include "gmock/internal/gmock-port.h" +#include "gtest/gtest.h" + +namespace testing { +namespace internal { + +// Converts an identifier name to a space-separated list of lower-case +// words. Each maximum substring of the form [A-Za-z][a-z]*|\d+ is +// treated as one word. For example, both "FooBar123" and +// "foo_bar_123" are converted to "foo bar 123". +string ConvertIdentifierNameToWords(const char* id_name) { + string result; + char prev_char = '\0'; + for (const char* p = id_name; *p != '\0'; prev_char = *(p++)) { + // We don't care about the current locale as the input is + // guaranteed to be a valid C++ identifier name. + const bool starts_new_word = IsUpper(*p) || + (!IsAlpha(prev_char) && IsLower(*p)) || + (!IsDigit(prev_char) && IsDigit(*p)); + + if (IsAlNum(*p)) { + if (starts_new_word && result != "") + result += ' '; + result += ToLower(*p); + } + } + return result; +} + +// This class reports Google Mock failures as Google Test failures. A +// user can define another class in a similar fashion if he intends to +// use Google Mock with a testing framework other than Google Test. +class GoogleTestFailureReporter : public FailureReporterInterface { + public: + virtual void ReportFailure(FailureType type, const char* file, int line, + const string& message) { + AssertHelper(type == FATAL ? + TestPartResult::kFatalFailure : + TestPartResult::kNonFatalFailure, + file, + line, + message.c_str()) = Message(); + if (type == FATAL) { + posix::Abort(); + } + } +}; + +// Returns the global failure reporter. Will create a +// GoogleTestFailureReporter and return it the first time called. +FailureReporterInterface* GetFailureReporter() { + // Points to the global failure reporter used by Google Mock. gcc + // guarantees that the following use of failure_reporter is + // thread-safe. We may need to add additional synchronization to + // protect failure_reporter if we port Google Mock to other + // compilers. + static FailureReporterInterface* const failure_reporter = + new GoogleTestFailureReporter(); + return failure_reporter; +} + +// Protects global resources (stdout in particular) used by Log(). +static GTEST_DEFINE_STATIC_MUTEX_(g_log_mutex); + +// Returns true iff a log with the given severity is visible according +// to the --gmock_verbose flag. +bool LogIsVisible(LogSeverity severity) { + if (GMOCK_FLAG(verbose) == kInfoVerbosity) { + // Always show the log if --gmock_verbose=info. + return true; + } else if (GMOCK_FLAG(verbose) == kErrorVerbosity) { + // Always hide it if --gmock_verbose=error. + return false; + } else { + // If --gmock_verbose is neither "info" nor "error", we treat it + // as "warning" (its default value). + return severity == WARNING; + } +} + +// Prints the given message to stdout iff 'severity' >= the level +// specified by the --gmock_verbose flag. If stack_frames_to_skip >= +// 0, also prints the stack trace excluding the top +// stack_frames_to_skip frames. In opt mode, any positive +// stack_frames_to_skip is treated as 0, since we don't know which +// function calls will be inlined by the compiler and need to be +// conservative. +void Log(LogSeverity severity, const string& message, + int stack_frames_to_skip) { + if (!LogIsVisible(severity)) + return; + + // Ensures that logs from different threads don't interleave. + MutexLock l(&g_log_mutex); + + // "using ::std::cout;" doesn't work with Symbian's STLport, where cout is a + // macro. + + if (severity == WARNING) { + // Prints a GMOCK WARNING marker to make the warnings easily searchable. + std::cout << "\nGMOCK WARNING:"; + } + // Pre-pends a new-line to message if it doesn't start with one. + if (message.empty() || message[0] != '\n') { + std::cout << "\n"; + } + std::cout << message; + if (stack_frames_to_skip >= 0) { +#ifdef NDEBUG + // In opt mode, we have to be conservative and skip no stack frame. + const int actual_to_skip = 0; +#else + // In dbg mode, we can do what the caller tell us to do (plus one + // for skipping this function's stack frame). + const int actual_to_skip = stack_frames_to_skip + 1; +#endif // NDEBUG + + // Appends a new-line to message if it doesn't end with one. + if (!message.empty() && *message.rbegin() != '\n') { + std::cout << "\n"; + } + std::cout << "Stack trace:\n" + << ::testing::internal::GetCurrentOsStackTraceExceptTop( + ::testing::UnitTest::GetInstance(), actual_to_skip); + } + std::cout << ::std::flush; +} + +} // namespace internal +} // namespace testing diff --git a/libwvdrmengine/test/gmock/src/gmock-matchers.cc b/libwvdrmengine/test/gmock/src/gmock-matchers.cc new file mode 100644 index 00000000..a5e6824d --- /dev/null +++ b/libwvdrmengine/test/gmock/src/gmock-matchers.cc @@ -0,0 +1,101 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file implements Matcher, Matcher, and +// utilities for defining matchers. + +#include "gmock/gmock-matchers.h" +#include "gmock/gmock-generated-matchers.h" + +#include +#include +#include + +namespace testing { + +// Constructs a matcher that matches a const string& whose value is +// equal to s. +Matcher::Matcher(const internal::string& s) { + *this = Eq(s); +} + +// Constructs a matcher that matches a const string& whose value is +// equal to s. +Matcher::Matcher(const char* s) { + *this = Eq(internal::string(s)); +} + +// Constructs a matcher that matches a string whose value is equal to s. +Matcher::Matcher(const internal::string& s) { *this = Eq(s); } + +// Constructs a matcher that matches a string whose value is equal to s. +Matcher::Matcher(const char* s) { + *this = Eq(internal::string(s)); +} + +namespace internal { + +// Joins a vector of strings as if they are fields of a tuple; returns +// the joined string. +string JoinAsTuple(const Strings& fields) { + switch (fields.size()) { + case 0: + return ""; + case 1: + return fields[0]; + default: + string result = "(" + fields[0]; + for (size_t i = 1; i < fields.size(); i++) { + result += ", "; + result += fields[i]; + } + result += ")"; + return result; + } +} + +// Returns the description for a matcher defined using the MATCHER*() +// macro where the user-supplied description string is "", if +// 'negation' is false; otherwise returns the description of the +// negation of the matcher. 'param_values' contains a list of strings +// that are the print-out of the matcher's parameters. +string FormatMatcherDescription(bool negation, const char* matcher_name, + const Strings& param_values) { + string result = ConvertIdentifierNameToWords(matcher_name); + if (param_values.size() >= 1) + result += " " + JoinAsTuple(param_values); + return negation ? "not (" + result + ")" : result; +} + +} // namespace internal +} // namespace testing diff --git a/libwvdrmengine/test/gmock/src/gmock-spec-builders.cc b/libwvdrmengine/test/gmock/src/gmock-spec-builders.cc new file mode 100644 index 00000000..aa33cc44 --- /dev/null +++ b/libwvdrmengine/test/gmock/src/gmock-spec-builders.cc @@ -0,0 +1,797 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file implements the spec builder syntax (ON_CALL and +// EXPECT_CALL). + +#include "gmock/gmock-spec-builders.h" + +#include +#include // NOLINT +#include +#include +#include +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#if GTEST_OS_CYGWIN || GTEST_OS_LINUX || GTEST_OS_MAC +# include // NOLINT +#endif + +namespace testing { +namespace internal { + +// Protects the mock object registry (in class Mock), all function +// mockers, and all expectations. +GTEST_DEFINE_STATIC_MUTEX_(g_gmock_mutex); + +// Logs a message including file and line number information. +void LogWithLocation(testing::internal::LogSeverity severity, + const char* file, int line, + const string& message) { + ::std::ostringstream s; + s << file << ":" << line << ": " << message << ::std::endl; + Log(severity, s.str(), 0); +} + +// Constructs an ExpectationBase object. +ExpectationBase::ExpectationBase(const char* a_file, + int a_line, + const string& a_source_text) + : file_(a_file), + line_(a_line), + source_text_(a_source_text), + cardinality_specified_(false), + cardinality_(Exactly(1)), + call_count_(0), + retired_(false), + extra_matcher_specified_(false), + repeated_action_specified_(false), + retires_on_saturation_(false), + last_clause_(kNone), + action_count_checked_(false) {} + +// Destructs an ExpectationBase object. +ExpectationBase::~ExpectationBase() {} + +// Explicitly specifies the cardinality of this expectation. Used by +// the subclasses to implement the .Times() clause. +void ExpectationBase::SpecifyCardinality(const Cardinality& a_cardinality) { + cardinality_specified_ = true; + cardinality_ = a_cardinality; +} + +// Retires all pre-requisites of this expectation. +void ExpectationBase::RetireAllPreRequisites() { + if (is_retired()) { + // We can take this short-cut as we never retire an expectation + // until we have retired all its pre-requisites. + return; + } + + for (ExpectationSet::const_iterator it = immediate_prerequisites_.begin(); + it != immediate_prerequisites_.end(); ++it) { + ExpectationBase* const prerequisite = it->expectation_base().get(); + if (!prerequisite->is_retired()) { + prerequisite->RetireAllPreRequisites(); + prerequisite->Retire(); + } + } +} + +// Returns true iff all pre-requisites of this expectation have been +// satisfied. +// L >= g_gmock_mutex +bool ExpectationBase::AllPrerequisitesAreSatisfied() const { + g_gmock_mutex.AssertHeld(); + for (ExpectationSet::const_iterator it = immediate_prerequisites_.begin(); + it != immediate_prerequisites_.end(); ++it) { + if (!(it->expectation_base()->IsSatisfied()) || + !(it->expectation_base()->AllPrerequisitesAreSatisfied())) + return false; + } + return true; +} + +// Adds unsatisfied pre-requisites of this expectation to 'result'. +// L >= g_gmock_mutex +void ExpectationBase::FindUnsatisfiedPrerequisites( + ExpectationSet* result) const { + g_gmock_mutex.AssertHeld(); + for (ExpectationSet::const_iterator it = immediate_prerequisites_.begin(); + it != immediate_prerequisites_.end(); ++it) { + if (it->expectation_base()->IsSatisfied()) { + // If *it is satisfied and has a call count of 0, some of its + // pre-requisites may not be satisfied yet. + if (it->expectation_base()->call_count_ == 0) { + it->expectation_base()->FindUnsatisfiedPrerequisites(result); + } + } else { + // Now that we know *it is unsatisfied, we are not so interested + // in whether its pre-requisites are satisfied. Therefore we + // don't recursively call FindUnsatisfiedPrerequisites() here. + *result += *it; + } + } +} + +// Describes how many times a function call matching this +// expectation has occurred. +// L >= g_gmock_mutex +void ExpectationBase::DescribeCallCountTo(::std::ostream* os) const { + g_gmock_mutex.AssertHeld(); + + // Describes how many times the function is expected to be called. + *os << " Expected: to be "; + cardinality().DescribeTo(os); + *os << "\n Actual: "; + Cardinality::DescribeActualCallCountTo(call_count(), os); + + // Describes the state of the expectation (e.g. is it satisfied? + // is it active?). + *os << " - " << (IsOverSaturated() ? "over-saturated" : + IsSaturated() ? "saturated" : + IsSatisfied() ? "satisfied" : "unsatisfied") + << " and " + << (is_retired() ? "retired" : "active"); +} + +// Checks the action count (i.e. the number of WillOnce() and +// WillRepeatedly() clauses) against the cardinality if this hasn't +// been done before. Prints a warning if there are too many or too +// few actions. +// L < mutex_ +void ExpectationBase::CheckActionCountIfNotDone() const { + bool should_check = false; + { + MutexLock l(&mutex_); + if (!action_count_checked_) { + action_count_checked_ = true; + should_check = true; + } + } + + if (should_check) { + if (!cardinality_specified_) { + // The cardinality was inferred - no need to check the action + // count against it. + return; + } + + // The cardinality was explicitly specified. + const int action_count = static_cast(untyped_actions_.size()); + const int upper_bound = cardinality().ConservativeUpperBound(); + const int lower_bound = cardinality().ConservativeLowerBound(); + bool too_many; // True if there are too many actions, or false + // if there are too few. + if (action_count > upper_bound || + (action_count == upper_bound && repeated_action_specified_)) { + too_many = true; + } else if (0 < action_count && action_count < lower_bound && + !repeated_action_specified_) { + too_many = false; + } else { + return; + } + + ::std::stringstream ss; + DescribeLocationTo(&ss); + ss << "Too " << (too_many ? "many" : "few") + << " actions specified in " << source_text() << "...\n" + << "Expected to be "; + cardinality().DescribeTo(&ss); + ss << ", but has " << (too_many ? "" : "only ") + << action_count << " WillOnce()" + << (action_count == 1 ? "" : "s"); + if (repeated_action_specified_) { + ss << " and a WillRepeatedly()"; + } + ss << "."; + Log(WARNING, ss.str(), -1); // -1 means "don't print stack trace". + } +} + +// Implements the .Times() clause. +void ExpectationBase::UntypedTimes(const Cardinality& a_cardinality) { + if (last_clause_ == kTimes) { + ExpectSpecProperty(false, + ".Times() cannot appear " + "more than once in an EXPECT_CALL()."); + } else { + ExpectSpecProperty(last_clause_ < kTimes, + ".Times() cannot appear after " + ".InSequence(), .WillOnce(), .WillRepeatedly(), " + "or .RetiresOnSaturation()."); + } + last_clause_ = kTimes; + + SpecifyCardinality(a_cardinality); +} + +// Points to the implicit sequence introduced by a living InSequence +// object (if any) in the current thread or NULL. +ThreadLocal g_gmock_implicit_sequence; + +// Reports an uninteresting call (whose description is in msg) in the +// manner specified by 'reaction'. +void ReportUninterestingCall(CallReaction reaction, const string& msg) { + switch (reaction) { + case ALLOW: + Log(INFO, msg, 3); + break; + case WARN: + Log(WARNING, msg, 3); + break; + default: // FAIL + Expect(false, NULL, -1, msg); + } +} + +UntypedFunctionMockerBase::UntypedFunctionMockerBase() + : mock_obj_(NULL), name_("") {} + +UntypedFunctionMockerBase::~UntypedFunctionMockerBase() {} + +// Sets the mock object this mock method belongs to, and registers +// this information in the global mock registry. Will be called +// whenever an EXPECT_CALL() or ON_CALL() is executed on this mock +// method. +// L < g_gmock_mutex +void UntypedFunctionMockerBase::RegisterOwner(const void* mock_obj) { + { + MutexLock l(&g_gmock_mutex); + mock_obj_ = mock_obj; + } + Mock::Register(mock_obj, this); +} + +// Sets the mock object this mock method belongs to, and sets the name +// of the mock function. Will be called upon each invocation of this +// mock function. +// L < g_gmock_mutex +void UntypedFunctionMockerBase::SetOwnerAndName( + const void* mock_obj, const char* name) { + // We protect name_ under g_gmock_mutex in case this mock function + // is called from two threads concurrently. + MutexLock l(&g_gmock_mutex); + mock_obj_ = mock_obj; + name_ = name; +} + +// Returns the name of the function being mocked. Must be called +// after RegisterOwner() or SetOwnerAndName() has been called. +// L < g_gmock_mutex +const void* UntypedFunctionMockerBase::MockObject() const { + const void* mock_obj; + { + // We protect mock_obj_ under g_gmock_mutex in case this mock + // function is called from two threads concurrently. + MutexLock l(&g_gmock_mutex); + Assert(mock_obj_ != NULL, __FILE__, __LINE__, + "MockObject() must not be called before RegisterOwner() or " + "SetOwnerAndName() has been called."); + mock_obj = mock_obj_; + } + return mock_obj; +} + +// Returns the name of this mock method. Must be called after +// SetOwnerAndName() has been called. +// L < g_gmock_mutex +const char* UntypedFunctionMockerBase::Name() const { + const char* name; + { + // We protect name_ under g_gmock_mutex in case this mock + // function is called from two threads concurrently. + MutexLock l(&g_gmock_mutex); + Assert(name_ != NULL, __FILE__, __LINE__, + "Name() must not be called before SetOwnerAndName() has " + "been called."); + name = name_; + } + return name; +} + +// Calculates the result of invoking this mock function with the given +// arguments, prints it, and returns it. The caller is responsible +// for deleting the result. +// L < g_gmock_mutex +const UntypedActionResultHolderBase* +UntypedFunctionMockerBase::UntypedInvokeWith(const void* const untyped_args) { + if (untyped_expectations_.size() == 0) { + // No expectation is set on this mock method - we have an + // uninteresting call. + + // We must get Google Mock's reaction on uninteresting calls + // made on this mock object BEFORE performing the action, + // because the action may DELETE the mock object and make the + // following expression meaningless. + const CallReaction reaction = + Mock::GetReactionOnUninterestingCalls(MockObject()); + + // True iff we need to print this call's arguments and return + // value. This definition must be kept in sync with + // the behavior of ReportUninterestingCall(). + const bool need_to_report_uninteresting_call = + // If the user allows this uninteresting call, we print it + // only when he wants informational messages. + reaction == ALLOW ? LogIsVisible(INFO) : + // If the user wants this to be a warning, we print it only + // when he wants to see warnings. + reaction == WARN ? LogIsVisible(WARNING) : + // Otherwise, the user wants this to be an error, and we + // should always print detailed information in the error. + true; + + if (!need_to_report_uninteresting_call) { + // Perform the action without printing the call information. + return this->UntypedPerformDefaultAction(untyped_args, ""); + } + + // Warns about the uninteresting call. + ::std::stringstream ss; + this->UntypedDescribeUninterestingCall(untyped_args, &ss); + + // Calculates the function result. + const UntypedActionResultHolderBase* const result = + this->UntypedPerformDefaultAction(untyped_args, ss.str()); + + // Prints the function result. + if (result != NULL) + result->PrintAsActionResult(&ss); + + ReportUninterestingCall(reaction, ss.str()); + return result; + } + + bool is_excessive = false; + ::std::stringstream ss; + ::std::stringstream why; + ::std::stringstream loc; + const void* untyped_action = NULL; + + // The UntypedFindMatchingExpectation() function acquires and + // releases g_gmock_mutex. + const ExpectationBase* const untyped_expectation = + this->UntypedFindMatchingExpectation( + untyped_args, &untyped_action, &is_excessive, + &ss, &why); + const bool found = untyped_expectation != NULL; + + // True iff we need to print the call's arguments and return value. + // This definition must be kept in sync with the uses of Expect() + // and Log() in this function. + const bool need_to_report_call = !found || is_excessive || LogIsVisible(INFO); + if (!need_to_report_call) { + // Perform the action without printing the call information. + return + untyped_action == NULL ? + this->UntypedPerformDefaultAction(untyped_args, "") : + this->UntypedPerformAction(untyped_action, untyped_args); + } + + ss << " Function call: " << Name(); + this->UntypedPrintArgs(untyped_args, &ss); + + // In case the action deletes a piece of the expectation, we + // generate the message beforehand. + if (found && !is_excessive) { + untyped_expectation->DescribeLocationTo(&loc); + } + + const UntypedActionResultHolderBase* const result = + untyped_action == NULL ? + this->UntypedPerformDefaultAction(untyped_args, ss.str()) : + this->UntypedPerformAction(untyped_action, untyped_args); + if (result != NULL) + result->PrintAsActionResult(&ss); + ss << "\n" << why.str(); + + if (!found) { + // No expectation matches this call - reports a failure. + Expect(false, NULL, -1, ss.str()); + } else if (is_excessive) { + // We had an upper-bound violation and the failure message is in ss. + Expect(false, untyped_expectation->file(), + untyped_expectation->line(), ss.str()); + } else { + // We had an expected call and the matching expectation is + // described in ss. + Log(INFO, loc.str() + ss.str(), 2); + } + + return result; +} + +// Returns an Expectation object that references and co-owns exp, +// which must be an expectation on this mock function. +Expectation UntypedFunctionMockerBase::GetHandleOf(ExpectationBase* exp) { + for (UntypedExpectations::const_iterator it = + untyped_expectations_.begin(); + it != untyped_expectations_.end(); ++it) { + if (it->get() == exp) { + return Expectation(*it); + } + } + + Assert(false, __FILE__, __LINE__, "Cannot find expectation."); + return Expectation(); + // The above statement is just to make the code compile, and will + // never be executed. +} + +// Verifies that all expectations on this mock function have been +// satisfied. Reports one or more Google Test non-fatal failures +// and returns false if not. +// L >= g_gmock_mutex +bool UntypedFunctionMockerBase::VerifyAndClearExpectationsLocked() { + g_gmock_mutex.AssertHeld(); + bool expectations_met = true; + for (UntypedExpectations::const_iterator it = + untyped_expectations_.begin(); + it != untyped_expectations_.end(); ++it) { + ExpectationBase* const untyped_expectation = it->get(); + if (untyped_expectation->IsOverSaturated()) { + // There was an upper-bound violation. Since the error was + // already reported when it occurred, there is no need to do + // anything here. + expectations_met = false; + } else if (!untyped_expectation->IsSatisfied()) { + expectations_met = false; + ::std::stringstream ss; + ss << "Actual function call count doesn't match " + << untyped_expectation->source_text() << "...\n"; + // No need to show the source file location of the expectation + // in the description, as the Expect() call that follows already + // takes care of it. + untyped_expectation->MaybeDescribeExtraMatcherTo(&ss); + untyped_expectation->DescribeCallCountTo(&ss); + Expect(false, untyped_expectation->file(), + untyped_expectation->line(), ss.str()); + } + } + untyped_expectations_.clear(); + return expectations_met; +} + +} // namespace internal + +// Class Mock. + +namespace { + +typedef std::set FunctionMockers; + +// The current state of a mock object. Such information is needed for +// detecting leaked mock objects and explicitly verifying a mock's +// expectations. +struct MockObjectState { + MockObjectState() + : first_used_file(NULL), first_used_line(-1), leakable(false) {} + + // Where in the source file an ON_CALL or EXPECT_CALL is first + // invoked on this mock object. + const char* first_used_file; + int first_used_line; + ::std::string first_used_test_case; + ::std::string first_used_test; + bool leakable; // true iff it's OK to leak the object. + FunctionMockers function_mockers; // All registered methods of the object. +}; + +// A global registry holding the state of all mock objects that are +// alive. A mock object is added to this registry the first time +// Mock::AllowLeak(), ON_CALL(), or EXPECT_CALL() is called on it. It +// is removed from the registry in the mock object's destructor. +class MockObjectRegistry { + public: + // Maps a mock object (identified by its address) to its state. + typedef std::map StateMap; + + // This destructor will be called when a program exits, after all + // tests in it have been run. By then, there should be no mock + // object alive. Therefore we report any living object as test + // failure, unless the user explicitly asked us to ignore it. + ~MockObjectRegistry() { + // "using ::std::cout;" doesn't work with Symbian's STLport, where cout is + // a macro. + + if (!GMOCK_FLAG(catch_leaked_mocks)) + return; + + int leaked_count = 0; + for (StateMap::const_iterator it = states_.begin(); it != states_.end(); + ++it) { + if (it->second.leakable) // The user said it's fine to leak this object. + continue; + + // TODO(wan@google.com): Print the type of the leaked object. + // This can help the user identify the leaked object. + std::cout << "\n"; + const MockObjectState& state = it->second; + std::cout << internal::FormatFileLocation(state.first_used_file, + state.first_used_line); + std::cout << " ERROR: this mock object"; + if (state.first_used_test != "") { + std::cout << " (used in test " << state.first_used_test_case << "." + << state.first_used_test << ")"; + } + std::cout << " should be deleted but never is. Its address is @" + << it->first << "."; + leaked_count++; + } + if (leaked_count > 0) { + std::cout << "\nERROR: " << leaked_count + << " leaked mock " << (leaked_count == 1 ? "object" : "objects") + << " found at program exit.\n"; + std::cout.flush(); + ::std::cerr.flush(); + // RUN_ALL_TESTS() has already returned when this destructor is + // called. Therefore we cannot use the normal Google Test + // failure reporting mechanism. + _exit(1); // We cannot call exit() as it is not reentrant and + // may already have been called. + } + } + + StateMap& states() { return states_; } + private: + StateMap states_; +}; + +// Protected by g_gmock_mutex. +MockObjectRegistry g_mock_object_registry; + +// Maps a mock object to the reaction Google Mock should have when an +// uninteresting method is called. Protected by g_gmock_mutex. +std::map g_uninteresting_call_reaction; + +// Sets the reaction Google Mock should have when an uninteresting +// method of the given mock object is called. +// L < g_gmock_mutex +void SetReactionOnUninterestingCalls(const void* mock_obj, + internal::CallReaction reaction) { + internal::MutexLock l(&internal::g_gmock_mutex); + g_uninteresting_call_reaction[mock_obj] = reaction; +} + +} // namespace + +// Tells Google Mock to allow uninteresting calls on the given mock +// object. +// L < g_gmock_mutex +void Mock::AllowUninterestingCalls(const void* mock_obj) { + SetReactionOnUninterestingCalls(mock_obj, internal::ALLOW); +} + +// Tells Google Mock to warn the user about uninteresting calls on the +// given mock object. +// L < g_gmock_mutex +void Mock::WarnUninterestingCalls(const void* mock_obj) { + SetReactionOnUninterestingCalls(mock_obj, internal::WARN); +} + +// Tells Google Mock to fail uninteresting calls on the given mock +// object. +// L < g_gmock_mutex +void Mock::FailUninterestingCalls(const void* mock_obj) { + SetReactionOnUninterestingCalls(mock_obj, internal::FAIL); +} + +// Tells Google Mock the given mock object is being destroyed and its +// entry in the call-reaction table should be removed. +// L < g_gmock_mutex +void Mock::UnregisterCallReaction(const void* mock_obj) { + internal::MutexLock l(&internal::g_gmock_mutex); + g_uninteresting_call_reaction.erase(mock_obj); +} + +// Returns the reaction Google Mock will have on uninteresting calls +// made on the given mock object. +// L < g_gmock_mutex +internal::CallReaction Mock::GetReactionOnUninterestingCalls( + const void* mock_obj) { + internal::MutexLock l(&internal::g_gmock_mutex); + return (g_uninteresting_call_reaction.count(mock_obj) == 0) ? + internal::WARN : g_uninteresting_call_reaction[mock_obj]; +} + +// Tells Google Mock to ignore mock_obj when checking for leaked mock +// objects. +// L < g_gmock_mutex +void Mock::AllowLeak(const void* mock_obj) { + internal::MutexLock l(&internal::g_gmock_mutex); + g_mock_object_registry.states()[mock_obj].leakable = true; +} + +// Verifies and clears all expectations on the given mock object. If +// the expectations aren't satisfied, generates one or more Google +// Test non-fatal failures and returns false. +// L < g_gmock_mutex +bool Mock::VerifyAndClearExpectations(void* mock_obj) { + internal::MutexLock l(&internal::g_gmock_mutex); + return VerifyAndClearExpectationsLocked(mock_obj); +} + +// Verifies all expectations on the given mock object and clears its +// default actions and expectations. Returns true iff the +// verification was successful. +// L < g_gmock_mutex +bool Mock::VerifyAndClear(void* mock_obj) { + internal::MutexLock l(&internal::g_gmock_mutex); + ClearDefaultActionsLocked(mock_obj); + return VerifyAndClearExpectationsLocked(mock_obj); +} + +// Verifies and clears all expectations on the given mock object. If +// the expectations aren't satisfied, generates one or more Google +// Test non-fatal failures and returns false. +// L >= g_gmock_mutex +bool Mock::VerifyAndClearExpectationsLocked(void* mock_obj) { + internal::g_gmock_mutex.AssertHeld(); + if (g_mock_object_registry.states().count(mock_obj) == 0) { + // No EXPECT_CALL() was set on the given mock object. + return true; + } + + // Verifies and clears the expectations on each mock method in the + // given mock object. + bool expectations_met = true; + FunctionMockers& mockers = + g_mock_object_registry.states()[mock_obj].function_mockers; + for (FunctionMockers::const_iterator it = mockers.begin(); + it != mockers.end(); ++it) { + if (!(*it)->VerifyAndClearExpectationsLocked()) { + expectations_met = false; + } + } + + // We don't clear the content of mockers, as they may still be + // needed by ClearDefaultActionsLocked(). + return expectations_met; +} + +// Registers a mock object and a mock method it owns. +// L < g_gmock_mutex +void Mock::Register(const void* mock_obj, + internal::UntypedFunctionMockerBase* mocker) { + internal::MutexLock l(&internal::g_gmock_mutex); + g_mock_object_registry.states()[mock_obj].function_mockers.insert(mocker); +} + +// Tells Google Mock where in the source code mock_obj is used in an +// ON_CALL or EXPECT_CALL. In case mock_obj is leaked, this +// information helps the user identify which object it is. +// L < g_gmock_mutex +void Mock::RegisterUseByOnCallOrExpectCall( + const void* mock_obj, const char* file, int line) { + internal::MutexLock l(&internal::g_gmock_mutex); + MockObjectState& state = g_mock_object_registry.states()[mock_obj]; + if (state.first_used_file == NULL) { + state.first_used_file = file; + state.first_used_line = line; + const TestInfo* const test_info = + UnitTest::GetInstance()->current_test_info(); + if (test_info != NULL) { + // TODO(wan@google.com): record the test case name when the + // ON_CALL or EXPECT_CALL is invoked from SetUpTestCase() or + // TearDownTestCase(). + state.first_used_test_case = test_info->test_case_name(); + state.first_used_test = test_info->name(); + } + } +} + +// Unregisters a mock method; removes the owning mock object from the +// registry when the last mock method associated with it has been +// unregistered. This is called only in the destructor of +// FunctionMockerBase. +// L >= g_gmock_mutex +void Mock::UnregisterLocked(internal::UntypedFunctionMockerBase* mocker) { + internal::g_gmock_mutex.AssertHeld(); + for (MockObjectRegistry::StateMap::iterator it = + g_mock_object_registry.states().begin(); + it != g_mock_object_registry.states().end(); ++it) { + FunctionMockers& mockers = it->second.function_mockers; + if (mockers.erase(mocker) > 0) { + // mocker was in mockers and has been just removed. + if (mockers.empty()) { + g_mock_object_registry.states().erase(it); + } + return; + } + } +} + +// Clears all ON_CALL()s set on the given mock object. +// L >= g_gmock_mutex +void Mock::ClearDefaultActionsLocked(void* mock_obj) { + internal::g_gmock_mutex.AssertHeld(); + + if (g_mock_object_registry.states().count(mock_obj) == 0) { + // No ON_CALL() was set on the given mock object. + return; + } + + // Clears the default actions for each mock method in the given mock + // object. + FunctionMockers& mockers = + g_mock_object_registry.states()[mock_obj].function_mockers; + for (FunctionMockers::const_iterator it = mockers.begin(); + it != mockers.end(); ++it) { + (*it)->ClearDefaultActionsLocked(); + } + + // We don't clear the content of mockers, as they may still be + // needed by VerifyAndClearExpectationsLocked(). +} + +Expectation::Expectation() {} + +Expectation::Expectation( + const internal::linked_ptr& an_expectation_base) + : expectation_base_(an_expectation_base) {} + +Expectation::~Expectation() {} + +// Adds an expectation to a sequence. +void Sequence::AddExpectation(const Expectation& expectation) const { + if (*last_expectation_ != expectation) { + if (last_expectation_->expectation_base() != NULL) { + expectation.expectation_base()->immediate_prerequisites_ + += *last_expectation_; + } + *last_expectation_ = expectation; + } +} + +// Creates the implicit sequence if there isn't one. +InSequence::InSequence() { + if (internal::g_gmock_implicit_sequence.get() == NULL) { + internal::g_gmock_implicit_sequence.set(new Sequence); + sequence_created_ = true; + } else { + sequence_created_ = false; + } +} + +// Deletes the implicit sequence if it was created by the constructor +// of this object. +InSequence::~InSequence() { + if (sequence_created_) { + delete internal::g_gmock_implicit_sequence.get(); + internal::g_gmock_implicit_sequence.set(NULL); + } +} + +} // namespace testing diff --git a/libwvdrmengine/test/gmock/src/gmock.cc b/libwvdrmengine/test/gmock/src/gmock.cc new file mode 100644 index 00000000..700bcb2e --- /dev/null +++ b/libwvdrmengine/test/gmock/src/gmock.cc @@ -0,0 +1,182 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +#include "gmock/gmock.h" +#include "gmock/internal/gmock-port.h" + +namespace testing { + +// TODO(wan@google.com): support using environment variables to +// control the flag values, like what Google Test does. + +GMOCK_DEFINE_bool_(catch_leaked_mocks, true, + "true iff Google Mock should report leaked mock objects " + "as failures."); + +GMOCK_DEFINE_string_(verbose, internal::kWarningVerbosity, + "Controls how verbose Google Mock's output is." + " Valid values:\n" + " info - prints all messages.\n" + " warning - prints warnings and errors.\n" + " error - prints errors only."); + +namespace internal { + +// Parses a string as a command line flag. The string should have the +// format "--gmock_flag=value". When def_optional is true, the +// "=value" part can be omitted. +// +// Returns the value of the flag, or NULL if the parsing failed. +static const char* ParseGoogleMockFlagValue(const char* str, + const char* flag, + bool def_optional) { + // str and flag must not be NULL. + if (str == NULL || flag == NULL) return NULL; + + // The flag must start with "--gmock_". + const String flag_str = String::Format("--gmock_%s", flag); + const size_t flag_len = flag_str.length(); + if (strncmp(str, flag_str.c_str(), flag_len) != 0) return NULL; + + // Skips the flag name. + const char* flag_end = str + flag_len; + + // When def_optional is true, it's OK to not have a "=value" part. + if (def_optional && (flag_end[0] == '\0')) { + return flag_end; + } + + // If def_optional is true and there are more characters after the + // flag name, or if def_optional is false, there must be a '=' after + // the flag name. + if (flag_end[0] != '=') return NULL; + + // Returns the string after "=". + return flag_end + 1; +} + +// Parses a string for a Google Mock bool flag, in the form of +// "--gmock_flag=value". +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +static bool ParseGoogleMockBoolFlag(const char* str, const char* flag, + bool* value) { + // Gets the value of the flag as a string. + const char* const value_str = ParseGoogleMockFlagValue(str, flag, true); + + // Aborts if the parsing failed. + if (value_str == NULL) return false; + + // Converts the string value to a bool. + *value = !(*value_str == '0' || *value_str == 'f' || *value_str == 'F'); + return true; +} + +// Parses a string for a Google Mock string flag, in the form of +// "--gmock_flag=value". +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +static bool ParseGoogleMockStringFlag(const char* str, const char* flag, + String* value) { + // Gets the value of the flag as a string. + const char* const value_str = ParseGoogleMockFlagValue(str, flag, false); + + // Aborts if the parsing failed. + if (value_str == NULL) return false; + + // Sets *value to the value of the flag. + *value = value_str; + return true; +} + +// The internal implementation of InitGoogleMock(). +// +// The type parameter CharType can be instantiated to either char or +// wchar_t. +template +void InitGoogleMockImpl(int* argc, CharType** argv) { + // Makes sure Google Test is initialized. InitGoogleTest() is + // idempotent, so it's fine if the user has already called it. + InitGoogleTest(argc, argv); + if (*argc <= 0) return; + + for (int i = 1; i != *argc; i++) { + const String arg_string = StreamableToString(argv[i]); + const char* const arg = arg_string.c_str(); + + // Do we see a Google Mock flag? + if (ParseGoogleMockBoolFlag(arg, "catch_leaked_mocks", + &GMOCK_FLAG(catch_leaked_mocks)) || + ParseGoogleMockStringFlag(arg, "verbose", &GMOCK_FLAG(verbose))) { + // Yes. Shift the remainder of the argv list left by one. Note + // that argv has (*argc + 1) elements, the last one always being + // NULL. The following loop moves the trailing NULL element as + // well. + for (int j = i; j != *argc; j++) { + argv[j] = argv[j + 1]; + } + + // Decrements the argument count. + (*argc)--; + + // We also need to decrement the iterator as we just removed + // an element. + i--; + } + } +} + +} // namespace internal + +// Initializes Google Mock. This must be called before running the +// tests. In particular, it parses a command line for the flags that +// Google Mock recognizes. Whenever a Google Mock flag is seen, it is +// removed from argv, and *argc is decremented. +// +// No value is returned. Instead, the Google Mock flag variables are +// updated. +// +// Since Google Test is needed for Google Mock to work, this function +// also initializes Google Test and parses its flags, if that hasn't +// been done. +void InitGoogleMock(int* argc, char** argv) { + internal::InitGoogleMockImpl(argc, argv); +} + +// This overloaded version can be used in Windows programs compiled in +// UNICODE mode. +void InitGoogleMock(int* argc, wchar_t** argv) { + internal::InitGoogleMockImpl(argc, argv); +} + +} // namespace testing diff --git a/libwvdrmengine/test/gmock/src/gmock_main.cc b/libwvdrmengine/test/gmock/src/gmock_main.cc new file mode 100644 index 00000000..9d8aea22 --- /dev/null +++ b/libwvdrmengine/test/gmock/src/gmock_main.cc @@ -0,0 +1,54 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +#include +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +// MS C++ compiler/linker has a bug on Windows (not on Windows CE), which +// causes a link error when _tmain is defined in a static library and UNICODE +// is enabled. For this reason instead of _tmain, main function is used on +// Windows. See the following link to track the current status of this bug: +// http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=394464 // NOLINT +#if GTEST_OS_WINDOWS_MOBILE +# include // NOLINT + +int _tmain(int argc, TCHAR** argv) { +#else +int main(int argc, char** argv) { +#endif // GTEST_OS_WINDOWS_MOBILE + std::cout << "Running main() from gmock_main.cc\n"; + // Since Google Mock depends on Google Test, InitGoogleMock() is + // also responsible for initializing Google Test. Therefore there's + // no need for calling testing::InitGoogleTest() separately. + testing::InitGoogleMock(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/libwvdrmengine/test/gmock/test/gmock-actions_test.cc b/libwvdrmengine/test/gmock/test/gmock-actions_test.cc new file mode 100644 index 00000000..b7803fe9 --- /dev/null +++ b/libwvdrmengine/test/gmock/test/gmock-actions_test.cc @@ -0,0 +1,1305 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file tests the built-in actions. + +#include "gmock/gmock-actions.h" +#include +#include +#include +#include "gmock/gmock.h" +#include "gmock/internal/gmock-port.h" +#include "gtest/gtest.h" +#include "gtest/gtest-spi.h" + +namespace { + +using ::std::tr1::get; +using ::std::tr1::make_tuple; +using ::std::tr1::tuple; +using ::std::tr1::tuple_element; +using testing::internal::BuiltInDefaultValue; +using testing::internal::Int64; +using testing::internal::UInt64; +// This list should be kept sorted. +using testing::_; +using testing::Action; +using testing::ActionInterface; +using testing::Assign; +using testing::ByRef; +using testing::DefaultValue; +using testing::DoDefault; +using testing::IgnoreResult; +using testing::Invoke; +using testing::InvokeWithoutArgs; +using testing::MakePolymorphicAction; +using testing::Ne; +using testing::PolymorphicAction; +using testing::Return; +using testing::ReturnNull; +using testing::ReturnRef; +using testing::ReturnRefOfCopy; +using testing::SetArgPointee; +using testing::SetArgumentPointee; + +#if !GTEST_OS_WINDOWS_MOBILE +using testing::SetErrnoAndReturn; +#endif + +#if GTEST_HAS_PROTOBUF_ +using testing::internal::TestMessage; +#endif // GTEST_HAS_PROTOBUF_ + +// Tests that BuiltInDefaultValue::Get() returns NULL. +TEST(BuiltInDefaultValueTest, IsNullForPointerTypes) { + EXPECT_TRUE(BuiltInDefaultValue::Get() == NULL); + EXPECT_TRUE(BuiltInDefaultValue::Get() == NULL); + EXPECT_TRUE(BuiltInDefaultValue::Get() == NULL); +} + +// Tests that BuiltInDefaultValue::Exists() return true. +TEST(BuiltInDefaultValueTest, ExistsForPointerTypes) { + EXPECT_TRUE(BuiltInDefaultValue::Exists()); + EXPECT_TRUE(BuiltInDefaultValue::Exists()); + EXPECT_TRUE(BuiltInDefaultValue::Exists()); +} + +// Tests that BuiltInDefaultValue::Get() returns 0 when T is a +// built-in numeric type. +TEST(BuiltInDefaultValueTest, IsZeroForNumericTypes) { + EXPECT_EQ(0U, BuiltInDefaultValue::Get()); + EXPECT_EQ(0, BuiltInDefaultValue::Get()); + EXPECT_EQ(0, BuiltInDefaultValue::Get()); +#if GMOCK_HAS_SIGNED_WCHAR_T_ + EXPECT_EQ(0U, BuiltInDefaultValue::Get()); + EXPECT_EQ(0, BuiltInDefaultValue::Get()); +#endif +#if GMOCK_WCHAR_T_IS_NATIVE_ + EXPECT_EQ(0, BuiltInDefaultValue::Get()); +#endif + EXPECT_EQ(0U, BuiltInDefaultValue::Get()); // NOLINT + EXPECT_EQ(0, BuiltInDefaultValue::Get()); // NOLINT + EXPECT_EQ(0, BuiltInDefaultValue::Get()); // NOLINT + EXPECT_EQ(0U, BuiltInDefaultValue::Get()); + EXPECT_EQ(0, BuiltInDefaultValue::Get()); + EXPECT_EQ(0, BuiltInDefaultValue::Get()); + EXPECT_EQ(0U, BuiltInDefaultValue::Get()); // NOLINT + EXPECT_EQ(0, BuiltInDefaultValue::Get()); // NOLINT + EXPECT_EQ(0, BuiltInDefaultValue::Get()); // NOLINT + EXPECT_EQ(0U, BuiltInDefaultValue::Get()); + EXPECT_EQ(0, BuiltInDefaultValue::Get()); + EXPECT_EQ(0, BuiltInDefaultValue::Get()); + EXPECT_EQ(0, BuiltInDefaultValue::Get()); +} + +// Tests that BuiltInDefaultValue::Exists() returns true when T is a +// built-in numeric type. +TEST(BuiltInDefaultValueTest, ExistsForNumericTypes) { + EXPECT_TRUE(BuiltInDefaultValue::Exists()); + EXPECT_TRUE(BuiltInDefaultValue::Exists()); + EXPECT_TRUE(BuiltInDefaultValue::Exists()); +#if GMOCK_HAS_SIGNED_WCHAR_T_ + EXPECT_TRUE(BuiltInDefaultValue::Exists()); + EXPECT_TRUE(BuiltInDefaultValue::Exists()); +#endif +#if GMOCK_WCHAR_T_IS_NATIVE_ + EXPECT_TRUE(BuiltInDefaultValue::Exists()); +#endif + EXPECT_TRUE(BuiltInDefaultValue::Exists()); // NOLINT + EXPECT_TRUE(BuiltInDefaultValue::Exists()); // NOLINT + EXPECT_TRUE(BuiltInDefaultValue::Exists()); // NOLINT + EXPECT_TRUE(BuiltInDefaultValue::Exists()); + EXPECT_TRUE(BuiltInDefaultValue::Exists()); + EXPECT_TRUE(BuiltInDefaultValue::Exists()); + EXPECT_TRUE(BuiltInDefaultValue::Exists()); // NOLINT + EXPECT_TRUE(BuiltInDefaultValue::Exists()); // NOLINT + EXPECT_TRUE(BuiltInDefaultValue::Exists()); // NOLINT + EXPECT_TRUE(BuiltInDefaultValue::Exists()); + EXPECT_TRUE(BuiltInDefaultValue::Exists()); + EXPECT_TRUE(BuiltInDefaultValue::Exists()); + EXPECT_TRUE(BuiltInDefaultValue::Exists()); +} + +// Tests that BuiltInDefaultValue::Get() returns false. +TEST(BuiltInDefaultValueTest, IsFalseForBool) { + EXPECT_FALSE(BuiltInDefaultValue::Get()); +} + +// Tests that BuiltInDefaultValue::Exists() returns true. +TEST(BuiltInDefaultValueTest, BoolExists) { + EXPECT_TRUE(BuiltInDefaultValue::Exists()); +} + +// Tests that BuiltInDefaultValue::Get() returns "" when T is a +// string type. +TEST(BuiltInDefaultValueTest, IsEmptyStringForString) { +#if GTEST_HAS_GLOBAL_STRING + EXPECT_EQ("", BuiltInDefaultValue< ::string>::Get()); +#endif // GTEST_HAS_GLOBAL_STRING + + EXPECT_EQ("", BuiltInDefaultValue< ::std::string>::Get()); +} + +// Tests that BuiltInDefaultValue::Exists() returns true when T is a +// string type. +TEST(BuiltInDefaultValueTest, ExistsForString) { +#if GTEST_HAS_GLOBAL_STRING + EXPECT_TRUE(BuiltInDefaultValue< ::string>::Exists()); +#endif // GTEST_HAS_GLOBAL_STRING + + EXPECT_TRUE(BuiltInDefaultValue< ::std::string>::Exists()); +} + +// Tests that BuiltInDefaultValue::Get() returns the same +// value as BuiltInDefaultValue::Get() does. +TEST(BuiltInDefaultValueTest, WorksForConstTypes) { + EXPECT_EQ("", BuiltInDefaultValue::Get()); + EXPECT_EQ(0, BuiltInDefaultValue::Get()); + EXPECT_TRUE(BuiltInDefaultValue::Get() == NULL); + EXPECT_FALSE(BuiltInDefaultValue::Get()); +} + +// Tests that BuiltInDefaultValue::Get() aborts the program with +// the correct error message when T is a user-defined type. +struct UserType { + UserType() : value(0) {} + + int value; +}; + +TEST(BuiltInDefaultValueTest, UserTypeHasNoDefault) { + EXPECT_FALSE(BuiltInDefaultValue::Exists()); +} + +// Tests that BuiltInDefaultValue::Get() aborts the program. +TEST(BuiltInDefaultValueDeathTest, IsUndefinedForReferences) { + EXPECT_DEATH_IF_SUPPORTED({ + BuiltInDefaultValue::Get(); + }, ""); + EXPECT_DEATH_IF_SUPPORTED({ + BuiltInDefaultValue::Get(); + }, ""); +} + +TEST(BuiltInDefaultValueDeathTest, IsUndefinedForUserTypes) { + EXPECT_DEATH_IF_SUPPORTED({ + BuiltInDefaultValue::Get(); + }, ""); +} + +// Tests that DefaultValue::IsSet() is false initially. +TEST(DefaultValueTest, IsInitiallyUnset) { + EXPECT_FALSE(DefaultValue::IsSet()); + EXPECT_FALSE(DefaultValue::IsSet()); +} + +// Tests that DefaultValue can be set and then unset. +TEST(DefaultValueTest, CanBeSetAndUnset) { + EXPECT_TRUE(DefaultValue::Exists()); + EXPECT_FALSE(DefaultValue::Exists()); + + DefaultValue::Set(1); + DefaultValue::Set(UserType()); + + EXPECT_EQ(1, DefaultValue::Get()); + EXPECT_EQ(0, DefaultValue::Get().value); + + EXPECT_TRUE(DefaultValue::Exists()); + EXPECT_TRUE(DefaultValue::Exists()); + + DefaultValue::Clear(); + DefaultValue::Clear(); + + EXPECT_FALSE(DefaultValue::IsSet()); + EXPECT_FALSE(DefaultValue::IsSet()); + + EXPECT_TRUE(DefaultValue::Exists()); + EXPECT_FALSE(DefaultValue::Exists()); +} + +// Tests that DefaultValue::Get() returns the +// BuiltInDefaultValue::Get() when DefaultValue::IsSet() is +// false. +TEST(DefaultValueDeathTest, GetReturnsBuiltInDefaultValueWhenUnset) { + EXPECT_FALSE(DefaultValue::IsSet()); + EXPECT_TRUE(DefaultValue::Exists()); + EXPECT_FALSE(DefaultValue::IsSet()); + EXPECT_FALSE(DefaultValue::Exists()); + + EXPECT_EQ(0, DefaultValue::Get()); + + EXPECT_DEATH_IF_SUPPORTED({ + DefaultValue::Get(); + }, ""); +} + +// Tests that DefaultValue::Get() returns void. +TEST(DefaultValueTest, GetWorksForVoid) { + return DefaultValue::Get(); +} + +// Tests using DefaultValue with a reference type. + +// Tests that DefaultValue::IsSet() is false initially. +TEST(DefaultValueOfReferenceTest, IsInitiallyUnset) { + EXPECT_FALSE(DefaultValue::IsSet()); + EXPECT_FALSE(DefaultValue::IsSet()); +} + +// Tests that DefaultValue::Exists is false initiallly. +TEST(DefaultValueOfReferenceTest, IsInitiallyNotExisting) { + EXPECT_FALSE(DefaultValue::Exists()); + EXPECT_FALSE(DefaultValue::Exists()); +} + +// Tests that DefaultValue can be set and then unset. +TEST(DefaultValueOfReferenceTest, CanBeSetAndUnset) { + int n = 1; + DefaultValue::Set(n); + UserType u; + DefaultValue::Set(u); + + EXPECT_TRUE(DefaultValue::Exists()); + EXPECT_TRUE(DefaultValue::Exists()); + + EXPECT_EQ(&n, &(DefaultValue::Get())); + EXPECT_EQ(&u, &(DefaultValue::Get())); + + DefaultValue::Clear(); + DefaultValue::Clear(); + + EXPECT_FALSE(DefaultValue::Exists()); + EXPECT_FALSE(DefaultValue::Exists()); + + EXPECT_FALSE(DefaultValue::IsSet()); + EXPECT_FALSE(DefaultValue::IsSet()); +} + +// Tests that DefaultValue::Get() returns the +// BuiltInDefaultValue::Get() when DefaultValue::IsSet() is +// false. +TEST(DefaultValueOfReferenceDeathTest, GetReturnsBuiltInDefaultValueWhenUnset) { + EXPECT_FALSE(DefaultValue::IsSet()); + EXPECT_FALSE(DefaultValue::IsSet()); + + EXPECT_DEATH_IF_SUPPORTED({ + DefaultValue::Get(); + }, ""); + EXPECT_DEATH_IF_SUPPORTED({ + DefaultValue::Get(); + }, ""); +} + +// Tests that ActionInterface can be implemented by defining the +// Perform method. + +typedef int MyFunction(bool, int); + +class MyActionImpl : public ActionInterface { + public: + virtual int Perform(const tuple& args) { + return get<0>(args) ? get<1>(args) : 0; + } +}; + +TEST(ActionInterfaceTest, CanBeImplementedByDefiningPerform) { + MyActionImpl my_action_impl; + (void)my_action_impl; +} + +TEST(ActionInterfaceTest, MakeAction) { + Action action = MakeAction(new MyActionImpl); + + // When exercising the Perform() method of Action, we must pass + // it a tuple whose size and type are compatible with F's argument + // types. For example, if F is int(), then Perform() takes a + // 0-tuple; if F is void(bool, int), then Perform() takes a + // tuple, and so on. + EXPECT_EQ(5, action.Perform(make_tuple(true, 5))); +} + +// Tests that Action can be contructed from a pointer to +// ActionInterface. +TEST(ActionTest, CanBeConstructedFromActionInterface) { + Action action(new MyActionImpl); +} + +// Tests that Action delegates actual work to ActionInterface. +TEST(ActionTest, DelegatesWorkToActionInterface) { + const Action action(new MyActionImpl); + + EXPECT_EQ(5, action.Perform(make_tuple(true, 5))); + EXPECT_EQ(0, action.Perform(make_tuple(false, 1))); +} + +// Tests that Action can be copied. +TEST(ActionTest, IsCopyable) { + Action a1(new MyActionImpl); + Action a2(a1); // Tests the copy constructor. + + // a1 should continue to work after being copied from. + EXPECT_EQ(5, a1.Perform(make_tuple(true, 5))); + EXPECT_EQ(0, a1.Perform(make_tuple(false, 1))); + + // a2 should work like the action it was copied from. + EXPECT_EQ(5, a2.Perform(make_tuple(true, 5))); + EXPECT_EQ(0, a2.Perform(make_tuple(false, 1))); + + a2 = a1; // Tests the assignment operator. + + // a1 should continue to work after being copied from. + EXPECT_EQ(5, a1.Perform(make_tuple(true, 5))); + EXPECT_EQ(0, a1.Perform(make_tuple(false, 1))); + + // a2 should work like the action it was copied from. + EXPECT_EQ(5, a2.Perform(make_tuple(true, 5))); + EXPECT_EQ(0, a2.Perform(make_tuple(false, 1))); +} + +// Tests that an Action object can be converted to a +// compatible Action object. + +class IsNotZero : public ActionInterface { // NOLINT + public: + virtual bool Perform(const tuple& arg) { + return get<0>(arg) != 0; + } +}; + +#if !GTEST_OS_SYMBIAN +// Compiling this test on Nokia's Symbian compiler fails with: +// 'Result' is not a member of class 'testing::internal::Function' +// (point of instantiation: '@unnamed@gmock_actions_test_cc@:: +// ActionTest_CanBeConvertedToOtherActionType_Test::TestBody()') +// with no obvious fix. +TEST(ActionTest, CanBeConvertedToOtherActionType) { + const Action a1(new IsNotZero); // NOLINT + const Action a2 = Action(a1); // NOLINT + EXPECT_EQ(1, a2.Perform(make_tuple('a'))); + EXPECT_EQ(0, a2.Perform(make_tuple('\0'))); +} +#endif // !GTEST_OS_SYMBIAN + +// The following two classes are for testing MakePolymorphicAction(). + +// Implements a polymorphic action that returns the second of the +// arguments it receives. +class ReturnSecondArgumentAction { + public: + // We want to verify that MakePolymorphicAction() can work with a + // polymorphic action whose Perform() method template is either + // const or not. This lets us verify the non-const case. + template + Result Perform(const ArgumentTuple& args) { return get<1>(args); } +}; + +// Implements a polymorphic action that can be used in a nullary +// function to return 0. +class ReturnZeroFromNullaryFunctionAction { + public: + // For testing that MakePolymorphicAction() works when the + // implementation class' Perform() method template takes only one + // template parameter. + // + // We want to verify that MakePolymorphicAction() can work with a + // polymorphic action whose Perform() method template is either + // const or not. This lets us verify the const case. + template + Result Perform(const tuple<>&) const { return 0; } +}; + +// These functions verify that MakePolymorphicAction() returns a +// PolymorphicAction where T is the argument's type. + +PolymorphicAction ReturnSecondArgument() { + return MakePolymorphicAction(ReturnSecondArgumentAction()); +} + +PolymorphicAction +ReturnZeroFromNullaryFunction() { + return MakePolymorphicAction(ReturnZeroFromNullaryFunctionAction()); +} + +// Tests that MakePolymorphicAction() turns a polymorphic action +// implementation class into a polymorphic action. +TEST(MakePolymorphicActionTest, ConstructsActionFromImpl) { + Action a1 = ReturnSecondArgument(); // NOLINT + EXPECT_EQ(5, a1.Perform(make_tuple(false, 5, 2.0))); +} + +// Tests that MakePolymorphicAction() works when the implementation +// class' Perform() method template has only one template parameter. +TEST(MakePolymorphicActionTest, WorksWhenPerformHasOneTemplateParameter) { + Action a1 = ReturnZeroFromNullaryFunction(); + EXPECT_EQ(0, a1.Perform(make_tuple())); + + Action a2 = ReturnZeroFromNullaryFunction(); + EXPECT_TRUE(a2.Perform(make_tuple()) == NULL); +} + +// Tests that Return() works as an action for void-returning +// functions. +TEST(ReturnTest, WorksForVoid) { + const Action ret = Return(); // NOLINT + return ret.Perform(make_tuple(1)); +} + +// Tests that Return(v) returns v. +TEST(ReturnTest, ReturnsGivenValue) { + Action ret = Return(1); // NOLINT + EXPECT_EQ(1, ret.Perform(make_tuple())); + + ret = Return(-5); + EXPECT_EQ(-5, ret.Perform(make_tuple())); +} + +// Tests that Return("string literal") works. +TEST(ReturnTest, AcceptsStringLiteral) { + Action a1 = Return("Hello"); + EXPECT_STREQ("Hello", a1.Perform(make_tuple())); + + Action a2 = Return("world"); + EXPECT_EQ("world", a2.Perform(make_tuple())); +} + +// Tests that Return(v) is covaraint. + +struct Base { + bool operator==(const Base&) { return true; } +}; + +struct Derived : public Base { + bool operator==(const Derived&) { return true; } +}; + +TEST(ReturnTest, IsCovariant) { + Base base; + Derived derived; + Action ret = Return(&base); + EXPECT_EQ(&base, ret.Perform(make_tuple())); + + ret = Return(&derived); + EXPECT_EQ(&derived, ret.Perform(make_tuple())); +} + +// Tests that the type of the value passed into Return is converted into T +// when the action is cast to Action rather than when the action is +// performed. See comments on testing::internal::ReturnAction in +// gmock-actions.h for more information. +class FromType { + public: + FromType(bool* is_converted) : converted_(is_converted) {} + bool* converted() const { return converted_; } + + private: + bool* const converted_; + + GTEST_DISALLOW_ASSIGN_(FromType); +}; + +class ToType { + public: + ToType(const FromType& x) { *x.converted() = true; } +}; + +TEST(ReturnTest, ConvertsArgumentWhenConverted) { + bool converted = false; + FromType x(&converted); + Action action(Return(x)); + EXPECT_TRUE(converted) << "Return must convert its argument in its own " + << "conversion operator."; + converted = false; + action.Perform(tuple<>()); + EXPECT_FALSE(converted) << "Action must NOT convert its argument " + << "when performed." ; +} + +class DestinationType {}; + +class SourceType { + public: + // Note: a non-const typecast operator. + operator DestinationType() { return DestinationType(); } +}; + +TEST(ReturnTest, CanConvertArgumentUsingNonConstTypeCastOperator) { + SourceType s; + Action action(Return(s)); +} + +// Tests that ReturnNull() returns NULL in a pointer-returning function. +TEST(ReturnNullTest, WorksInPointerReturningFunction) { + const Action a1 = ReturnNull(); + EXPECT_TRUE(a1.Perform(make_tuple()) == NULL); + + const Action a2 = ReturnNull(); // NOLINT + EXPECT_TRUE(a2.Perform(make_tuple(true)) == NULL); +} + +// Tests that ReturnRef(v) works for reference types. +TEST(ReturnRefTest, WorksForReference) { + const int n = 0; + const Action ret = ReturnRef(n); // NOLINT + + EXPECT_EQ(&n, &ret.Perform(make_tuple(true))); +} + +// Tests that ReturnRef(v) is covariant. +TEST(ReturnRefTest, IsCovariant) { + Base base; + Derived derived; + Action a = ReturnRef(base); + EXPECT_EQ(&base, &a.Perform(make_tuple())); + + a = ReturnRef(derived); + EXPECT_EQ(&derived, &a.Perform(make_tuple())); +} + +// Tests that ReturnRefOfCopy(v) works for reference types. +TEST(ReturnRefOfCopyTest, WorksForReference) { + int n = 42; + const Action ret = ReturnRefOfCopy(n); + + EXPECT_NE(&n, &ret.Perform(make_tuple())); + EXPECT_EQ(42, ret.Perform(make_tuple())); + + n = 43; + EXPECT_NE(&n, &ret.Perform(make_tuple())); + EXPECT_EQ(42, ret.Perform(make_tuple())); +} + +// Tests that ReturnRefOfCopy(v) is covariant. +TEST(ReturnRefOfCopyTest, IsCovariant) { + Base base; + Derived derived; + Action a = ReturnRefOfCopy(base); + EXPECT_NE(&base, &a.Perform(make_tuple())); + + a = ReturnRefOfCopy(derived); + EXPECT_NE(&derived, &a.Perform(make_tuple())); +} + +// Tests that DoDefault() does the default action for the mock method. + +class MyClass {}; + +class MockClass { + public: + MockClass() {} + + MOCK_METHOD1(IntFunc, int(bool flag)); // NOLINT + MOCK_METHOD0(Foo, MyClass()); + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(MockClass); +}; + +// Tests that DoDefault() returns the built-in default value for the +// return type by default. +TEST(DoDefaultTest, ReturnsBuiltInDefaultValueByDefault) { + MockClass mock; + EXPECT_CALL(mock, IntFunc(_)) + .WillOnce(DoDefault()); + EXPECT_EQ(0, mock.IntFunc(true)); +} + +// Tests that DoDefault() aborts the process when there is no built-in +// default value for the return type. +TEST(DoDefaultDeathTest, DiesForUnknowType) { + MockClass mock; + EXPECT_CALL(mock, Foo()) + .WillRepeatedly(DoDefault()); + EXPECT_DEATH_IF_SUPPORTED({ + mock.Foo(); + }, ""); +} + +// Tests that using DoDefault() inside a composite action leads to a +// run-time error. + +void VoidFunc(bool /* flag */) {} + +TEST(DoDefaultDeathTest, DiesIfUsedInCompositeAction) { + MockClass mock; + EXPECT_CALL(mock, IntFunc(_)) + .WillRepeatedly(DoAll(Invoke(VoidFunc), + DoDefault())); + + // Ideally we should verify the error message as well. Sadly, + // EXPECT_DEATH() can only capture stderr, while Google Mock's + // errors are printed on stdout. Therefore we have to settle for + // not verifying the message. + EXPECT_DEATH_IF_SUPPORTED({ + mock.IntFunc(true); + }, ""); +} + +// Tests that DoDefault() returns the default value set by +// DefaultValue::Set() when it's not overriden by an ON_CALL(). +TEST(DoDefaultTest, ReturnsUserSpecifiedPerTypeDefaultValueWhenThereIsOne) { + DefaultValue::Set(1); + MockClass mock; + EXPECT_CALL(mock, IntFunc(_)) + .WillOnce(DoDefault()); + EXPECT_EQ(1, mock.IntFunc(false)); + DefaultValue::Clear(); +} + +// Tests that DoDefault() does the action specified by ON_CALL(). +TEST(DoDefaultTest, DoesWhatOnCallSpecifies) { + MockClass mock; + ON_CALL(mock, IntFunc(_)) + .WillByDefault(Return(2)); + EXPECT_CALL(mock, IntFunc(_)) + .WillOnce(DoDefault()); + EXPECT_EQ(2, mock.IntFunc(false)); +} + +// Tests that using DoDefault() in ON_CALL() leads to a run-time failure. +TEST(DoDefaultTest, CannotBeUsedInOnCall) { + MockClass mock; + EXPECT_NONFATAL_FAILURE({ // NOLINT + ON_CALL(mock, IntFunc(_)) + .WillByDefault(DoDefault()); + }, "DoDefault() cannot be used in ON_CALL()"); +} + +// Tests that SetArgPointee(v) sets the variable pointed to by +// the N-th (0-based) argument to v. +TEST(SetArgPointeeTest, SetsTheNthPointee) { + typedef void MyFunction(bool, int*, char*); + Action a = SetArgPointee<1>(2); + + int n = 0; + char ch = '\0'; + a.Perform(make_tuple(true, &n, &ch)); + EXPECT_EQ(2, n); + EXPECT_EQ('\0', ch); + + a = SetArgPointee<2>('a'); + n = 0; + ch = '\0'; + a.Perform(make_tuple(true, &n, &ch)); + EXPECT_EQ(0, n); + EXPECT_EQ('a', ch); +} + +#if !((GTEST_GCC_VER_ && GTEST_GCC_VER_ < 40000) || GTEST_OS_SYMBIAN) +// Tests that SetArgPointee() accepts a string literal. +// GCC prior to v4.0 and the Symbian compiler do not support this. +TEST(SetArgPointeeTest, AcceptsStringLiteral) { + typedef void MyFunction(std::string*, const char**); + Action a = SetArgPointee<0>("hi"); + std::string str; + const char* ptr = NULL; + a.Perform(make_tuple(&str, &ptr)); + EXPECT_EQ("hi", str); + EXPECT_TRUE(ptr == NULL); + + a = SetArgPointee<1>("world"); + str = ""; + a.Perform(make_tuple(&str, &ptr)); + EXPECT_EQ("", str); + EXPECT_STREQ("world", ptr); +} + +TEST(SetArgPointeeTest, AcceptsWideStringLiteral) { + typedef void MyFunction(const wchar_t**); + Action a = SetArgPointee<0>(L"world"); + const wchar_t* ptr = NULL; + a.Perform(make_tuple(&ptr)); + EXPECT_STREQ(L"world", ptr); + +# if GTEST_HAS_STD_WSTRING + + typedef void MyStringFunction(std::wstring*); + Action a2 = SetArgPointee<0>(L"world"); + std::wstring str = L""; + a2.Perform(make_tuple(&str)); + EXPECT_EQ(L"world", str); + +# endif +} +#endif + +// Tests that SetArgPointee() accepts a char pointer. +TEST(SetArgPointeeTest, AcceptsCharPointer) { + typedef void MyFunction(bool, std::string*, const char**); + const char* const hi = "hi"; + Action a = SetArgPointee<1>(hi); + std::string str; + const char* ptr = NULL; + a.Perform(make_tuple(true, &str, &ptr)); + EXPECT_EQ("hi", str); + EXPECT_TRUE(ptr == NULL); + + char world_array[] = "world"; + char* const world = world_array; + a = SetArgPointee<2>(world); + str = ""; + a.Perform(make_tuple(true, &str, &ptr)); + EXPECT_EQ("", str); + EXPECT_EQ(world, ptr); +} + +TEST(SetArgPointeeTest, AcceptsWideCharPointer) { + typedef void MyFunction(bool, const wchar_t**); + const wchar_t* const hi = L"hi"; + Action a = SetArgPointee<1>(hi); + const wchar_t* ptr = NULL; + a.Perform(make_tuple(true, &ptr)); + EXPECT_EQ(hi, ptr); + +# if GTEST_HAS_STD_WSTRING + + typedef void MyStringFunction(bool, std::wstring*); + wchar_t world_array[] = L"world"; + wchar_t* const world = world_array; + Action a2 = SetArgPointee<1>(world); + std::wstring str; + a2.Perform(make_tuple(true, &str)); + EXPECT_EQ(world_array, str); +# endif +} + +#if GTEST_HAS_PROTOBUF_ + +// Tests that SetArgPointee(proto_buffer) sets the v1 protobuf +// variable pointed to by the N-th (0-based) argument to proto_buffer. +TEST(SetArgPointeeTest, SetsTheNthPointeeOfProtoBufferType) { + TestMessage* const msg = new TestMessage; + msg->set_member("yes"); + TestMessage orig_msg; + orig_msg.CopyFrom(*msg); + + Action a = SetArgPointee<1>(*msg); + // SetArgPointee(proto_buffer) makes a copy of proto_buffer + // s.t. the action works even when the original proto_buffer has + // died. We ensure this behavior by deleting msg before using the + // action. + delete msg; + + TestMessage dest; + EXPECT_FALSE(orig_msg.Equals(dest)); + a.Perform(make_tuple(true, &dest)); + EXPECT_TRUE(orig_msg.Equals(dest)); +} + +// Tests that SetArgPointee(proto_buffer) sets the +// ::ProtocolMessage variable pointed to by the N-th (0-based) +// argument to proto_buffer. +TEST(SetArgPointeeTest, SetsTheNthPointeeOfProtoBufferBaseType) { + TestMessage* const msg = new TestMessage; + msg->set_member("yes"); + TestMessage orig_msg; + orig_msg.CopyFrom(*msg); + + Action a = SetArgPointee<1>(*msg); + // SetArgPointee(proto_buffer) makes a copy of proto_buffer + // s.t. the action works even when the original proto_buffer has + // died. We ensure this behavior by deleting msg before using the + // action. + delete msg; + + TestMessage dest; + ::ProtocolMessage* const dest_base = &dest; + EXPECT_FALSE(orig_msg.Equals(dest)); + a.Perform(make_tuple(true, dest_base)); + EXPECT_TRUE(orig_msg.Equals(dest)); +} + +// Tests that SetArgPointee(proto2_buffer) sets the v2 +// protobuf variable pointed to by the N-th (0-based) argument to +// proto2_buffer. +TEST(SetArgPointeeTest, SetsTheNthPointeeOfProto2BufferType) { + using testing::internal::FooMessage; + FooMessage* const msg = new FooMessage; + msg->set_int_field(2); + msg->set_string_field("hi"); + FooMessage orig_msg; + orig_msg.CopyFrom(*msg); + + Action a = SetArgPointee<1>(*msg); + // SetArgPointee(proto2_buffer) makes a copy of + // proto2_buffer s.t. the action works even when the original + // proto2_buffer has died. We ensure this behavior by deleting msg + // before using the action. + delete msg; + + FooMessage dest; + dest.set_int_field(0); + a.Perform(make_tuple(true, &dest)); + EXPECT_EQ(2, dest.int_field()); + EXPECT_EQ("hi", dest.string_field()); +} + +// Tests that SetArgPointee(proto2_buffer) sets the +// proto2::Message variable pointed to by the N-th (0-based) argument +// to proto2_buffer. +TEST(SetArgPointeeTest, SetsTheNthPointeeOfProto2BufferBaseType) { + using testing::internal::FooMessage; + FooMessage* const msg = new FooMessage; + msg->set_int_field(2); + msg->set_string_field("hi"); + FooMessage orig_msg; + orig_msg.CopyFrom(*msg); + + Action a = SetArgPointee<1>(*msg); + // SetArgPointee(proto2_buffer) makes a copy of + // proto2_buffer s.t. the action works even when the original + // proto2_buffer has died. We ensure this behavior by deleting msg + // before using the action. + delete msg; + + FooMessage dest; + dest.set_int_field(0); + ::proto2::Message* const dest_base = &dest; + a.Perform(make_tuple(true, dest_base)); + EXPECT_EQ(2, dest.int_field()); + EXPECT_EQ("hi", dest.string_field()); +} + +#endif // GTEST_HAS_PROTOBUF_ + +// Tests that SetArgumentPointee(v) sets the variable pointed to by +// the N-th (0-based) argument to v. +TEST(SetArgumentPointeeTest, SetsTheNthPointee) { + typedef void MyFunction(bool, int*, char*); + Action a = SetArgumentPointee<1>(2); + + int n = 0; + char ch = '\0'; + a.Perform(make_tuple(true, &n, &ch)); + EXPECT_EQ(2, n); + EXPECT_EQ('\0', ch); + + a = SetArgumentPointee<2>('a'); + n = 0; + ch = '\0'; + a.Perform(make_tuple(true, &n, &ch)); + EXPECT_EQ(0, n); + EXPECT_EQ('a', ch); +} + +#if GTEST_HAS_PROTOBUF_ + +// Tests that SetArgumentPointee(proto_buffer) sets the v1 protobuf +// variable pointed to by the N-th (0-based) argument to proto_buffer. +TEST(SetArgumentPointeeTest, SetsTheNthPointeeOfProtoBufferType) { + TestMessage* const msg = new TestMessage; + msg->set_member("yes"); + TestMessage orig_msg; + orig_msg.CopyFrom(*msg); + + Action a = SetArgumentPointee<1>(*msg); + // SetArgumentPointee(proto_buffer) makes a copy of proto_buffer + // s.t. the action works even when the original proto_buffer has + // died. We ensure this behavior by deleting msg before using the + // action. + delete msg; + + TestMessage dest; + EXPECT_FALSE(orig_msg.Equals(dest)); + a.Perform(make_tuple(true, &dest)); + EXPECT_TRUE(orig_msg.Equals(dest)); +} + +// Tests that SetArgumentPointee(proto_buffer) sets the +// ::ProtocolMessage variable pointed to by the N-th (0-based) +// argument to proto_buffer. +TEST(SetArgumentPointeeTest, SetsTheNthPointeeOfProtoBufferBaseType) { + TestMessage* const msg = new TestMessage; + msg->set_member("yes"); + TestMessage orig_msg; + orig_msg.CopyFrom(*msg); + + Action a = SetArgumentPointee<1>(*msg); + // SetArgumentPointee(proto_buffer) makes a copy of proto_buffer + // s.t. the action works even when the original proto_buffer has + // died. We ensure this behavior by deleting msg before using the + // action. + delete msg; + + TestMessage dest; + ::ProtocolMessage* const dest_base = &dest; + EXPECT_FALSE(orig_msg.Equals(dest)); + a.Perform(make_tuple(true, dest_base)); + EXPECT_TRUE(orig_msg.Equals(dest)); +} + +// Tests that SetArgumentPointee(proto2_buffer) sets the v2 +// protobuf variable pointed to by the N-th (0-based) argument to +// proto2_buffer. +TEST(SetArgumentPointeeTest, SetsTheNthPointeeOfProto2BufferType) { + using testing::internal::FooMessage; + FooMessage* const msg = new FooMessage; + msg->set_int_field(2); + msg->set_string_field("hi"); + FooMessage orig_msg; + orig_msg.CopyFrom(*msg); + + Action a = SetArgumentPointee<1>(*msg); + // SetArgumentPointee(proto2_buffer) makes a copy of + // proto2_buffer s.t. the action works even when the original + // proto2_buffer has died. We ensure this behavior by deleting msg + // before using the action. + delete msg; + + FooMessage dest; + dest.set_int_field(0); + a.Perform(make_tuple(true, &dest)); + EXPECT_EQ(2, dest.int_field()); + EXPECT_EQ("hi", dest.string_field()); +} + +// Tests that SetArgumentPointee(proto2_buffer) sets the +// proto2::Message variable pointed to by the N-th (0-based) argument +// to proto2_buffer. +TEST(SetArgumentPointeeTest, SetsTheNthPointeeOfProto2BufferBaseType) { + using testing::internal::FooMessage; + FooMessage* const msg = new FooMessage; + msg->set_int_field(2); + msg->set_string_field("hi"); + FooMessage orig_msg; + orig_msg.CopyFrom(*msg); + + Action a = SetArgumentPointee<1>(*msg); + // SetArgumentPointee(proto2_buffer) makes a copy of + // proto2_buffer s.t. the action works even when the original + // proto2_buffer has died. We ensure this behavior by deleting msg + // before using the action. + delete msg; + + FooMessage dest; + dest.set_int_field(0); + ::proto2::Message* const dest_base = &dest; + a.Perform(make_tuple(true, dest_base)); + EXPECT_EQ(2, dest.int_field()); + EXPECT_EQ("hi", dest.string_field()); +} + +#endif // GTEST_HAS_PROTOBUF_ + +// Sample functions and functors for testing Invoke() and etc. +int Nullary() { return 1; } + +class NullaryFunctor { + public: + int operator()() { return 2; } +}; + +bool g_done = false; +void VoidNullary() { g_done = true; } + +class VoidNullaryFunctor { + public: + void operator()() { g_done = true; } +}; + +bool Unary(int x) { return x < 0; } + +const char* Plus1(const char* s) { return s + 1; } + +void VoidUnary(int /* n */) { g_done = true; } + +bool ByConstRef(const std::string& s) { return s == "Hi"; } + +const double g_double = 0; +bool ReferencesGlobalDouble(const double& x) { return &x == &g_double; } + +std::string ByNonConstRef(std::string& s) { return s += "+"; } // NOLINT + +struct UnaryFunctor { + int operator()(bool x) { return x ? 1 : -1; } +}; + +const char* Binary(const char* input, short n) { return input + n; } // NOLINT + +void VoidBinary(int, char) { g_done = true; } + +int Ternary(int x, char y, short z) { return x + y + z; } // NOLINT + +void VoidTernary(int, char, bool) { g_done = true; } + +int SumOf4(int a, int b, int c, int d) { return a + b + c + d; } + +void VoidFunctionWithFourArguments(char, int, float, double) { g_done = true; } + +int SumOf5(int a, int b, int c, int d, int e) { return a + b + c + d + e; } + +struct SumOf5Functor { + int operator()(int a, int b, int c, int d, int e) { + return a + b + c + d + e; + } +}; + +int SumOf6(int a, int b, int c, int d, int e, int f) { + return a + b + c + d + e + f; +} + +struct SumOf6Functor { + int operator()(int a, int b, int c, int d, int e, int f) { + return a + b + c + d + e + f; + } +}; + +class Foo { + public: + Foo() : value_(123) {} + + int Nullary() const { return value_; } + short Unary(long x) { return static_cast(value_ + x); } // NOLINT + std::string Binary(const std::string& str, char c) const { return str + c; } + int Ternary(int x, bool y, char z) { return value_ + x + y*z; } + int SumOf4(int a, int b, int c, int d) const { + return a + b + c + d + value_; + } + int SumOf5(int a, int b, int c, int d, int e) { return a + b + c + d + e; } + int SumOf6(int a, int b, int c, int d, int e, int f) { + return a + b + c + d + e + f; + } + private: + int value_; +}; + +// Tests InvokeWithoutArgs(function). +TEST(InvokeWithoutArgsTest, Function) { + // As an action that takes one argument. + Action a = InvokeWithoutArgs(Nullary); // NOLINT + EXPECT_EQ(1, a.Perform(make_tuple(2))); + + // As an action that takes two arguments. + Action a2 = InvokeWithoutArgs(Nullary); // NOLINT + EXPECT_EQ(1, a2.Perform(make_tuple(2, 3.5))); + + // As an action that returns void. + Action a3 = InvokeWithoutArgs(VoidNullary); // NOLINT + g_done = false; + a3.Perform(make_tuple(1)); + EXPECT_TRUE(g_done); +} + +// Tests InvokeWithoutArgs(functor). +TEST(InvokeWithoutArgsTest, Functor) { + // As an action that takes no argument. + Action a = InvokeWithoutArgs(NullaryFunctor()); // NOLINT + EXPECT_EQ(2, a.Perform(make_tuple())); + + // As an action that takes three arguments. + Action a2 = // NOLINT + InvokeWithoutArgs(NullaryFunctor()); + EXPECT_EQ(2, a2.Perform(make_tuple(3, 3.5, 'a'))); + + // As an action that returns void. + Action a3 = InvokeWithoutArgs(VoidNullaryFunctor()); + g_done = false; + a3.Perform(make_tuple()); + EXPECT_TRUE(g_done); +} + +// Tests InvokeWithoutArgs(obj_ptr, method). +TEST(InvokeWithoutArgsTest, Method) { + Foo foo; + Action a = // NOLINT + InvokeWithoutArgs(&foo, &Foo::Nullary); + EXPECT_EQ(123, a.Perform(make_tuple(true, 'a'))); +} + +// Tests using IgnoreResult() on a polymorphic action. +TEST(IgnoreResultTest, PolymorphicAction) { + Action a = IgnoreResult(Return(5)); // NOLINT + a.Perform(make_tuple(1)); +} + +// Tests using IgnoreResult() on a monomorphic action. + +int ReturnOne() { + g_done = true; + return 1; +} + +TEST(IgnoreResultTest, MonomorphicAction) { + g_done = false; + Action a = IgnoreResult(Invoke(ReturnOne)); + a.Perform(make_tuple()); + EXPECT_TRUE(g_done); +} + +// Tests using IgnoreResult() on an action that returns a class type. + +MyClass ReturnMyClass(double /* x */) { + g_done = true; + return MyClass(); +} + +TEST(IgnoreResultTest, ActionReturningClass) { + g_done = false; + Action a = IgnoreResult(Invoke(ReturnMyClass)); // NOLINT + a.Perform(make_tuple(2)); + EXPECT_TRUE(g_done); +} + +TEST(AssignTest, Int) { + int x = 0; + Action a = Assign(&x, 5); + a.Perform(make_tuple(0)); + EXPECT_EQ(5, x); +} + +TEST(AssignTest, String) { + ::std::string x; + Action a = Assign(&x, "Hello, world"); + a.Perform(make_tuple()); + EXPECT_EQ("Hello, world", x); +} + +TEST(AssignTest, CompatibleTypes) { + double x = 0; + Action a = Assign(&x, 5); + a.Perform(make_tuple(0)); + EXPECT_DOUBLE_EQ(5, x); +} + +#if !GTEST_OS_WINDOWS_MOBILE + +class SetErrnoAndReturnTest : public testing::Test { + protected: + virtual void SetUp() { errno = 0; } + virtual void TearDown() { errno = 0; } +}; + +TEST_F(SetErrnoAndReturnTest, Int) { + Action a = SetErrnoAndReturn(ENOTTY, -5); + EXPECT_EQ(-5, a.Perform(make_tuple())); + EXPECT_EQ(ENOTTY, errno); +} + +TEST_F(SetErrnoAndReturnTest, Ptr) { + int x; + Action a = SetErrnoAndReturn(ENOTTY, &x); + EXPECT_EQ(&x, a.Perform(make_tuple())); + EXPECT_EQ(ENOTTY, errno); +} + +TEST_F(SetErrnoAndReturnTest, CompatibleTypes) { + Action a = SetErrnoAndReturn(EINVAL, 5); + EXPECT_DOUBLE_EQ(5.0, a.Perform(make_tuple())); + EXPECT_EQ(EINVAL, errno); +} + +#endif // !GTEST_OS_WINDOWS_MOBILE + +// Tests ByRef(). + +// Tests that ReferenceWrapper is copyable. +TEST(ByRefTest, IsCopyable) { + const std::string s1 = "Hi"; + const std::string s2 = "Hello"; + + ::testing::internal::ReferenceWrapper ref_wrapper = ByRef(s1); + const std::string& r1 = ref_wrapper; + EXPECT_EQ(&s1, &r1); + + // Assigns a new value to ref_wrapper. + ref_wrapper = ByRef(s2); + const std::string& r2 = ref_wrapper; + EXPECT_EQ(&s2, &r2); + + ::testing::internal::ReferenceWrapper ref_wrapper1 = ByRef(s1); + // Copies ref_wrapper1 to ref_wrapper. + ref_wrapper = ref_wrapper1; + const std::string& r3 = ref_wrapper; + EXPECT_EQ(&s1, &r3); +} + +// Tests using ByRef() on a const value. +TEST(ByRefTest, ConstValue) { + const int n = 0; + // int& ref = ByRef(n); // This shouldn't compile - we have a + // negative compilation test to catch it. + const int& const_ref = ByRef(n); + EXPECT_EQ(&n, &const_ref); +} + +// Tests using ByRef() on a non-const value. +TEST(ByRefTest, NonConstValue) { + int n = 0; + + // ByRef(n) can be used as either an int&, + int& ref = ByRef(n); + EXPECT_EQ(&n, &ref); + + // or a const int&. + const int& const_ref = ByRef(n); + EXPECT_EQ(&n, &const_ref); +} + +// Tests explicitly specifying the type when using ByRef(). +TEST(ByRefTest, ExplicitType) { + int n = 0; + const int& r1 = ByRef(n); + EXPECT_EQ(&n, &r1); + + // ByRef(n); // This shouldn't compile - we have a negative + // compilation test to catch it. + + Derived d; + Derived& r2 = ByRef(d); + EXPECT_EQ(&d, &r2); + + const Derived& r3 = ByRef(d); + EXPECT_EQ(&d, &r3); + + Base& r4 = ByRef(d); + EXPECT_EQ(&d, &r4); + + const Base& r5 = ByRef(d); + EXPECT_EQ(&d, &r5); + + // The following shouldn't compile - we have a negative compilation + // test for it. + // + // Base b; + // ByRef(b); +} + +// Tests that Google Mock prints expression ByRef(x) as a reference to x. +TEST(ByRefTest, PrintsCorrectly) { + int n = 42; + ::std::stringstream expected, actual; + testing::internal::UniversalPrinter::Print(n, &expected); + testing::internal::UniversalPrint(ByRef(n), &actual); + EXPECT_EQ(expected.str(), actual.str()); +} + +} // Unnamed namespace diff --git a/libwvdrmengine/test/gmock/test/gmock-cardinalities_test.cc b/libwvdrmengine/test/gmock/test/gmock-cardinalities_test.cc new file mode 100644 index 00000000..64815e57 --- /dev/null +++ b/libwvdrmengine/test/gmock/test/gmock-cardinalities_test.cc @@ -0,0 +1,428 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file tests the built-in cardinalities. + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "gtest/gtest-spi.h" + +namespace { + +using std::stringstream; +using testing::AnyNumber; +using testing::AtLeast; +using testing::AtMost; +using testing::Between; +using testing::Cardinality; +using testing::CardinalityInterface; +using testing::Exactly; +using testing::IsSubstring; +using testing::MakeCardinality; + +class MockFoo { + public: + MockFoo() {} + MOCK_METHOD0(Bar, int()); // NOLINT + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFoo); +}; + +// Tests that Cardinality objects can be default constructed. +TEST(CardinalityTest, IsDefaultConstructable) { + Cardinality c; +} + +// Tests that Cardinality objects are copyable. +TEST(CardinalityTest, IsCopyable) { + // Tests the copy constructor. + Cardinality c = Exactly(1); + EXPECT_FALSE(c.IsSatisfiedByCallCount(0)); + EXPECT_TRUE(c.IsSatisfiedByCallCount(1)); + EXPECT_TRUE(c.IsSaturatedByCallCount(1)); + + // Tests the assignment operator. + c = Exactly(2); + EXPECT_FALSE(c.IsSatisfiedByCallCount(1)); + EXPECT_TRUE(c.IsSatisfiedByCallCount(2)); + EXPECT_TRUE(c.IsSaturatedByCallCount(2)); +} + +TEST(CardinalityTest, IsOverSaturatedByCallCountWorks) { + const Cardinality c = AtMost(5); + EXPECT_FALSE(c.IsOverSaturatedByCallCount(4)); + EXPECT_FALSE(c.IsOverSaturatedByCallCount(5)); + EXPECT_TRUE(c.IsOverSaturatedByCallCount(6)); +} + +// Tests that Cardinality::DescribeActualCallCountTo() creates the +// correct description. +TEST(CardinalityTest, CanDescribeActualCallCount) { + stringstream ss0; + Cardinality::DescribeActualCallCountTo(0, &ss0); + EXPECT_EQ("never called", ss0.str()); + + stringstream ss1; + Cardinality::DescribeActualCallCountTo(1, &ss1); + EXPECT_EQ("called once", ss1.str()); + + stringstream ss2; + Cardinality::DescribeActualCallCountTo(2, &ss2); + EXPECT_EQ("called twice", ss2.str()); + + stringstream ss3; + Cardinality::DescribeActualCallCountTo(3, &ss3); + EXPECT_EQ("called 3 times", ss3.str()); +} + +// Tests AnyNumber() +TEST(AnyNumber, Works) { + const Cardinality c = AnyNumber(); + EXPECT_TRUE(c.IsSatisfiedByCallCount(0)); + EXPECT_FALSE(c.IsSaturatedByCallCount(0)); + + EXPECT_TRUE(c.IsSatisfiedByCallCount(1)); + EXPECT_FALSE(c.IsSaturatedByCallCount(1)); + + EXPECT_TRUE(c.IsSatisfiedByCallCount(9)); + EXPECT_FALSE(c.IsSaturatedByCallCount(9)); + + stringstream ss; + c.DescribeTo(&ss); + EXPECT_PRED_FORMAT2(IsSubstring, "called any number of times", + ss.str()); +} + +TEST(AnyNumberTest, HasCorrectBounds) { + const Cardinality c = AnyNumber(); + EXPECT_EQ(0, c.ConservativeLowerBound()); + EXPECT_EQ(INT_MAX, c.ConservativeUpperBound()); +} + +// Tests AtLeast(n). + +TEST(AtLeastTest, OnNegativeNumber) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + AtLeast(-1); + }, "The invocation lower bound must be >= 0"); +} + +TEST(AtLeastTest, OnZero) { + const Cardinality c = AtLeast(0); + EXPECT_TRUE(c.IsSatisfiedByCallCount(0)); + EXPECT_FALSE(c.IsSaturatedByCallCount(0)); + + EXPECT_TRUE(c.IsSatisfiedByCallCount(1)); + EXPECT_FALSE(c.IsSaturatedByCallCount(1)); + + stringstream ss; + c.DescribeTo(&ss); + EXPECT_PRED_FORMAT2(IsSubstring, "any number of times", + ss.str()); +} + +TEST(AtLeastTest, OnPositiveNumber) { + const Cardinality c = AtLeast(2); + EXPECT_FALSE(c.IsSatisfiedByCallCount(0)); + EXPECT_FALSE(c.IsSaturatedByCallCount(0)); + + EXPECT_FALSE(c.IsSatisfiedByCallCount(1)); + EXPECT_FALSE(c.IsSaturatedByCallCount(1)); + + EXPECT_TRUE(c.IsSatisfiedByCallCount(2)); + EXPECT_FALSE(c.IsSaturatedByCallCount(2)); + + stringstream ss1; + AtLeast(1).DescribeTo(&ss1); + EXPECT_PRED_FORMAT2(IsSubstring, "at least once", + ss1.str()); + + stringstream ss2; + c.DescribeTo(&ss2); + EXPECT_PRED_FORMAT2(IsSubstring, "at least twice", + ss2.str()); + + stringstream ss3; + AtLeast(3).DescribeTo(&ss3); + EXPECT_PRED_FORMAT2(IsSubstring, "at least 3 times", + ss3.str()); +} + +TEST(AtLeastTest, HasCorrectBounds) { + const Cardinality c = AtLeast(2); + EXPECT_EQ(2, c.ConservativeLowerBound()); + EXPECT_EQ(INT_MAX, c.ConservativeUpperBound()); +} + +// Tests AtMost(n). + +TEST(AtMostTest, OnNegativeNumber) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + AtMost(-1); + }, "The invocation upper bound must be >= 0"); +} + +TEST(AtMostTest, OnZero) { + const Cardinality c = AtMost(0); + EXPECT_TRUE(c.IsSatisfiedByCallCount(0)); + EXPECT_TRUE(c.IsSaturatedByCallCount(0)); + + EXPECT_FALSE(c.IsSatisfiedByCallCount(1)); + EXPECT_TRUE(c.IsSaturatedByCallCount(1)); + + stringstream ss; + c.DescribeTo(&ss); + EXPECT_PRED_FORMAT2(IsSubstring, "never called", + ss.str()); +} + +TEST(AtMostTest, OnPositiveNumber) { + const Cardinality c = AtMost(2); + EXPECT_TRUE(c.IsSatisfiedByCallCount(0)); + EXPECT_FALSE(c.IsSaturatedByCallCount(0)); + + EXPECT_TRUE(c.IsSatisfiedByCallCount(1)); + EXPECT_FALSE(c.IsSaturatedByCallCount(1)); + + EXPECT_TRUE(c.IsSatisfiedByCallCount(2)); + EXPECT_TRUE(c.IsSaturatedByCallCount(2)); + + stringstream ss1; + AtMost(1).DescribeTo(&ss1); + EXPECT_PRED_FORMAT2(IsSubstring, "called at most once", + ss1.str()); + + stringstream ss2; + c.DescribeTo(&ss2); + EXPECT_PRED_FORMAT2(IsSubstring, "called at most twice", + ss2.str()); + + stringstream ss3; + AtMost(3).DescribeTo(&ss3); + EXPECT_PRED_FORMAT2(IsSubstring, "called at most 3 times", + ss3.str()); +} + +TEST(AtMostTest, HasCorrectBounds) { + const Cardinality c = AtMost(2); + EXPECT_EQ(0, c.ConservativeLowerBound()); + EXPECT_EQ(2, c.ConservativeUpperBound()); +} + +// Tests Between(m, n). + +TEST(BetweenTest, OnNegativeStart) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + Between(-1, 2); + }, "The invocation lower bound must be >= 0, but is actually -1"); +} + +TEST(BetweenTest, OnNegativeEnd) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + Between(1, -2); + }, "The invocation upper bound must be >= 0, but is actually -2"); +} + +TEST(BetweenTest, OnStartBiggerThanEnd) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + Between(2, 1); + }, "The invocation upper bound (1) must be >= " + "the invocation lower bound (2)"); +} + +TEST(BetweenTest, OnZeroStartAndZeroEnd) { + const Cardinality c = Between(0, 0); + + EXPECT_TRUE(c.IsSatisfiedByCallCount(0)); + EXPECT_TRUE(c.IsSaturatedByCallCount(0)); + + EXPECT_FALSE(c.IsSatisfiedByCallCount(1)); + EXPECT_TRUE(c.IsSaturatedByCallCount(1)); + + stringstream ss; + c.DescribeTo(&ss); + EXPECT_PRED_FORMAT2(IsSubstring, "never called", + ss.str()); +} + +TEST(BetweenTest, OnZeroStartAndNonZeroEnd) { + const Cardinality c = Between(0, 2); + + EXPECT_TRUE(c.IsSatisfiedByCallCount(0)); + EXPECT_FALSE(c.IsSaturatedByCallCount(0)); + + EXPECT_TRUE(c.IsSatisfiedByCallCount(2)); + EXPECT_TRUE(c.IsSaturatedByCallCount(2)); + + EXPECT_FALSE(c.IsSatisfiedByCallCount(4)); + EXPECT_TRUE(c.IsSaturatedByCallCount(4)); + + stringstream ss; + c.DescribeTo(&ss); + EXPECT_PRED_FORMAT2(IsSubstring, "called at most twice", + ss.str()); +} + +TEST(BetweenTest, OnSameStartAndEnd) { + const Cardinality c = Between(3, 3); + + EXPECT_FALSE(c.IsSatisfiedByCallCount(2)); + EXPECT_FALSE(c.IsSaturatedByCallCount(2)); + + EXPECT_TRUE(c.IsSatisfiedByCallCount(3)); + EXPECT_TRUE(c.IsSaturatedByCallCount(3)); + + EXPECT_FALSE(c.IsSatisfiedByCallCount(4)); + EXPECT_TRUE(c.IsSaturatedByCallCount(4)); + + stringstream ss; + c.DescribeTo(&ss); + EXPECT_PRED_FORMAT2(IsSubstring, "called 3 times", + ss.str()); +} + +TEST(BetweenTest, OnDifferentStartAndEnd) { + const Cardinality c = Between(3, 5); + + EXPECT_FALSE(c.IsSatisfiedByCallCount(2)); + EXPECT_FALSE(c.IsSaturatedByCallCount(2)); + + EXPECT_TRUE(c.IsSatisfiedByCallCount(3)); + EXPECT_FALSE(c.IsSaturatedByCallCount(3)); + + EXPECT_TRUE(c.IsSatisfiedByCallCount(5)); + EXPECT_TRUE(c.IsSaturatedByCallCount(5)); + + EXPECT_FALSE(c.IsSatisfiedByCallCount(6)); + EXPECT_TRUE(c.IsSaturatedByCallCount(6)); + + stringstream ss; + c.DescribeTo(&ss); + EXPECT_PRED_FORMAT2(IsSubstring, "called between 3 and 5 times", + ss.str()); +} + +TEST(BetweenTest, HasCorrectBounds) { + const Cardinality c = Between(3, 5); + EXPECT_EQ(3, c.ConservativeLowerBound()); + EXPECT_EQ(5, c.ConservativeUpperBound()); +} + +// Tests Exactly(n). + +TEST(ExactlyTest, OnNegativeNumber) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + Exactly(-1); + }, "The invocation lower bound must be >= 0"); +} + +TEST(ExactlyTest, OnZero) { + const Cardinality c = Exactly(0); + EXPECT_TRUE(c.IsSatisfiedByCallCount(0)); + EXPECT_TRUE(c.IsSaturatedByCallCount(0)); + + EXPECT_FALSE(c.IsSatisfiedByCallCount(1)); + EXPECT_TRUE(c.IsSaturatedByCallCount(1)); + + stringstream ss; + c.DescribeTo(&ss); + EXPECT_PRED_FORMAT2(IsSubstring, "never called", + ss.str()); +} + +TEST(ExactlyTest, OnPositiveNumber) { + const Cardinality c = Exactly(2); + EXPECT_FALSE(c.IsSatisfiedByCallCount(0)); + EXPECT_FALSE(c.IsSaturatedByCallCount(0)); + + EXPECT_TRUE(c.IsSatisfiedByCallCount(2)); + EXPECT_TRUE(c.IsSaturatedByCallCount(2)); + + stringstream ss1; + Exactly(1).DescribeTo(&ss1); + EXPECT_PRED_FORMAT2(IsSubstring, "called once", + ss1.str()); + + stringstream ss2; + c.DescribeTo(&ss2); + EXPECT_PRED_FORMAT2(IsSubstring, "called twice", + ss2.str()); + + stringstream ss3; + Exactly(3).DescribeTo(&ss3); + EXPECT_PRED_FORMAT2(IsSubstring, "called 3 times", + ss3.str()); +} + +TEST(ExactlyTest, HasCorrectBounds) { + const Cardinality c = Exactly(3); + EXPECT_EQ(3, c.ConservativeLowerBound()); + EXPECT_EQ(3, c.ConservativeUpperBound()); +} + +// Tests that a user can make his own cardinality by implementing +// CardinalityInterface and calling MakeCardinality(). + +class EvenCardinality : public CardinalityInterface { + public: + // Returns true iff call_count calls will satisfy this cardinality. + virtual bool IsSatisfiedByCallCount(int call_count) const { + return (call_count % 2 == 0); + } + + // Returns true iff call_count calls will saturate this cardinality. + virtual bool IsSaturatedByCallCount(int /* call_count */) const { + return false; + } + + // Describes self to an ostream. + virtual void DescribeTo(::std::ostream* ss) const { + *ss << "called even number of times"; + } +}; + +TEST(MakeCardinalityTest, ConstructsCardinalityFromInterface) { + const Cardinality c = MakeCardinality(new EvenCardinality); + + EXPECT_TRUE(c.IsSatisfiedByCallCount(2)); + EXPECT_FALSE(c.IsSatisfiedByCallCount(3)); + + EXPECT_FALSE(c.IsSaturatedByCallCount(10000)); + + stringstream ss; + c.DescribeTo(&ss); + EXPECT_EQ("called even number of times", ss.str()); +} + +} // Unnamed namespace diff --git a/libwvdrmengine/test/gmock/test/gmock-generated-actions_test.cc b/libwvdrmengine/test/gmock/test/gmock-generated-actions_test.cc new file mode 100644 index 00000000..436f1a2e --- /dev/null +++ b/libwvdrmengine/test/gmock/test/gmock-generated-actions_test.cc @@ -0,0 +1,1212 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file tests the built-in actions generated by a script. + +#include "gmock/gmock-generated-actions.h" + +#include +#include +#include +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace testing { +namespace gmock_generated_actions_test { + +using ::std::plus; +using ::std::string; +using ::std::tr1::get; +using ::std::tr1::make_tuple; +using ::std::tr1::tuple; +using ::std::tr1::tuple_element; +using testing::_; +using testing::Action; +using testing::ActionInterface; +using testing::ByRef; +using testing::DoAll; +using testing::Invoke; +using testing::Return; +using testing::ReturnNew; +using testing::SetArgPointee; +using testing::StaticAssertTypeEq; +using testing::Unused; +using testing::WithArgs; + +// For suppressing compiler warnings on conversion possibly losing precision. +inline short Short(short n) { return n; } // NOLINT +inline char Char(char ch) { return ch; } + +// Sample functions and functors for testing various actions. +int Nullary() { return 1; } + +class NullaryFunctor { + public: + int operator()() { return 2; } +}; + +bool g_done = false; + +bool Unary(int x) { return x < 0; } + +const char* Plus1(const char* s) { return s + 1; } + +bool ByConstRef(const string& s) { return s == "Hi"; } + +const double g_double = 0; +bool ReferencesGlobalDouble(const double& x) { return &x == &g_double; } + +string ByNonConstRef(string& s) { return s += "+"; } // NOLINT + +struct UnaryFunctor { + int operator()(bool x) { return x ? 1 : -1; } +}; + +const char* Binary(const char* input, short n) { return input + n; } // NOLINT + +void VoidBinary(int, char) { g_done = true; } + +int Ternary(int x, char y, short z) { return x + y + z; } // NOLINT + +void VoidTernary(int, char, bool) { g_done = true; } + +int SumOf4(int a, int b, int c, int d) { return a + b + c + d; } + +string Concat4(const char* s1, const char* s2, const char* s3, + const char* s4) { + return string(s1) + s2 + s3 + s4; +} + +int SumOf5(int a, int b, int c, int d, int e) { return a + b + c + d + e; } + +struct SumOf5Functor { + int operator()(int a, int b, int c, int d, int e) { + return a + b + c + d + e; + } +}; + +string Concat5(const char* s1, const char* s2, const char* s3, + const char* s4, const char* s5) { + return string(s1) + s2 + s3 + s4 + s5; +} + +int SumOf6(int a, int b, int c, int d, int e, int f) { + return a + b + c + d + e + f; +} + +struct SumOf6Functor { + int operator()(int a, int b, int c, int d, int e, int f) { + return a + b + c + d + e + f; + } +}; + +string Concat6(const char* s1, const char* s2, const char* s3, + const char* s4, const char* s5, const char* s6) { + return string(s1) + s2 + s3 + s4 + s5 + s6; +} + +string Concat7(const char* s1, const char* s2, const char* s3, + const char* s4, const char* s5, const char* s6, + const char* s7) { + return string(s1) + s2 + s3 + s4 + s5 + s6 + s7; +} + +string Concat8(const char* s1, const char* s2, const char* s3, + const char* s4, const char* s5, const char* s6, + const char* s7, const char* s8) { + return string(s1) + s2 + s3 + s4 + s5 + s6 + s7 + s8; +} + +string Concat9(const char* s1, const char* s2, const char* s3, + const char* s4, const char* s5, const char* s6, + const char* s7, const char* s8, const char* s9) { + return string(s1) + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9; +} + +string Concat10(const char* s1, const char* s2, const char* s3, + const char* s4, const char* s5, const char* s6, + const char* s7, const char* s8, const char* s9, + const char* s10) { + return string(s1) + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9 + s10; +} + +// A helper that turns the type of a C-string literal from const +// char[N] to const char*. +inline const char* CharPtr(const char* s) { return s; } + +// Tests InvokeArgument(...). + +// Tests using InvokeArgument with a nullary function. +TEST(InvokeArgumentTest, Function0) { + Action a = InvokeArgument<1>(); // NOLINT + EXPECT_EQ(1, a.Perform(make_tuple(2, &Nullary))); +} + +// Tests using InvokeArgument with a unary function. +TEST(InvokeArgumentTest, Functor1) { + Action a = InvokeArgument<0>(true); // NOLINT + EXPECT_EQ(1, a.Perform(make_tuple(UnaryFunctor()))); +} + +// Tests using InvokeArgument with a 5-ary function. +TEST(InvokeArgumentTest, Function5) { + Action a = // NOLINT + InvokeArgument<0>(10000, 2000, 300, 40, 5); + EXPECT_EQ(12345, a.Perform(make_tuple(&SumOf5))); +} + +// Tests using InvokeArgument with a 5-ary functor. +TEST(InvokeArgumentTest, Functor5) { + Action a = // NOLINT + InvokeArgument<0>(10000, 2000, 300, 40, 5); + EXPECT_EQ(12345, a.Perform(make_tuple(SumOf5Functor()))); +} + +// Tests using InvokeArgument with a 6-ary function. +TEST(InvokeArgumentTest, Function6) { + Action a = // NOLINT + InvokeArgument<0>(100000, 20000, 3000, 400, 50, 6); + EXPECT_EQ(123456, a.Perform(make_tuple(&SumOf6))); +} + +// Tests using InvokeArgument with a 6-ary functor. +TEST(InvokeArgumentTest, Functor6) { + Action a = // NOLINT + InvokeArgument<0>(100000, 20000, 3000, 400, 50, 6); + EXPECT_EQ(123456, a.Perform(make_tuple(SumOf6Functor()))); +} + +// Tests using InvokeArgument with a 7-ary function. +TEST(InvokeArgumentTest, Function7) { + Action a = + InvokeArgument<0>("1", "2", "3", "4", "5", "6", "7"); + EXPECT_EQ("1234567", a.Perform(make_tuple(&Concat7))); +} + +// Tests using InvokeArgument with a 8-ary function. +TEST(InvokeArgumentTest, Function8) { + Action a = + InvokeArgument<0>("1", "2", "3", "4", "5", "6", "7", "8"); + EXPECT_EQ("12345678", a.Perform(make_tuple(&Concat8))); +} + +// Tests using InvokeArgument with a 9-ary function. +TEST(InvokeArgumentTest, Function9) { + Action a = + InvokeArgument<0>("1", "2", "3", "4", "5", "6", "7", "8", "9"); + EXPECT_EQ("123456789", a.Perform(make_tuple(&Concat9))); +} + +// Tests using InvokeArgument with a 10-ary function. +TEST(InvokeArgumentTest, Function10) { + Action a = + InvokeArgument<0>("1", "2", "3", "4", "5", "6", "7", "8", "9", "0"); + EXPECT_EQ("1234567890", a.Perform(make_tuple(&Concat10))); +} + +// Tests using InvokeArgument with a function that takes a pointer argument. +TEST(InvokeArgumentTest, ByPointerFunction) { + Action a = // NOLINT + InvokeArgument<0>(static_cast("Hi"), Short(1)); + EXPECT_STREQ("i", a.Perform(make_tuple(&Binary))); +} + +// Tests using InvokeArgument with a function that takes a const char* +// by passing it a C-string literal. +TEST(InvokeArgumentTest, FunctionWithCStringLiteral) { + Action a = // NOLINT + InvokeArgument<0>("Hi", Short(1)); + EXPECT_STREQ("i", a.Perform(make_tuple(&Binary))); +} + +// Tests using InvokeArgument with a function that takes a const reference. +TEST(InvokeArgumentTest, ByConstReferenceFunction) { + Action a = // NOLINT + InvokeArgument<0>(string("Hi")); + // When action 'a' is constructed, it makes a copy of the temporary + // string object passed to it, so it's OK to use 'a' later, when the + // temporary object has already died. + EXPECT_TRUE(a.Perform(make_tuple(&ByConstRef))); +} + +// Tests using InvokeArgument with ByRef() and a function that takes a +// const reference. +TEST(InvokeArgumentTest, ByExplicitConstReferenceFunction) { + Action a = // NOLINT + InvokeArgument<0>(ByRef(g_double)); + // The above line calls ByRef() on a const value. + EXPECT_TRUE(a.Perform(make_tuple(&ReferencesGlobalDouble))); + + double x = 0; + a = InvokeArgument<0>(ByRef(x)); // This calls ByRef() on a non-const. + EXPECT_FALSE(a.Perform(make_tuple(&ReferencesGlobalDouble))); +} + +// Tests using WithArgs and with an action that takes 1 argument. +TEST(WithArgsTest, OneArg) { + Action a = WithArgs<1>(Invoke(Unary)); // NOLINT + EXPECT_TRUE(a.Perform(make_tuple(1.5, -1))); + EXPECT_FALSE(a.Perform(make_tuple(1.5, 1))); +} + +// Tests using WithArgs with an action that takes 2 arguments. +TEST(WithArgsTest, TwoArgs) { + Action a = + WithArgs<0, 2>(Invoke(Binary)); + const char s[] = "Hello"; + EXPECT_EQ(s + 2, a.Perform(make_tuple(CharPtr(s), 0.5, Short(2)))); +} + +// Tests using WithArgs with an action that takes 3 arguments. +TEST(WithArgsTest, ThreeArgs) { + Action a = // NOLINT + WithArgs<0, 2, 3>(Invoke(Ternary)); + EXPECT_EQ(123, a.Perform(make_tuple(100, 6.5, Char(20), Short(3)))); +} + +// Tests using WithArgs with an action that takes 4 arguments. +TEST(WithArgsTest, FourArgs) { + Action a = + WithArgs<4, 3, 1, 0>(Invoke(Concat4)); + EXPECT_EQ("4310", a.Perform(make_tuple(CharPtr("0"), CharPtr("1"), 2.5, + CharPtr("3"), CharPtr("4")))); +} + +// Tests using WithArgs with an action that takes 5 arguments. +TEST(WithArgsTest, FiveArgs) { + Action a = + WithArgs<4, 3, 2, 1, 0>(Invoke(Concat5)); + EXPECT_EQ("43210", + a.Perform(make_tuple(CharPtr("0"), CharPtr("1"), CharPtr("2"), + CharPtr("3"), CharPtr("4")))); +} + +// Tests using WithArgs with an action that takes 6 arguments. +TEST(WithArgsTest, SixArgs) { + Action a = + WithArgs<0, 1, 2, 2, 1, 0>(Invoke(Concat6)); + EXPECT_EQ("012210", + a.Perform(make_tuple(CharPtr("0"), CharPtr("1"), CharPtr("2")))); +} + +// Tests using WithArgs with an action that takes 7 arguments. +TEST(WithArgsTest, SevenArgs) { + Action a = + WithArgs<0, 1, 2, 3, 2, 1, 0>(Invoke(Concat7)); + EXPECT_EQ("0123210", + a.Perform(make_tuple(CharPtr("0"), CharPtr("1"), CharPtr("2"), + CharPtr("3")))); +} + +// Tests using WithArgs with an action that takes 8 arguments. +TEST(WithArgsTest, EightArgs) { + Action a = + WithArgs<0, 1, 2, 3, 0, 1, 2, 3>(Invoke(Concat8)); + EXPECT_EQ("01230123", + a.Perform(make_tuple(CharPtr("0"), CharPtr("1"), CharPtr("2"), + CharPtr("3")))); +} + +// Tests using WithArgs with an action that takes 9 arguments. +TEST(WithArgsTest, NineArgs) { + Action a = + WithArgs<0, 1, 2, 3, 1, 2, 3, 2, 3>(Invoke(Concat9)); + EXPECT_EQ("012312323", + a.Perform(make_tuple(CharPtr("0"), CharPtr("1"), CharPtr("2"), + CharPtr("3")))); +} + +// Tests using WithArgs with an action that takes 10 arguments. +TEST(WithArgsTest, TenArgs) { + Action a = + WithArgs<0, 1, 2, 3, 2, 1, 0, 1, 2, 3>(Invoke(Concat10)); + EXPECT_EQ("0123210123", + a.Perform(make_tuple(CharPtr("0"), CharPtr("1"), CharPtr("2"), + CharPtr("3")))); +} + +// Tests using WithArgs with an action that is not Invoke(). +class SubstractAction : public ActionInterface { // NOLINT + public: + virtual int Perform(const tuple& args) { + return get<0>(args) - get<1>(args); + } +}; + +TEST(WithArgsTest, NonInvokeAction) { + Action a = // NOLINT + WithArgs<2, 1>(MakeAction(new SubstractAction)); + EXPECT_EQ(8, a.Perform(make_tuple(CharPtr("hi"), 2, 10))); +} + +// Tests using WithArgs to pass all original arguments in the original order. +TEST(WithArgsTest, Identity) { + Action a = // NOLINT + WithArgs<0, 1, 2>(Invoke(Ternary)); + EXPECT_EQ(123, a.Perform(make_tuple(100, Char(20), Short(3)))); +} + +// Tests using WithArgs with repeated arguments. +TEST(WithArgsTest, RepeatedArguments) { + Action a = // NOLINT + WithArgs<1, 1, 1, 1>(Invoke(SumOf4)); + EXPECT_EQ(4, a.Perform(make_tuple(false, 1, 10))); +} + +// Tests using WithArgs with reversed argument order. +TEST(WithArgsTest, ReversedArgumentOrder) { + Action a = // NOLINT + WithArgs<1, 0>(Invoke(Binary)); + const char s[] = "Hello"; + EXPECT_EQ(s + 2, a.Perform(make_tuple(Short(2), CharPtr(s)))); +} + +// Tests using WithArgs with compatible, but not identical, argument types. +TEST(WithArgsTest, ArgsOfCompatibleTypes) { + Action a = // NOLINT + WithArgs<0, 1, 3>(Invoke(Ternary)); + EXPECT_EQ(123, a.Perform(make_tuple(Short(100), Char(20), 5.6, Char(3)))); +} + +// Tests using WithArgs with an action that returns void. +TEST(WithArgsTest, VoidAction) { + Action a = WithArgs<2, 1>(Invoke(VoidBinary)); + g_done = false; + a.Perform(make_tuple(1.5, 'a', 3)); + EXPECT_TRUE(g_done); +} + +// Tests DoAll(a1, a2). +TEST(DoAllTest, TwoActions) { + int n = 0; + Action a = DoAll(SetArgPointee<0>(1), // NOLINT + Return(2)); + EXPECT_EQ(2, a.Perform(make_tuple(&n))); + EXPECT_EQ(1, n); +} + +// Tests DoAll(a1, a2, a3). +TEST(DoAllTest, ThreeActions) { + int m = 0, n = 0; + Action a = DoAll(SetArgPointee<0>(1), // NOLINT + SetArgPointee<1>(2), + Return(3)); + EXPECT_EQ(3, a.Perform(make_tuple(&m, &n))); + EXPECT_EQ(1, m); + EXPECT_EQ(2, n); +} + +// Tests DoAll(a1, a2, a3, a4). +TEST(DoAllTest, FourActions) { + int m = 0, n = 0; + char ch = '\0'; + Action a = // NOLINT + DoAll(SetArgPointee<0>(1), + SetArgPointee<1>(2), + SetArgPointee<2>('a'), + Return(3)); + EXPECT_EQ(3, a.Perform(make_tuple(&m, &n, &ch))); + EXPECT_EQ(1, m); + EXPECT_EQ(2, n); + EXPECT_EQ('a', ch); +} + +// Tests DoAll(a1, a2, a3, a4, a5). +TEST(DoAllTest, FiveActions) { + int m = 0, n = 0; + char a = '\0', b = '\0'; + Action action = // NOLINT + DoAll(SetArgPointee<0>(1), + SetArgPointee<1>(2), + SetArgPointee<2>('a'), + SetArgPointee<3>('b'), + Return(3)); + EXPECT_EQ(3, action.Perform(make_tuple(&m, &n, &a, &b))); + EXPECT_EQ(1, m); + EXPECT_EQ(2, n); + EXPECT_EQ('a', a); + EXPECT_EQ('b', b); +} + +// Tests DoAll(a1, a2, ..., a6). +TEST(DoAllTest, SixActions) { + int m = 0, n = 0; + char a = '\0', b = '\0', c = '\0'; + Action action = // NOLINT + DoAll(SetArgPointee<0>(1), + SetArgPointee<1>(2), + SetArgPointee<2>('a'), + SetArgPointee<3>('b'), + SetArgPointee<4>('c'), + Return(3)); + EXPECT_EQ(3, action.Perform(make_tuple(&m, &n, &a, &b, &c))); + EXPECT_EQ(1, m); + EXPECT_EQ(2, n); + EXPECT_EQ('a', a); + EXPECT_EQ('b', b); + EXPECT_EQ('c', c); +} + +// Tests DoAll(a1, a2, ..., a7). +TEST(DoAllTest, SevenActions) { + int m = 0, n = 0; + char a = '\0', b = '\0', c = '\0', d = '\0'; + Action action = // NOLINT + DoAll(SetArgPointee<0>(1), + SetArgPointee<1>(2), + SetArgPointee<2>('a'), + SetArgPointee<3>('b'), + SetArgPointee<4>('c'), + SetArgPointee<5>('d'), + Return(3)); + EXPECT_EQ(3, action.Perform(make_tuple(&m, &n, &a, &b, &c, &d))); + EXPECT_EQ(1, m); + EXPECT_EQ(2, n); + EXPECT_EQ('a', a); + EXPECT_EQ('b', b); + EXPECT_EQ('c', c); + EXPECT_EQ('d', d); +} + +// Tests DoAll(a1, a2, ..., a8). +TEST(DoAllTest, EightActions) { + int m = 0, n = 0; + char a = '\0', b = '\0', c = '\0', d = '\0', e = '\0'; + Action action = + DoAll(SetArgPointee<0>(1), + SetArgPointee<1>(2), + SetArgPointee<2>('a'), + SetArgPointee<3>('b'), + SetArgPointee<4>('c'), + SetArgPointee<5>('d'), + SetArgPointee<6>('e'), + Return(3)); + EXPECT_EQ(3, action.Perform(make_tuple(&m, &n, &a, &b, &c, &d, &e))); + EXPECT_EQ(1, m); + EXPECT_EQ(2, n); + EXPECT_EQ('a', a); + EXPECT_EQ('b', b); + EXPECT_EQ('c', c); + EXPECT_EQ('d', d); + EXPECT_EQ('e', e); +} + +// Tests DoAll(a1, a2, ..., a9). +TEST(DoAllTest, NineActions) { + int m = 0, n = 0; + char a = '\0', b = '\0', c = '\0', d = '\0', e = '\0', f = '\0'; + Action action = + DoAll(SetArgPointee<0>(1), + SetArgPointee<1>(2), + SetArgPointee<2>('a'), + SetArgPointee<3>('b'), + SetArgPointee<4>('c'), + SetArgPointee<5>('d'), + SetArgPointee<6>('e'), + SetArgPointee<7>('f'), + Return(3)); + EXPECT_EQ(3, action.Perform(make_tuple(&m, &n, &a, &b, &c, &d, &e, &f))); + EXPECT_EQ(1, m); + EXPECT_EQ(2, n); + EXPECT_EQ('a', a); + EXPECT_EQ('b', b); + EXPECT_EQ('c', c); + EXPECT_EQ('d', d); + EXPECT_EQ('e', e); + EXPECT_EQ('f', f); +} + +// Tests DoAll(a1, a2, ..., a10). +TEST(DoAllTest, TenActions) { + int m = 0, n = 0; + char a = '\0', b = '\0', c = '\0', d = '\0'; + char e = '\0', f = '\0', g = '\0'; + Action action = + DoAll(SetArgPointee<0>(1), + SetArgPointee<1>(2), + SetArgPointee<2>('a'), + SetArgPointee<3>('b'), + SetArgPointee<4>('c'), + SetArgPointee<5>('d'), + SetArgPointee<6>('e'), + SetArgPointee<7>('f'), + SetArgPointee<8>('g'), + Return(3)); + EXPECT_EQ(3, action.Perform(make_tuple(&m, &n, &a, &b, &c, &d, &e, &f, &g))); + EXPECT_EQ(1, m); + EXPECT_EQ(2, n); + EXPECT_EQ('a', a); + EXPECT_EQ('b', b); + EXPECT_EQ('c', c); + EXPECT_EQ('d', d); + EXPECT_EQ('e', e); + EXPECT_EQ('f', f); + EXPECT_EQ('g', g); +} + +// The ACTION*() macros trigger warning C4100 (unreferenced formal +// parameter) in MSVC with -W4. Unfortunately they cannot be fixed in +// the macro definition, as the warnings are generated when the macro +// is expanded and macro expansion cannot contain #pragma. Therefore +// we suppress them here. +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable:4100) +#endif + +// Tests the ACTION*() macro family. + +// Tests that ACTION() can define an action that doesn't reference the +// mock function arguments. +ACTION(Return5) { return 5; } + +TEST(ActionMacroTest, WorksWhenNotReferencingArguments) { + Action a1 = Return5(); + EXPECT_DOUBLE_EQ(5, a1.Perform(make_tuple())); + + Action a2 = Return5(); + EXPECT_EQ(5, a2.Perform(make_tuple(1, true))); +} + +// Tests that ACTION() can define an action that returns void. +ACTION(IncrementArg1) { (*arg1)++; } + +TEST(ActionMacroTest, WorksWhenReturningVoid) { + Action a1 = IncrementArg1(); + int n = 0; + a1.Perform(make_tuple(5, &n)); + EXPECT_EQ(1, n); +} + +// Tests that the body of ACTION() can reference the type of the +// argument. +ACTION(IncrementArg2) { + StaticAssertTypeEq(); + arg2_type temp = arg2; + (*temp)++; +} + +TEST(ActionMacroTest, CanReferenceArgumentType) { + Action a1 = IncrementArg2(); + int n = 0; + a1.Perform(make_tuple(5, false, &n)); + EXPECT_EQ(1, n); +} + +// Tests that the body of ACTION() can reference the argument tuple +// via args_type and args. +ACTION(Sum2) { + StaticAssertTypeEq< ::std::tr1::tuple, args_type>(); + args_type args_copy = args; + return get<0>(args_copy) + get<1>(args_copy); +} + +TEST(ActionMacroTest, CanReferenceArgumentTuple) { + Action a1 = Sum2(); + int dummy = 0; + EXPECT_EQ(11, a1.Perform(make_tuple(5, Char(6), &dummy))); +} + +// Tests that the body of ACTION() can reference the mock function +// type. +int Dummy(bool flag) { return flag? 1 : 0; } + +ACTION(InvokeDummy) { + StaticAssertTypeEq(); + function_type* fp = &Dummy; + return (*fp)(true); +} + +TEST(ActionMacroTest, CanReferenceMockFunctionType) { + Action a1 = InvokeDummy(); + EXPECT_EQ(1, a1.Perform(make_tuple(true))); + EXPECT_EQ(1, a1.Perform(make_tuple(false))); +} + +// Tests that the body of ACTION() can reference the mock function's +// return type. +ACTION(InvokeDummy2) { + StaticAssertTypeEq(); + return_type result = Dummy(true); + return result; +} + +TEST(ActionMacroTest, CanReferenceMockFunctionReturnType) { + Action a1 = InvokeDummy2(); + EXPECT_EQ(1, a1.Perform(make_tuple(true))); + EXPECT_EQ(1, a1.Perform(make_tuple(false))); +} + +// Tests that ACTION() works for arguments passed by const reference. +ACTION(ReturnAddrOfConstBoolReferenceArg) { + StaticAssertTypeEq(); + return &arg1; +} + +TEST(ActionMacroTest, WorksForConstReferenceArg) { + Action a = ReturnAddrOfConstBoolReferenceArg(); + const bool b = false; + EXPECT_EQ(&b, a.Perform(tuple(0, b))); +} + +// Tests that ACTION() works for arguments passed by non-const reference. +ACTION(ReturnAddrOfIntReferenceArg) { + StaticAssertTypeEq(); + return &arg0; +} + +TEST(ActionMacroTest, WorksForNonConstReferenceArg) { + Action a = ReturnAddrOfIntReferenceArg(); + int n = 0; + EXPECT_EQ(&n, a.Perform(tuple(n, true, 1))); +} + +// Tests that ACTION() can be used in a namespace. +namespace action_test { +ACTION(Sum) { return arg0 + arg1; } +} // namespace action_test + +TEST(ActionMacroTest, WorksInNamespace) { + Action a1 = action_test::Sum(); + EXPECT_EQ(3, a1.Perform(make_tuple(1, 2))); +} + +// Tests that the same ACTION definition works for mock functions with +// different argument numbers. +ACTION(PlusTwo) { return arg0 + 2; } + +TEST(ActionMacroTest, WorksForDifferentArgumentNumbers) { + Action a1 = PlusTwo(); + EXPECT_EQ(4, a1.Perform(make_tuple(2))); + + Action a2 = PlusTwo(); + int dummy; + EXPECT_DOUBLE_EQ(6, a2.Perform(make_tuple(4.0f, &dummy))); +} + +// Tests that ACTION_P can define a parameterized action. +ACTION_P(Plus, n) { return arg0 + n; } + +TEST(ActionPMacroTest, DefinesParameterizedAction) { + Action a1 = Plus(9); + EXPECT_EQ(10, a1.Perform(make_tuple(1, true))); +} + +// Tests that the body of ACTION_P can reference the argument types +// and the parameter type. +ACTION_P(TypedPlus, n) { + arg0_type t1 = arg0; + n_type t2 = n; + return t1 + t2; +} + +TEST(ActionPMacroTest, CanReferenceArgumentAndParameterTypes) { + Action a1 = TypedPlus(9); + EXPECT_EQ(10, a1.Perform(make_tuple(Char(1), true))); +} + +// Tests that a parameterized action can be used in any mock function +// whose type is compatible. +TEST(ActionPMacroTest, WorksInCompatibleMockFunction) { + Action a1 = Plus("tail"); + const std::string re = "re"; + EXPECT_EQ("retail", a1.Perform(make_tuple(re))); +} + +// Tests that we can use ACTION*() to define actions overloaded on the +// number of parameters. + +ACTION(OverloadedAction) { return arg0 ? arg1 : "hello"; } + +ACTION_P(OverloadedAction, default_value) { + return arg0 ? arg1 : default_value; +} + +ACTION_P2(OverloadedAction, true_value, false_value) { + return arg0 ? true_value : false_value; +} + +TEST(ActionMacroTest, CanDefineOverloadedActions) { + typedef Action MyAction; + + const MyAction a1 = OverloadedAction(); + EXPECT_STREQ("hello", a1.Perform(make_tuple(false, CharPtr("world")))); + EXPECT_STREQ("world", a1.Perform(make_tuple(true, CharPtr("world")))); + + const MyAction a2 = OverloadedAction("hi"); + EXPECT_STREQ("hi", a2.Perform(make_tuple(false, CharPtr("world")))); + EXPECT_STREQ("world", a2.Perform(make_tuple(true, CharPtr("world")))); + + const MyAction a3 = OverloadedAction("hi", "you"); + EXPECT_STREQ("hi", a3.Perform(make_tuple(true, CharPtr("world")))); + EXPECT_STREQ("you", a3.Perform(make_tuple(false, CharPtr("world")))); +} + +// Tests ACTION_Pn where n >= 3. + +ACTION_P3(Plus, m, n, k) { return arg0 + m + n + k; } + +TEST(ActionPnMacroTest, WorksFor3Parameters) { + Action a1 = Plus(100, 20, 3.4); + EXPECT_DOUBLE_EQ(3123.4, a1.Perform(make_tuple(3000, true))); + + Action a2 = Plus("tail", "-", ">"); + const std::string re = "re"; + EXPECT_EQ("retail->", a2.Perform(make_tuple(re))); +} + +ACTION_P4(Plus, p0, p1, p2, p3) { return arg0 + p0 + p1 + p2 + p3; } + +TEST(ActionPnMacroTest, WorksFor4Parameters) { + Action a1 = Plus(1, 2, 3, 4); + EXPECT_EQ(10 + 1 + 2 + 3 + 4, a1.Perform(make_tuple(10))); +} + +ACTION_P5(Plus, p0, p1, p2, p3, p4) { return arg0 + p0 + p1 + p2 + p3 + p4; } + +TEST(ActionPnMacroTest, WorksFor5Parameters) { + Action a1 = Plus(1, 2, 3, 4, 5); + EXPECT_EQ(10 + 1 + 2 + 3 + 4 + 5, a1.Perform(make_tuple(10))); +} + +ACTION_P6(Plus, p0, p1, p2, p3, p4, p5) { + return arg0 + p0 + p1 + p2 + p3 + p4 + p5; +} + +TEST(ActionPnMacroTest, WorksFor6Parameters) { + Action a1 = Plus(1, 2, 3, 4, 5, 6); + EXPECT_EQ(10 + 1 + 2 + 3 + 4 + 5 + 6, a1.Perform(make_tuple(10))); +} + +ACTION_P7(Plus, p0, p1, p2, p3, p4, p5, p6) { + return arg0 + p0 + p1 + p2 + p3 + p4 + p5 + p6; +} + +TEST(ActionPnMacroTest, WorksFor7Parameters) { + Action a1 = Plus(1, 2, 3, 4, 5, 6, 7); + EXPECT_EQ(10 + 1 + 2 + 3 + 4 + 5 + 6 + 7, a1.Perform(make_tuple(10))); +} + +ACTION_P8(Plus, p0, p1, p2, p3, p4, p5, p6, p7) { + return arg0 + p0 + p1 + p2 + p3 + p4 + p5 + p6 + p7; +} + +TEST(ActionPnMacroTest, WorksFor8Parameters) { + Action a1 = Plus(1, 2, 3, 4, 5, 6, 7, 8); + EXPECT_EQ(10 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8, a1.Perform(make_tuple(10))); +} + +ACTION_P9(Plus, p0, p1, p2, p3, p4, p5, p6, p7, p8) { + return arg0 + p0 + p1 + p2 + p3 + p4 + p5 + p6 + p7 + p8; +} + +TEST(ActionPnMacroTest, WorksFor9Parameters) { + Action a1 = Plus(1, 2, 3, 4, 5, 6, 7, 8, 9); + EXPECT_EQ(10 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9, a1.Perform(make_tuple(10))); +} + +ACTION_P10(Plus, p0, p1, p2, p3, p4, p5, p6, p7, p8, last_param) { + arg0_type t0 = arg0; + last_param_type t9 = last_param; + return t0 + p0 + p1 + p2 + p3 + p4 + p5 + p6 + p7 + p8 + t9; +} + +TEST(ActionPnMacroTest, WorksFor10Parameters) { + Action a1 = Plus(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + EXPECT_EQ(10 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10, + a1.Perform(make_tuple(10))); +} + +// Tests that the action body can promote the parameter types. + +ACTION_P2(PadArgument, prefix, suffix) { + // The following lines promote the two parameters to desired types. + std::string prefix_str(prefix); + char suffix_char = static_cast(suffix); + return prefix_str + arg0 + suffix_char; +} + +TEST(ActionPnMacroTest, SimpleTypePromotion) { + Action no_promo = + PadArgument(std::string("foo"), 'r'); + Action promo = + PadArgument("foo", static_cast('r')); + EXPECT_EQ("foobar", no_promo.Perform(make_tuple(CharPtr("ba")))); + EXPECT_EQ("foobar", promo.Perform(make_tuple(CharPtr("ba")))); +} + +// Tests that we can partially restrict parameter types using a +// straight-forward pattern. + +// Defines a generic action that doesn't restrict the types of its +// parameters. +ACTION_P3(ConcatImpl, a, b, c) { + std::stringstream ss; + ss << a << b << c; + return ss.str(); +} + +// Next, we try to restrict that either the first parameter is a +// string, or the second parameter is an int. + +// Defines a partially specialized wrapper that restricts the first +// parameter to std::string. +template +// ConcatImplActionP3 is the class template ACTION_P3 uses to +// implement ConcatImpl. We shouldn't change the name as this +// pattern requires the user to use it directly. +ConcatImplActionP3 +Concat(const std::string& a, T1 b, T2 c) { + if (true) { + // This branch verifies that ConcatImpl() can be invoked without + // explicit template arguments. + return ConcatImpl(a, b, c); + } else { + // This branch verifies that ConcatImpl() can also be invoked with + // explicit template arguments. It doesn't really need to be + // executed as this is a compile-time verification. + return ConcatImpl(a, b, c); + } +} + +// Defines another partially specialized wrapper that restricts the +// second parameter to int. +template +ConcatImplActionP3 +Concat(T1 a, int b, T2 c) { + return ConcatImpl(a, b, c); +} + +TEST(ActionPnMacroTest, CanPartiallyRestrictParameterTypes) { + Action a1 = Concat("Hello", "1", 2); + EXPECT_EQ("Hello12", a1.Perform(make_tuple())); + + a1 = Concat(1, 2, 3); + EXPECT_EQ("123", a1.Perform(make_tuple())); +} + +// Verifies the type of an ACTION*. + +ACTION(DoFoo) {} +ACTION_P(DoFoo, p) {} +ACTION_P2(DoFoo, p0, p1) {} + +TEST(ActionPnMacroTest, TypesAreCorrect) { + // DoFoo() must be assignable to a DoFooAction variable. + DoFooAction a0 = DoFoo(); + + // DoFoo(1) must be assignable to a DoFooActionP variable. + DoFooActionP a1 = DoFoo(1); + + // DoFoo(p1, ..., pk) must be assignable to a DoFooActionPk + // variable, and so on. + DoFooActionP2 a2 = DoFoo(1, '2'); + PlusActionP3 a3 = Plus(1, 2, '3'); + PlusActionP4 a4 = Plus(1, 2, 3, '4'); + PlusActionP5 a5 = Plus(1, 2, 3, 4, '5'); + PlusActionP6 a6 = Plus(1, 2, 3, 4, 5, '6'); + PlusActionP7 a7 = + Plus(1, 2, 3, 4, 5, 6, '7'); + PlusActionP8 a8 = + Plus(1, 2, 3, 4, 5, 6, 7, '8'); + PlusActionP9 a9 = + Plus(1, 2, 3, 4, 5, 6, 7, 8, '9'); + PlusActionP10 a10 = + Plus(1, 2, 3, 4, 5, 6, 7, 8, 9, '0'); +} + +// Tests that an ACTION_P*() action can be explicitly instantiated +// with reference-typed parameters. + +ACTION_P(Plus1, x) { return x; } +ACTION_P2(Plus2, x, y) { return x + y; } +ACTION_P3(Plus3, x, y, z) { return x + y + z; } +ACTION_P10(Plus10, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) { + return a0 + a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9; +} + +TEST(ActionPnMacroTest, CanExplicitlyInstantiateWithReferenceTypes) { + int x = 1, y = 2, z = 3; + const tuple<> empty = make_tuple(); + + Action a = Plus1(x); + EXPECT_EQ(1, a.Perform(empty)); + + a = Plus2(x, y); + EXPECT_EQ(3, a.Perform(empty)); + + a = Plus3(x, y, z); + EXPECT_EQ(6, a.Perform(empty)); + + int n[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + a = Plus10(n[0], n[1], n[2], n[3], n[4], n[5], n[6], n[7], + n[8], n[9]); + EXPECT_EQ(55, a.Perform(empty)); +} + +class NullaryConstructorClass { + public: + NullaryConstructorClass() : value_(123) {} + int value_; +}; + +// Tests using ReturnNew() with a nullary constructor. +TEST(ReturnNewTest, NoArgs) { + Action a = ReturnNew(); + NullaryConstructorClass* c = a.Perform(make_tuple()); + EXPECT_EQ(123, c->value_); + delete c; +} + +class UnaryConstructorClass { + public: + explicit UnaryConstructorClass(int value) : value_(value) {} + int value_; +}; + +// Tests using ReturnNew() with a unary constructor. +TEST(ReturnNewTest, Unary) { + Action a = ReturnNew(4000); + UnaryConstructorClass* c = a.Perform(make_tuple()); + EXPECT_EQ(4000, c->value_); + delete c; +} + +TEST(ReturnNewTest, UnaryWorksWhenMockMethodHasArgs) { + Action a = + ReturnNew(4000); + UnaryConstructorClass* c = a.Perform(make_tuple(false, 5)); + EXPECT_EQ(4000, c->value_); + delete c; +} + +TEST(ReturnNewTest, UnaryWorksWhenMockMethodReturnsPointerToConst) { + Action a = + ReturnNew(4000); + const UnaryConstructorClass* c = a.Perform(make_tuple()); + EXPECT_EQ(4000, c->value_); + delete c; +} + +class TenArgConstructorClass { + public: + TenArgConstructorClass(int a1, int a2, int a3, int a4, int a5, + int a6, int a7, int a8, int a9, int a10) + : value_(a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a10) { + } + int value_; +}; + +// Tests using ReturnNew() with a 10-argument constructor. +TEST(ReturnNewTest, ConstructorThatTakes10Arguments) { + Action a = + ReturnNew(1000000000, 200000000, 30000000, + 4000000, 500000, 60000, + 7000, 800, 90, 0); + TenArgConstructorClass* c = a.Perform(make_tuple()); + EXPECT_EQ(1234567890, c->value_); + delete c; +} + +// Tests that ACTION_TEMPLATE works when there is no value parameter. +ACTION_TEMPLATE(CreateNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_0_VALUE_PARAMS()) { + return new T; +} + +TEST(ActionTemplateTest, WorksWithoutValueParam) { + const Action a = CreateNew(); + int* p = a.Perform(make_tuple()); + delete p; +} + +// Tests that ACTION_TEMPLATE works when there are value parameters. +ACTION_TEMPLATE(CreateNew, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_1_VALUE_PARAMS(a0)) { + return new T(a0); +} + +TEST(ActionTemplateTest, WorksWithValueParams) { + const Action a = CreateNew(42); + int* p = a.Perform(make_tuple()); + EXPECT_EQ(42, *p); + delete p; +} + +// Tests that ACTION_TEMPLATE works for integral template parameters. +ACTION_TEMPLATE(MyDeleteArg, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_0_VALUE_PARAMS()) { + delete std::tr1::get(args); +} + +// Resets a bool variable in the destructor. +class BoolResetter { + public: + explicit BoolResetter(bool* value) : value_(value) {} + ~BoolResetter() { *value_ = false; } + private: + bool* value_; +}; + +TEST(ActionTemplateTest, WorksForIntegralTemplateParams) { + const Action a = MyDeleteArg<1>(); + int n = 0; + bool b = true; + BoolResetter* resetter = new BoolResetter(&b); + a.Perform(make_tuple(&n, resetter)); + EXPECT_FALSE(b); // Verifies that resetter is deleted. +} + +// Tests that ACTION_TEMPLATES works for template template parameters. +ACTION_TEMPLATE(ReturnSmartPointer, + HAS_1_TEMPLATE_PARAMS(template class, + Pointer), + AND_1_VALUE_PARAMS(pointee)) { + return Pointer(new pointee_type(pointee)); +} + +TEST(ActionTemplateTest, WorksForTemplateTemplateParameters) { + using ::testing::internal::linked_ptr; + const Action()> a = ReturnSmartPointer(42); + linked_ptr p = a.Perform(make_tuple()); + EXPECT_EQ(42, *p); +} + +// Tests that ACTION_TEMPLATE works for 10 template parameters. +template +struct GiantTemplate { + public: + explicit GiantTemplate(int a_value) : value(a_value) {} + int value; +}; + +ACTION_TEMPLATE(ReturnGiant, + HAS_10_TEMPLATE_PARAMS( + typename, T1, + typename, T2, + typename, T3, + int, k4, + bool, k5, + unsigned int, k6, + class, T7, + class, T8, + class, T9, + template class, T10), + AND_1_VALUE_PARAMS(value)) { + return GiantTemplate, T2, T3, k4, k5, k6, T7, T8, T9>(value); +} + +TEST(ActionTemplateTest, WorksFor10TemplateParameters) { + using ::testing::internal::linked_ptr; + typedef GiantTemplate, bool, double, 5, + true, 6, char, unsigned, int> Giant; + const Action a = ReturnGiant< + int, bool, double, 5, true, 6, char, unsigned, int, linked_ptr>(42); + Giant giant = a.Perform(make_tuple()); + EXPECT_EQ(42, giant.value); +} + +// Tests that ACTION_TEMPLATE works for 10 value parameters. +ACTION_TEMPLATE(ReturnSum, + HAS_1_TEMPLATE_PARAMS(typename, Number), + AND_10_VALUE_PARAMS(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10)) { + return static_cast(v1) + v2 + v3 + v4 + v5 + v6 + v7 + v8 + v9 + v10; +} + +TEST(ActionTemplateTest, WorksFor10ValueParameters) { + const Action a = ReturnSum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + EXPECT_EQ(55, a.Perform(make_tuple())); +} + +// Tests that ACTION_TEMPLATE and ACTION/ACTION_P* can be overloaded +// on the number of value parameters. + +ACTION(ReturnSum) { return 0; } + +ACTION_P(ReturnSum, x) { return x; } + +ACTION_TEMPLATE(ReturnSum, + HAS_1_TEMPLATE_PARAMS(typename, Number), + AND_2_VALUE_PARAMS(v1, v2)) { + return static_cast(v1) + v2; +} + +ACTION_TEMPLATE(ReturnSum, + HAS_1_TEMPLATE_PARAMS(typename, Number), + AND_3_VALUE_PARAMS(v1, v2, v3)) { + return static_cast(v1) + v2 + v3; +} + +ACTION_TEMPLATE(ReturnSum, + HAS_2_TEMPLATE_PARAMS(typename, Number, int, k), + AND_4_VALUE_PARAMS(v1, v2, v3, v4)) { + return static_cast(v1) + v2 + v3 + v4 + k; +} + +TEST(ActionTemplateTest, CanBeOverloadedOnNumberOfValueParameters) { + const Action a0 = ReturnSum(); + const Action a1 = ReturnSum(1); + const Action a2 = ReturnSum(1, 2); + const Action a3 = ReturnSum(1, 2, 3); + const Action a4 = ReturnSum(2000, 300, 40, 5); + EXPECT_EQ(0, a0.Perform(make_tuple())); + EXPECT_EQ(1, a1.Perform(make_tuple())); + EXPECT_EQ(3, a2.Perform(make_tuple())); + EXPECT_EQ(6, a3.Perform(make_tuple())); + EXPECT_EQ(12345, a4.Perform(make_tuple())); +} + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + +} // namespace gmock_generated_actions_test +} // namespace testing diff --git a/libwvdrmengine/test/gmock/test/gmock-generated-function-mockers_test.cc b/libwvdrmengine/test/gmock/test/gmock-generated-function-mockers_test.cc new file mode 100644 index 00000000..0d90ded7 --- /dev/null +++ b/libwvdrmengine/test/gmock/test/gmock-generated-function-mockers_test.cc @@ -0,0 +1,540 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file tests the function mocker classes. + +#include "gmock/gmock-generated-function-mockers.h" + +#include +#include +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#if GTEST_OS_WINDOWS +// MSDN says the header file to be included for STDMETHOD is BaseTyps.h but +// we are getting compiler errors if we use basetyps.h, hence including +// objbase.h for definition of STDMETHOD. +# include +#endif // GTEST_OS_WINDOWS + +// There is a bug in MSVC (fixed in VS 2008) that prevents creating a +// mock for a function with const arguments, so we don't test such +// cases for MSVC versions older than 2008. +#if !GTEST_OS_WINDOWS || (_MSC_VER >= 1500) +# define GMOCK_ALLOWS_CONST_PARAM_FUNCTIONS +#endif // !GTEST_OS_WINDOWS || (_MSC_VER >= 1500) + +namespace testing { +namespace gmock_generated_function_mockers_test { + +using testing::internal::string; +using testing::_; +using testing::A; +using testing::An; +using testing::AnyNumber; +using testing::Const; +using testing::DoDefault; +using testing::Eq; +using testing::Lt; +using testing::MockFunction; +using testing::Ref; +using testing::Return; +using testing::ReturnRef; +using testing::TypedEq; + +class FooInterface { + public: + virtual ~FooInterface() {} + + virtual void VoidReturning(int x) = 0; + + virtual int Nullary() = 0; + virtual bool Unary(int x) = 0; + virtual long Binary(short x, int y) = 0; // NOLINT + virtual int Decimal(bool b, char c, short d, int e, long f, // NOLINT + float g, double h, unsigned i, char* j, const string& k) + = 0; + + virtual bool TakesNonConstReference(int& n) = 0; // NOLINT + virtual string TakesConstReference(const int& n) = 0; +#ifdef GMOCK_ALLOWS_CONST_PARAM_FUNCTIONS + virtual bool TakesConst(const int x) = 0; +#endif // GMOCK_ALLOWS_CONST_PARAM_FUNCTIONS + + virtual int OverloadedOnArgumentNumber() = 0; + virtual int OverloadedOnArgumentNumber(int n) = 0; + + virtual int OverloadedOnArgumentType(int n) = 0; + virtual char OverloadedOnArgumentType(char c) = 0; + + virtual int OverloadedOnConstness() = 0; + virtual char OverloadedOnConstness() const = 0; + + virtual int TypeWithHole(int (*func)()) = 0; + virtual int TypeWithComma(const std::map& a_map) = 0; + +#if GTEST_OS_WINDOWS + STDMETHOD_(int, CTNullary)() = 0; + STDMETHOD_(bool, CTUnary)(int x) = 0; + STDMETHOD_(int, CTDecimal)(bool b, char c, short d, int e, long f, // NOLINT + float g, double h, unsigned i, char* j, const string& k) = 0; + STDMETHOD_(char, CTConst)(int x) const = 0; +#endif // GTEST_OS_WINDOWS +}; + +class MockFoo : public FooInterface { + public: + MockFoo() {} + + // Makes sure that a mock function parameter can be named. + MOCK_METHOD1(VoidReturning, void(int n)); // NOLINT + + MOCK_METHOD0(Nullary, int()); // NOLINT + + // Makes sure that a mock function parameter can be unnamed. + MOCK_METHOD1(Unary, bool(int)); // NOLINT + MOCK_METHOD2(Binary, long(short, int)); // NOLINT + MOCK_METHOD10(Decimal, int(bool, char, short, int, long, float, // NOLINT + double, unsigned, char*, const string& str)); + + MOCK_METHOD1(TakesNonConstReference, bool(int&)); // NOLINT + MOCK_METHOD1(TakesConstReference, string(const int&)); +#ifdef GMOCK_ALLOWS_CONST_PARAM_FUNCTIONS + MOCK_METHOD1(TakesConst, bool(const int)); // NOLINT +#endif // GMOCK_ALLOWS_CONST_PARAM_FUNCTIONS + MOCK_METHOD0(OverloadedOnArgumentNumber, int()); // NOLINT + MOCK_METHOD1(OverloadedOnArgumentNumber, int(int)); // NOLINT + + MOCK_METHOD1(OverloadedOnArgumentType, int(int)); // NOLINT + MOCK_METHOD1(OverloadedOnArgumentType, char(char)); // NOLINT + + MOCK_METHOD0(OverloadedOnConstness, int()); // NOLINT + MOCK_CONST_METHOD0(OverloadedOnConstness, char()); // NOLINT + + MOCK_METHOD1(TypeWithHole, int(int (*)())); // NOLINT + MOCK_METHOD1(TypeWithComma, int(const std::map&)); // NOLINT +#if GTEST_OS_WINDOWS + MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, CTNullary, int()); + MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, CTUnary, bool(int)); + MOCK_METHOD10_WITH_CALLTYPE(STDMETHODCALLTYPE, CTDecimal, int(bool b, char c, + short d, int e, long f, float g, double h, unsigned i, char* j, + const string& k)); + MOCK_CONST_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, CTConst, char(int)); +#endif // GTEST_OS_WINDOWS + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFoo); +}; + +class FunctionMockerTest : public testing::Test { + protected: + FunctionMockerTest() : foo_(&mock_foo_) {} + + FooInterface* const foo_; + MockFoo mock_foo_; +}; + +// Tests mocking a void-returning function. +TEST_F(FunctionMockerTest, MocksVoidFunction) { + EXPECT_CALL(mock_foo_, VoidReturning(Lt(100))); + foo_->VoidReturning(0); +} + +// Tests mocking a nullary function. +TEST_F(FunctionMockerTest, MocksNullaryFunction) { + EXPECT_CALL(mock_foo_, Nullary()) + .WillOnce(DoDefault()) + .WillOnce(Return(1)); + + EXPECT_EQ(0, foo_->Nullary()); + EXPECT_EQ(1, foo_->Nullary()); +} + +// Tests mocking a unary function. +TEST_F(FunctionMockerTest, MocksUnaryFunction) { + EXPECT_CALL(mock_foo_, Unary(Eq(2))) + .Times(2) + .WillOnce(Return(true)); + + EXPECT_TRUE(foo_->Unary(2)); + EXPECT_FALSE(foo_->Unary(2)); +} + +// Tests mocking a binary function. +TEST_F(FunctionMockerTest, MocksBinaryFunction) { + EXPECT_CALL(mock_foo_, Binary(2, _)) + .WillOnce(Return(3)); + + EXPECT_EQ(3, foo_->Binary(2, 1)); +} + +// Tests mocking a decimal function. +TEST_F(FunctionMockerTest, MocksDecimalFunction) { + EXPECT_CALL(mock_foo_, Decimal(true, 'a', 0, 0, 1L, A(), + Lt(100), 5U, NULL, "hi")) + .WillOnce(Return(5)); + + EXPECT_EQ(5, foo_->Decimal(true, 'a', 0, 0, 1, 0, 0, 5, NULL, "hi")); +} + +// Tests mocking a function that takes a non-const reference. +TEST_F(FunctionMockerTest, MocksFunctionWithNonConstReferenceArgument) { + int a = 0; + EXPECT_CALL(mock_foo_, TakesNonConstReference(Ref(a))) + .WillOnce(Return(true)); + + EXPECT_TRUE(foo_->TakesNonConstReference(a)); +} + +// Tests mocking a function that takes a const reference. +TEST_F(FunctionMockerTest, MocksFunctionWithConstReferenceArgument) { + int a = 0; + EXPECT_CALL(mock_foo_, TakesConstReference(Ref(a))) + .WillOnce(Return("Hello")); + + EXPECT_EQ("Hello", foo_->TakesConstReference(a)); +} + +#ifdef GMOCK_ALLOWS_CONST_PARAM_FUNCTIONS +// Tests mocking a function that takes a const variable. +TEST_F(FunctionMockerTest, MocksFunctionWithConstArgument) { + EXPECT_CALL(mock_foo_, TakesConst(Lt(10))) + .WillOnce(DoDefault()); + + EXPECT_FALSE(foo_->TakesConst(5)); +} +#endif // GMOCK_ALLOWS_CONST_PARAM_FUNCTIONS + +// Tests mocking functions overloaded on the number of arguments. +TEST_F(FunctionMockerTest, MocksFunctionsOverloadedOnArgumentNumber) { + EXPECT_CALL(mock_foo_, OverloadedOnArgumentNumber()) + .WillOnce(Return(1)); + EXPECT_CALL(mock_foo_, OverloadedOnArgumentNumber(_)) + .WillOnce(Return(2)); + + EXPECT_EQ(2, foo_->OverloadedOnArgumentNumber(1)); + EXPECT_EQ(1, foo_->OverloadedOnArgumentNumber()); +} + +// Tests mocking functions overloaded on the types of argument. +TEST_F(FunctionMockerTest, MocksFunctionsOverloadedOnArgumentType) { + EXPECT_CALL(mock_foo_, OverloadedOnArgumentType(An())) + .WillOnce(Return(1)); + EXPECT_CALL(mock_foo_, OverloadedOnArgumentType(TypedEq('a'))) + .WillOnce(Return('b')); + + EXPECT_EQ(1, foo_->OverloadedOnArgumentType(0)); + EXPECT_EQ('b', foo_->OverloadedOnArgumentType('a')); +} + +// Tests mocking functions overloaded on the const-ness of this object. +TEST_F(FunctionMockerTest, MocksFunctionsOverloadedOnConstnessOfThis) { + EXPECT_CALL(mock_foo_, OverloadedOnConstness()); + EXPECT_CALL(Const(mock_foo_), OverloadedOnConstness()) + .WillOnce(Return('a')); + + EXPECT_EQ(0, foo_->OverloadedOnConstness()); + EXPECT_EQ('a', Const(*foo_).OverloadedOnConstness()); +} + +#if GTEST_OS_WINDOWS +// Tests mocking a nullary function with calltype. +TEST_F(FunctionMockerTest, MocksNullaryFunctionWithCallType) { + EXPECT_CALL(mock_foo_, CTNullary()) + .WillOnce(Return(-1)) + .WillOnce(Return(0)); + + EXPECT_EQ(-1, foo_->CTNullary()); + EXPECT_EQ(0, foo_->CTNullary()); +} + +// Tests mocking a unary function with calltype. +TEST_F(FunctionMockerTest, MocksUnaryFunctionWithCallType) { + EXPECT_CALL(mock_foo_, CTUnary(Eq(2))) + .Times(2) + .WillOnce(Return(true)) + .WillOnce(Return(false)); + + EXPECT_TRUE(foo_->CTUnary(2)); + EXPECT_FALSE(foo_->CTUnary(2)); +} + +// Tests mocking a decimal function with calltype. +TEST_F(FunctionMockerTest, MocksDecimalFunctionWithCallType) { + EXPECT_CALL(mock_foo_, CTDecimal(true, 'a', 0, 0, 1L, A(), + Lt(100), 5U, NULL, "hi")) + .WillOnce(Return(10)); + + EXPECT_EQ(10, foo_->CTDecimal(true, 'a', 0, 0, 1, 0, 0, 5, NULL, "hi")); +} + +// Tests mocking functions overloaded on the const-ness of this object. +TEST_F(FunctionMockerTest, MocksFunctionsConstFunctionWithCallType) { + EXPECT_CALL(Const(mock_foo_), CTConst(_)) + .WillOnce(Return('a')); + + EXPECT_EQ('a', Const(*foo_).CTConst(0)); +} + +#endif // GTEST_OS_WINDOWS + +class MockB { + public: + MockB() {} + + MOCK_METHOD0(DoB, void()); + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(MockB); +}; + +// Tests that functions with no EXPECT_CALL() ruls can be called any +// number of times. +TEST(ExpectCallTest, UnmentionedFunctionCanBeCalledAnyNumberOfTimes) { + { + MockB b; + } + + { + MockB b; + b.DoB(); + } + + { + MockB b; + b.DoB(); + b.DoB(); + } +} + +// Tests mocking template interfaces. + +template +class StackInterface { + public: + virtual ~StackInterface() {} + + // Template parameter appears in function parameter. + virtual void Push(const T& value) = 0; + virtual void Pop() = 0; + virtual int GetSize() const = 0; + // Template parameter appears in function return type. + virtual const T& GetTop() const = 0; +}; + +template +class MockStack : public StackInterface { + public: + MockStack() {} + + MOCK_METHOD1_T(Push, void(const T& elem)); + MOCK_METHOD0_T(Pop, void()); + MOCK_CONST_METHOD0_T(GetSize, int()); // NOLINT + MOCK_CONST_METHOD0_T(GetTop, const T&()); + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(MockStack); +}; + +// Tests that template mock works. +TEST(TemplateMockTest, Works) { + MockStack mock; + + EXPECT_CALL(mock, GetSize()) + .WillOnce(Return(0)) + .WillOnce(Return(1)) + .WillOnce(Return(0)); + EXPECT_CALL(mock, Push(_)); + int n = 5; + EXPECT_CALL(mock, GetTop()) + .WillOnce(ReturnRef(n)); + EXPECT_CALL(mock, Pop()) + .Times(AnyNumber()); + + EXPECT_EQ(0, mock.GetSize()); + mock.Push(5); + EXPECT_EQ(1, mock.GetSize()); + EXPECT_EQ(5, mock.GetTop()); + mock.Pop(); + EXPECT_EQ(0, mock.GetSize()); +} + +#if GTEST_OS_WINDOWS +// Tests mocking template interfaces with calltype. + +template +class StackInterfaceWithCallType { + public: + virtual ~StackInterfaceWithCallType() {} + + // Template parameter appears in function parameter. + STDMETHOD_(void, Push)(const T& value) = 0; + STDMETHOD_(void, Pop)() = 0; + STDMETHOD_(int, GetSize)() const = 0; + // Template parameter appears in function return type. + STDMETHOD_(const T&, GetTop)() const = 0; +}; + +template +class MockStackWithCallType : public StackInterfaceWithCallType { + public: + MockStackWithCallType() {} + + MOCK_METHOD1_T_WITH_CALLTYPE(STDMETHODCALLTYPE, Push, void(const T& elem)); + MOCK_METHOD0_T_WITH_CALLTYPE(STDMETHODCALLTYPE, Pop, void()); + MOCK_CONST_METHOD0_T_WITH_CALLTYPE(STDMETHODCALLTYPE, GetSize, int()); + MOCK_CONST_METHOD0_T_WITH_CALLTYPE(STDMETHODCALLTYPE, GetTop, const T&()); + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(MockStackWithCallType); +}; + +// Tests that template mock with calltype works. +TEST(TemplateMockTestWithCallType, Works) { + MockStackWithCallType mock; + + EXPECT_CALL(mock, GetSize()) + .WillOnce(Return(0)) + .WillOnce(Return(1)) + .WillOnce(Return(0)); + EXPECT_CALL(mock, Push(_)); + int n = 5; + EXPECT_CALL(mock, GetTop()) + .WillOnce(ReturnRef(n)); + EXPECT_CALL(mock, Pop()) + .Times(AnyNumber()); + + EXPECT_EQ(0, mock.GetSize()); + mock.Push(5); + EXPECT_EQ(1, mock.GetSize()); + EXPECT_EQ(5, mock.GetTop()); + mock.Pop(); + EXPECT_EQ(0, mock.GetSize()); +} +#endif // GTEST_OS_WINDOWS + +#define MY_MOCK_METHODS1_ \ + MOCK_METHOD0(Overloaded, void()); \ + MOCK_CONST_METHOD1(Overloaded, int(int n)); \ + MOCK_METHOD2(Overloaded, bool(bool f, int n)) + +class MockOverloadedOnArgNumber { + public: + MockOverloadedOnArgNumber() {} + + MY_MOCK_METHODS1_; + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(MockOverloadedOnArgNumber); +}; + +TEST(OverloadedMockMethodTest, CanOverloadOnArgNumberInMacroBody) { + MockOverloadedOnArgNumber mock; + EXPECT_CALL(mock, Overloaded()); + EXPECT_CALL(mock, Overloaded(1)).WillOnce(Return(2)); + EXPECT_CALL(mock, Overloaded(true, 1)).WillOnce(Return(true)); + + mock.Overloaded(); + EXPECT_EQ(2, mock.Overloaded(1)); + EXPECT_TRUE(mock.Overloaded(true, 1)); +} + +#define MY_MOCK_METHODS2_ \ + MOCK_CONST_METHOD1(Overloaded, int(int n)); \ + MOCK_METHOD1(Overloaded, int(int n)); + +class MockOverloadedOnConstness { + public: + MockOverloadedOnConstness() {} + + MY_MOCK_METHODS2_; + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(MockOverloadedOnConstness); +}; + +TEST(OverloadedMockMethodTest, CanOverloadOnConstnessInMacroBody) { + MockOverloadedOnConstness mock; + const MockOverloadedOnConstness* const_mock = &mock; + EXPECT_CALL(mock, Overloaded(1)).WillOnce(Return(2)); + EXPECT_CALL(*const_mock, Overloaded(1)).WillOnce(Return(3)); + + EXPECT_EQ(2, mock.Overloaded(1)); + EXPECT_EQ(3, const_mock->Overloaded(1)); +} + +TEST(MockFunctionTest, WorksForVoidNullary) { + MockFunction foo; + EXPECT_CALL(foo, Call()); + foo.Call(); +} + +TEST(MockFunctionTest, WorksForNonVoidNullary) { + MockFunction foo; + EXPECT_CALL(foo, Call()) + .WillOnce(Return(1)) + .WillOnce(Return(2)); + EXPECT_EQ(1, foo.Call()); + EXPECT_EQ(2, foo.Call()); +} + +TEST(MockFunctionTest, WorksForVoidUnary) { + MockFunction foo; + EXPECT_CALL(foo, Call(1)); + foo.Call(1); +} + +TEST(MockFunctionTest, WorksForNonVoidBinary) { + MockFunction foo; + EXPECT_CALL(foo, Call(false, 42)) + .WillOnce(Return(1)) + .WillOnce(Return(2)); + EXPECT_CALL(foo, Call(true, Ge(100))) + .WillOnce(Return(3)); + EXPECT_EQ(1, foo.Call(false, 42)); + EXPECT_EQ(2, foo.Call(false, 42)); + EXPECT_EQ(3, foo.Call(true, 120)); +} + +TEST(MockFunctionTest, WorksFor10Arguments) { + MockFunction foo; + EXPECT_CALL(foo, Call(_, 'a', _, _, _, _, _, _, _, _)) + .WillOnce(Return(1)) + .WillOnce(Return(2)); + EXPECT_EQ(1, foo.Call(false, 'a', 0, 0, 0, 0, 0, 'b', 0, true)); + EXPECT_EQ(2, foo.Call(true, 'a', 0, 0, 0, 0, 0, 'b', 1, false)); +} + +} // namespace gmock_generated_function_mockers_test +} // namespace testing diff --git a/libwvdrmengine/test/gmock/test/gmock-generated-internal-utils_test.cc b/libwvdrmengine/test/gmock/test/gmock-generated-internal-utils_test.cc new file mode 100644 index 00000000..1156c7db --- /dev/null +++ b/libwvdrmengine/test/gmock/test/gmock-generated-internal-utils_test.cc @@ -0,0 +1,127 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file tests the internal utilities. + +#include "gmock/internal/gmock-generated-internal-utils.h" +#include "gmock/internal/gmock-internal-utils.h" +#include "gtest/gtest.h" + +namespace { + +using ::std::tr1::tuple; +using ::testing::Matcher; +using ::testing::internal::CompileAssertTypesEqual; +using ::testing::internal::MatcherTuple; +using ::testing::internal::Function; +using ::testing::internal::IgnoredValue; + +// Tests the MatcherTuple template struct. + +TEST(MatcherTupleTest, ForSize0) { + CompileAssertTypesEqual, MatcherTuple >::type>(); +} + +TEST(MatcherTupleTest, ForSize1) { + CompileAssertTypesEqual >, + MatcherTuple >::type>(); +} + +TEST(MatcherTupleTest, ForSize2) { + CompileAssertTypesEqual, Matcher >, + MatcherTuple >::type>(); +} + +TEST(MatcherTupleTest, ForSize5) { + CompileAssertTypesEqual, Matcher, Matcher, + Matcher, Matcher >, + MatcherTuple + >::type>(); +} + +// Tests the Function template struct. + +TEST(FunctionTest, Nullary) { + typedef Function F; // NOLINT + CompileAssertTypesEqual(); + CompileAssertTypesEqual, F::ArgumentTuple>(); + CompileAssertTypesEqual, F::ArgumentMatcherTuple>(); + CompileAssertTypesEqual(); + CompileAssertTypesEqual(); +} + +TEST(FunctionTest, Unary) { + typedef Function F; // NOLINT + CompileAssertTypesEqual(); + CompileAssertTypesEqual(); + CompileAssertTypesEqual, F::ArgumentTuple>(); + CompileAssertTypesEqual >, F::ArgumentMatcherTuple>(); + CompileAssertTypesEqual(); // NOLINT + CompileAssertTypesEqual(); +} + +TEST(FunctionTest, Binary) { + typedef Function F; // NOLINT + CompileAssertTypesEqual(); + CompileAssertTypesEqual(); + CompileAssertTypesEqual(); // NOLINT + CompileAssertTypesEqual, F::ArgumentTuple>(); // NOLINT + CompileAssertTypesEqual, Matcher >, // NOLINT + F::ArgumentMatcherTuple>(); + CompileAssertTypesEqual(); // NOLINT + CompileAssertTypesEqual(); +} + +TEST(FunctionTest, LongArgumentList) { + typedef Function F; // NOLINT + CompileAssertTypesEqual(); + CompileAssertTypesEqual(); + CompileAssertTypesEqual(); + CompileAssertTypesEqual(); + CompileAssertTypesEqual(); + CompileAssertTypesEqual(); // NOLINT + CompileAssertTypesEqual, // NOLINT + F::ArgumentTuple>(); + CompileAssertTypesEqual, Matcher, Matcher, + Matcher, Matcher >, // NOLINT + F::ArgumentMatcherTuple>(); + CompileAssertTypesEqual(); + CompileAssertTypesEqual< + IgnoredValue(bool, int, char*, int&, const long&), // NOLINT + F::MakeResultIgnoredValue>(); +} + +} // Unnamed namespace diff --git a/libwvdrmengine/test/gmock/test/gmock-generated-matchers_test.cc b/libwvdrmengine/test/gmock/test/gmock-generated-matchers_test.cc new file mode 100644 index 00000000..819f1a83 --- /dev/null +++ b/libwvdrmengine/test/gmock/test/gmock-generated-matchers_test.cc @@ -0,0 +1,1127 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Google Mock - a framework for writing C++ mock classes. +// +// This file tests the built-in matchers generated by a script. + +#include "gmock/gmock-generated-matchers.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "gtest/gtest-spi.h" + +namespace { + +using std::list; +using std::map; +using std::pair; +using std::set; +using std::stringstream; +using std::vector; +using std::tr1::get; +using std::tr1::make_tuple; +using std::tr1::tuple; +using testing::_; +using testing::Args; +using testing::Contains; +using testing::ElementsAre; +using testing::ElementsAreArray; +using testing::Eq; +using testing::Ge; +using testing::Gt; +using testing::Lt; +using testing::MakeMatcher; +using testing::Matcher; +using testing::MatcherInterface; +using testing::MatchResultListener; +using testing::Ne; +using testing::Not; +using testing::Pointee; +using testing::PrintToString; +using testing::Ref; +using testing::StaticAssertTypeEq; +using testing::StrEq; +using testing::Value; +using testing::internal::string; + +// Returns the description of the given matcher. +template +string Describe(const Matcher& m) { + stringstream ss; + m.DescribeTo(&ss); + return ss.str(); +} + +// Returns the description of the negation of the given matcher. +template +string DescribeNegation(const Matcher& m) { + stringstream ss; + m.DescribeNegationTo(&ss); + return ss.str(); +} + +// Returns the reason why x matches, or doesn't match, m. +template +string Explain(const MatcherType& m, const Value& x) { + stringstream ss; + m.ExplainMatchResultTo(x, &ss); + return ss.str(); +} + +// Tests Args(m). + +TEST(ArgsTest, AcceptsZeroTemplateArg) { + const tuple t(5, true); + EXPECT_THAT(t, Args<>(Eq(tuple<>()))); + EXPECT_THAT(t, Not(Args<>(Ne(tuple<>())))); +} + +TEST(ArgsTest, AcceptsOneTemplateArg) { + const tuple t(5, true); + EXPECT_THAT(t, Args<0>(Eq(make_tuple(5)))); + EXPECT_THAT(t, Args<1>(Eq(make_tuple(true)))); + EXPECT_THAT(t, Not(Args<1>(Eq(make_tuple(false))))); +} + +TEST(ArgsTest, AcceptsTwoTemplateArgs) { + const tuple t(4, 5, 6L); // NOLINT + + EXPECT_THAT(t, (Args<0, 1>(Lt()))); + EXPECT_THAT(t, (Args<1, 2>(Lt()))); + EXPECT_THAT(t, Not(Args<0, 2>(Gt()))); +} + +TEST(ArgsTest, AcceptsRepeatedTemplateArgs) { + const tuple t(4, 5, 6L); // NOLINT + EXPECT_THAT(t, (Args<0, 0>(Eq()))); + EXPECT_THAT(t, Not(Args<1, 1>(Ne()))); +} + +TEST(ArgsTest, AcceptsDecreasingTemplateArgs) { + const tuple t(4, 5, 6L); // NOLINT + EXPECT_THAT(t, (Args<2, 0>(Gt()))); + EXPECT_THAT(t, Not(Args<2, 1>(Lt()))); +} + +// The MATCHER*() macros trigger warning C4100 (unreferenced formal +// parameter) in MSVC with -W4. Unfortunately they cannot be fixed in +// the macro definition, as the warnings are generated when the macro +// is expanded and macro expansion cannot contain #pragma. Therefore +// we suppress them here. +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable:4100) +#endif + +MATCHER(SumIsZero, "") { + return get<0>(arg) + get<1>(arg) + get<2>(arg) == 0; +} + +TEST(ArgsTest, AcceptsMoreTemplateArgsThanArityOfOriginalTuple) { + EXPECT_THAT(make_tuple(-1, 2), (Args<0, 0, 1>(SumIsZero()))); + EXPECT_THAT(make_tuple(1, 2), Not(Args<0, 0, 1>(SumIsZero()))); +} + +TEST(ArgsTest, CanBeNested) { + const tuple t(4, 5, 6L, 6); // NOLINT + EXPECT_THAT(t, (Args<1, 2, 3>(Args<1, 2>(Eq())))); + EXPECT_THAT(t, (Args<0, 1, 3>(Args<0, 2>(Lt())))); +} + +TEST(ArgsTest, CanMatchTupleByValue) { + typedef tuple Tuple3; + const Matcher m = Args<1, 2>(Lt()); + EXPECT_TRUE(m.Matches(Tuple3('a', 1, 2))); + EXPECT_FALSE(m.Matches(Tuple3('b', 2, 2))); +} + +TEST(ArgsTest, CanMatchTupleByReference) { + typedef tuple Tuple3; + const Matcher m = Args<0, 1>(Lt()); + EXPECT_TRUE(m.Matches(Tuple3('a', 'b', 2))); + EXPECT_FALSE(m.Matches(Tuple3('b', 'b', 2))); +} + +// Validates that arg is printed as str. +MATCHER_P(PrintsAs, str, "") { + return testing::PrintToString(arg) == str; +} + +TEST(ArgsTest, AcceptsTenTemplateArgs) { + EXPECT_THAT(make_tuple(0, 1L, 2, 3L, 4, 5, 6, 7, 8, 9), + (Args<9, 8, 7, 6, 5, 4, 3, 2, 1, 0>( + PrintsAs("(9, 8, 7, 6, 5, 4, 3, 2, 1, 0)")))); + EXPECT_THAT(make_tuple(0, 1L, 2, 3L, 4, 5, 6, 7, 8, 9), + Not(Args<9, 8, 7, 6, 5, 4, 3, 2, 1, 0>( + PrintsAs("(0, 8, 7, 6, 5, 4, 3, 2, 1, 0)")))); +} + +TEST(ArgsTest, DescirbesSelfCorrectly) { + const Matcher > m = Args<2, 0>(Lt()); + EXPECT_EQ("are a tuple whose fields (#2, #0) are a pair where " + "the first < the second", + Describe(m)); +} + +TEST(ArgsTest, DescirbesNestedArgsCorrectly) { + const Matcher&> m = + Args<0, 2, 3>(Args<2, 0>(Lt())); + EXPECT_EQ("are a tuple whose fields (#0, #2, #3) are a tuple " + "whose fields (#2, #0) are a pair where the first < the second", + Describe(m)); +} + +TEST(ArgsTest, DescribesNegationCorrectly) { + const Matcher > m = Args<1, 0>(Gt()); + EXPECT_EQ("are a tuple whose fields (#1, #0) aren't a pair " + "where the first > the second", + DescribeNegation(m)); +} + +TEST(ArgsTest, ExplainsMatchResultWithoutInnerExplanation) { + const Matcher > m = Args<1, 2>(Eq()); + EXPECT_EQ("whose fields (#1, #2) are (42, 42)", + Explain(m, make_tuple(false, 42, 42))); + EXPECT_EQ("whose fields (#1, #2) are (42, 43)", + Explain(m, make_tuple(false, 42, 43))); +} + +// For testing Args<>'s explanation. +class LessThanMatcher : public MatcherInterface > { + public: + virtual void DescribeTo(::std::ostream* os) const {} + + virtual bool MatchAndExplain(tuple value, + MatchResultListener* listener) const { + const int diff = get<0>(value) - get<1>(value); + if (diff > 0) { + *listener << "where the first value is " << diff + << " more than the second"; + } + return diff < 0; + } +}; + +Matcher > LessThan() { + return MakeMatcher(new LessThanMatcher); +} + +TEST(ArgsTest, ExplainsMatchResultWithInnerExplanation) { + const Matcher > m = Args<0, 2>(LessThan()); + EXPECT_EQ("whose fields (#0, #2) are ('a' (97, 0x61), 42), " + "where the first value is 55 more than the second", + Explain(m, make_tuple('a', 42, 42))); + EXPECT_EQ("whose fields (#0, #2) are ('\\0', 43)", + Explain(m, make_tuple('\0', 42, 43))); +} + +// For testing ExplainMatchResultTo(). +class GreaterThanMatcher : public MatcherInterface { + public: + explicit GreaterThanMatcher(int rhs) : rhs_(rhs) {} + + virtual void DescribeTo(::std::ostream* os) const { + *os << "is greater than " << rhs_; + } + + virtual bool MatchAndExplain(int lhs, + MatchResultListener* listener) const { + const int diff = lhs - rhs_; + if (diff > 0) { + *listener << "which is " << diff << " more than " << rhs_; + } else if (diff == 0) { + *listener << "which is the same as " << rhs_; + } else { + *listener << "which is " << -diff << " less than " << rhs_; + } + + return lhs > rhs_; + } + + private: + int rhs_; +}; + +Matcher GreaterThan(int n) { + return MakeMatcher(new GreaterThanMatcher(n)); +} + +// Tests for ElementsAre(). + +// Evaluates to the number of elements in 'array'. +#define GMOCK_ARRAY_SIZE_(array) (sizeof(array)/sizeof(array[0])) + +TEST(ElementsAreTest, CanDescribeExpectingNoElement) { + Matcher&> m = ElementsAre(); + EXPECT_EQ("is empty", Describe(m)); +} + +TEST(ElementsAreTest, CanDescribeExpectingOneElement) { + Matcher > m = ElementsAre(Gt(5)); + EXPECT_EQ("has 1 element that is > 5", Describe(m)); +} + +TEST(ElementsAreTest, CanDescribeExpectingManyElements) { + Matcher > m = ElementsAre(StrEq("one"), "two"); + EXPECT_EQ("has 2 elements where\n" + "element #0 is equal to \"one\",\n" + "element #1 is equal to \"two\"", Describe(m)); +} + +TEST(ElementsAreTest, CanDescribeNegationOfExpectingNoElement) { + Matcher > m = ElementsAre(); + EXPECT_EQ("isn't empty", DescribeNegation(m)); +} + +TEST(ElementsAreTest, CanDescribeNegationOfExpectingOneElment) { + Matcher& > m = ElementsAre(Gt(5)); + EXPECT_EQ("doesn't have 1 element, or\n" + "element #0 isn't > 5", DescribeNegation(m)); +} + +TEST(ElementsAreTest, CanDescribeNegationOfExpectingManyElements) { + Matcher& > m = ElementsAre("one", "two"); + EXPECT_EQ("doesn't have 2 elements, or\n" + "element #0 isn't equal to \"one\", or\n" + "element #1 isn't equal to \"two\"", DescribeNegation(m)); +} + +TEST(ElementsAreTest, DoesNotExplainTrivialMatch) { + Matcher& > m = ElementsAre(1, Ne(2)); + + list test_list; + test_list.push_back(1); + test_list.push_back(3); + EXPECT_EQ("", Explain(m, test_list)); // No need to explain anything. +} + +TEST(ElementsAreTest, ExplainsNonTrivialMatch) { + Matcher& > m = + ElementsAre(GreaterThan(1), 0, GreaterThan(2)); + + const int a[] = { 10, 0, 100 }; + vector test_vector(a, a + GMOCK_ARRAY_SIZE_(a)); + EXPECT_EQ("whose element #0 matches, which is 9 more than 1,\n" + "and whose element #2 matches, which is 98 more than 2", + Explain(m, test_vector)); +} + +TEST(ElementsAreTest, CanExplainMismatchWrongSize) { + Matcher& > m = ElementsAre(1, 3); + + list test_list; + // No need to explain when the container is empty. + EXPECT_EQ("", Explain(m, test_list)); + + test_list.push_back(1); + EXPECT_EQ("which has 1 element", Explain(m, test_list)); +} + +TEST(ElementsAreTest, CanExplainMismatchRightSize) { + Matcher& > m = ElementsAre(1, GreaterThan(5)); + + vector v; + v.push_back(2); + v.push_back(1); + EXPECT_EQ("whose element #0 doesn't match", Explain(m, v)); + + v[0] = 1; + EXPECT_EQ("whose element #1 doesn't match, which is 4 less than 5", + Explain(m, v)); +} + +TEST(ElementsAreTest, MatchesOneElementVector) { + vector test_vector; + test_vector.push_back("test string"); + + EXPECT_THAT(test_vector, ElementsAre(StrEq("test string"))); +} + +TEST(ElementsAreTest, MatchesOneElementList) { + list test_list; + test_list.push_back("test string"); + + EXPECT_THAT(test_list, ElementsAre("test string")); +} + +TEST(ElementsAreTest, MatchesThreeElementVector) { + vector test_vector; + test_vector.push_back("one"); + test_vector.push_back("two"); + test_vector.push_back("three"); + + EXPECT_THAT(test_vector, ElementsAre("one", StrEq("two"), _)); +} + +TEST(ElementsAreTest, MatchesOneElementEqMatcher) { + vector test_vector; + test_vector.push_back(4); + + EXPECT_THAT(test_vector, ElementsAre(Eq(4))); +} + +TEST(ElementsAreTest, MatchesOneElementAnyMatcher) { + vector test_vector; + test_vector.push_back(4); + + EXPECT_THAT(test_vector, ElementsAre(_)); +} + +TEST(ElementsAreTest, MatchesOneElementValue) { + vector test_vector; + test_vector.push_back(4); + + EXPECT_THAT(test_vector, ElementsAre(4)); +} + +TEST(ElementsAreTest, MatchesThreeElementsMixedMatchers) { + vector test_vector; + test_vector.push_back(1); + test_vector.push_back(2); + test_vector.push_back(3); + + EXPECT_THAT(test_vector, ElementsAre(1, Eq(2), _)); +} + +TEST(ElementsAreTest, MatchesTenElementVector) { + const int a[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + vector test_vector(a, a + GMOCK_ARRAY_SIZE_(a)); + + EXPECT_THAT(test_vector, + // The element list can contain values and/or matchers + // of different types. + ElementsAre(0, Ge(0), _, 3, 4, Ne(2), Eq(6), 7, 8, _)); +} + +TEST(ElementsAreTest, DoesNotMatchWrongSize) { + vector test_vector; + test_vector.push_back("test string"); + test_vector.push_back("test string"); + + Matcher > m = ElementsAre(StrEq("test string")); + EXPECT_FALSE(m.Matches(test_vector)); +} + +TEST(ElementsAreTest, DoesNotMatchWrongValue) { + vector test_vector; + test_vector.push_back("other string"); + + Matcher > m = ElementsAre(StrEq("test string")); + EXPECT_FALSE(m.Matches(test_vector)); +} + +TEST(ElementsAreTest, DoesNotMatchWrongOrder) { + vector test_vector; + test_vector.push_back("one"); + test_vector.push_back("three"); + test_vector.push_back("two"); + + Matcher > m = ElementsAre( + StrEq("one"), StrEq("two"), StrEq("three")); + EXPECT_FALSE(m.Matches(test_vector)); +} + +TEST(ElementsAreTest, WorksForNestedContainer) { + const char* strings[] = { + "Hi", + "world" + }; + + vector > nested; + for (size_t i = 0; i < GMOCK_ARRAY_SIZE_(strings); i++) { + nested.push_back(list(strings[i], strings[i] + strlen(strings[i]))); + } + + EXPECT_THAT(nested, ElementsAre(ElementsAre('H', Ne('e')), + ElementsAre('w', 'o', _, _, 'd'))); + EXPECT_THAT(nested, Not(ElementsAre(ElementsAre('H', 'e'), + ElementsAre('w', 'o', _, _, 'd')))); +} + +TEST(ElementsAreTest, WorksWithByRefElementMatchers) { + int a[] = { 0, 1, 2 }; + vector v(a, a + GMOCK_ARRAY_SIZE_(a)); + + EXPECT_THAT(v, ElementsAre(Ref(v[0]), Ref(v[1]), Ref(v[2]))); + EXPECT_THAT(v, Not(ElementsAre(Ref(v[0]), Ref(v[1]), Ref(a[2])))); +} + +TEST(ElementsAreTest, WorksWithContainerPointerUsingPointee) { + int a[] = { 0, 1, 2 }; + vector v(a, a + GMOCK_ARRAY_SIZE_(a)); + + EXPECT_THAT(&v, Pointee(ElementsAre(0, 1, _))); + EXPECT_THAT(&v, Not(Pointee(ElementsAre(0, _, 3)))); +} + +TEST(ElementsAreTest, WorksWithNativeArrayPassedByReference) { + int array[] = { 0, 1, 2 }; + EXPECT_THAT(array, ElementsAre(0, 1, _)); + EXPECT_THAT(array, Not(ElementsAre(1, _, _))); + EXPECT_THAT(array, Not(ElementsAre(0, _))); +} + +class NativeArrayPassedAsPointerAndSize { + public: + NativeArrayPassedAsPointerAndSize() {} + + MOCK_METHOD2(Helper, void(int* array, int size)); + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(NativeArrayPassedAsPointerAndSize); +}; + +TEST(ElementsAreTest, WorksWithNativeArrayPassedAsPointerAndSize) { + int array[] = { 0, 1 }; + ::std::tr1::tuple array_as_tuple(array, 2); + EXPECT_THAT(array_as_tuple, ElementsAre(0, 1)); + EXPECT_THAT(array_as_tuple, Not(ElementsAre(0))); + + NativeArrayPassedAsPointerAndSize helper; + EXPECT_CALL(helper, Helper(_, _)) + .With(ElementsAre(0, 1)); + helper.Helper(array, 2); +} + +TEST(ElementsAreTest, WorksWithTwoDimensionalNativeArray) { + const char a2[][3] = { "hi", "lo" }; + EXPECT_THAT(a2, ElementsAre(ElementsAre('h', 'i', '\0'), + ElementsAre('l', 'o', '\0'))); + EXPECT_THAT(a2, ElementsAre(StrEq("hi"), StrEq("lo"))); + EXPECT_THAT(a2, ElementsAre(Not(ElementsAre('h', 'o', '\0')), + ElementsAre('l', 'o', '\0'))); +} + +// Tests for ElementsAreArray(). Since ElementsAreArray() shares most +// of the implementation with ElementsAre(), we don't test it as +// thoroughly here. + +TEST(ElementsAreArrayTest, CanBeCreatedWithValueArray) { + const int a[] = { 1, 2, 3 }; + + vector test_vector(a, a + GMOCK_ARRAY_SIZE_(a)); + EXPECT_THAT(test_vector, ElementsAreArray(a)); + + test_vector[2] = 0; + EXPECT_THAT(test_vector, Not(ElementsAreArray(a))); +} + +TEST(ElementsAreArrayTest, CanBeCreatedWithArraySize) { + const char* a[] = { "one", "two", "three" }; + + vector test_vector(a, a + GMOCK_ARRAY_SIZE_(a)); + EXPECT_THAT(test_vector, ElementsAreArray(a, GMOCK_ARRAY_SIZE_(a))); + + const char** p = a; + test_vector[0] = "1"; + EXPECT_THAT(test_vector, Not(ElementsAreArray(p, GMOCK_ARRAY_SIZE_(a)))); +} + +TEST(ElementsAreArrayTest, CanBeCreatedWithoutArraySize) { + const char* a[] = { "one", "two", "three" }; + + vector test_vector(a, a + GMOCK_ARRAY_SIZE_(a)); + EXPECT_THAT(test_vector, ElementsAreArray(a)); + + test_vector[0] = "1"; + EXPECT_THAT(test_vector, Not(ElementsAreArray(a))); +} + +TEST(ElementsAreArrayTest, CanBeCreatedWithMatcherArray) { + const Matcher kMatcherArray[] = + { StrEq("one"), StrEq("two"), StrEq("three") }; + + vector test_vector; + test_vector.push_back("one"); + test_vector.push_back("two"); + test_vector.push_back("three"); + EXPECT_THAT(test_vector, ElementsAreArray(kMatcherArray)); + + test_vector.push_back("three"); + EXPECT_THAT(test_vector, Not(ElementsAreArray(kMatcherArray))); +} + +// Since ElementsAre() and ElementsAreArray() share much of the +// implementation, we only do a sanity test for native arrays here. +TEST(ElementsAreArrayTest, WorksWithNativeArray) { + ::std::string a[] = { "hi", "ho" }; + ::std::string b[] = { "hi", "ho" }; + + EXPECT_THAT(a, ElementsAreArray(b)); + EXPECT_THAT(a, ElementsAreArray(b, 2)); + EXPECT_THAT(a, Not(ElementsAreArray(b, 1))); +} + +// Tests for the MATCHER*() macro family. + +// Tests that a simple MATCHER() definition works. + +MATCHER(IsEven, "") { return (arg % 2) == 0; } + +TEST(MatcherMacroTest, Works) { + const Matcher m = IsEven(); + EXPECT_TRUE(m.Matches(6)); + EXPECT_FALSE(m.Matches(7)); + + EXPECT_EQ("is even", Describe(m)); + EXPECT_EQ("not (is even)", DescribeNegation(m)); + EXPECT_EQ("", Explain(m, 6)); + EXPECT_EQ("", Explain(m, 7)); +} + +// This also tests that the description string can reference 'negation'. +MATCHER(IsEven2, negation ? "is odd" : "is even") { + if ((arg % 2) == 0) { + // Verifies that we can stream to result_listener, a listener + // supplied by the MATCHER macro implicitly. + *result_listener << "OK"; + return true; + } else { + *result_listener << "% 2 == " << (arg % 2); + return false; + } +} + +// This also tests that the description string can reference matcher +// parameters. +MATCHER_P2(EqSumOf, x, y, + string(negation ? "doesn't equal" : "equals") + " the sum of " + + PrintToString(x) + " and " + PrintToString(y)) { + if (arg == (x + y)) { + *result_listener << "OK"; + return true; + } else { + // Verifies that we can stream to the underlying stream of + // result_listener. + if (result_listener->stream() != NULL) { + *result_listener->stream() << "diff == " << (x + y - arg); + } + return false; + } +} + +// Tests that the matcher description can reference 'negation' and the +// matcher parameters. +TEST(MatcherMacroTest, DescriptionCanReferenceNegationAndParameters) { + const Matcher m1 = IsEven2(); + EXPECT_EQ("is even", Describe(m1)); + EXPECT_EQ("is odd", DescribeNegation(m1)); + + const Matcher m2 = EqSumOf(5, 9); + EXPECT_EQ("equals the sum of 5 and 9", Describe(m2)); + EXPECT_EQ("doesn't equal the sum of 5 and 9", DescribeNegation(m2)); +} + +// Tests explaining match result in a MATCHER* macro. +TEST(MatcherMacroTest, CanExplainMatchResult) { + const Matcher m1 = IsEven2(); + EXPECT_EQ("OK", Explain(m1, 4)); + EXPECT_EQ("% 2 == 1", Explain(m1, 5)); + + const Matcher m2 = EqSumOf(1, 2); + EXPECT_EQ("OK", Explain(m2, 3)); + EXPECT_EQ("diff == -1", Explain(m2, 4)); +} + +// Tests that the body of MATCHER() can reference the type of the +// value being matched. + +MATCHER(IsEmptyString, "") { + StaticAssertTypeEq< ::std::string, arg_type>(); + return arg == ""; +} + +MATCHER(IsEmptyStringByRef, "") { + StaticAssertTypeEq(); + return arg == ""; +} + +TEST(MatcherMacroTest, CanReferenceArgType) { + const Matcher< ::std::string> m1 = IsEmptyString(); + EXPECT_TRUE(m1.Matches("")); + + const Matcher m2 = IsEmptyStringByRef(); + EXPECT_TRUE(m2.Matches("")); +} + +// Tests that MATCHER() can be used in a namespace. + +namespace matcher_test { +MATCHER(IsOdd, "") { return (arg % 2) != 0; } +} // namespace matcher_test + +TEST(MatcherMacroTest, WorksInNamespace) { + Matcher m = matcher_test::IsOdd(); + EXPECT_FALSE(m.Matches(4)); + EXPECT_TRUE(m.Matches(5)); +} + +// Tests that Value() can be used to compose matchers. +MATCHER(IsPositiveOdd, "") { + return Value(arg, matcher_test::IsOdd()) && arg > 0; +} + +TEST(MatcherMacroTest, CanBeComposedUsingValue) { + EXPECT_THAT(3, IsPositiveOdd()); + EXPECT_THAT(4, Not(IsPositiveOdd())); + EXPECT_THAT(-1, Not(IsPositiveOdd())); +} + +// Tests that a simple MATCHER_P() definition works. + +MATCHER_P(IsGreaterThan32And, n, "") { return arg > 32 && arg > n; } + +TEST(MatcherPMacroTest, Works) { + const Matcher m = IsGreaterThan32And(5); + EXPECT_TRUE(m.Matches(36)); + EXPECT_FALSE(m.Matches(5)); + + EXPECT_EQ("is greater than 32 and 5", Describe(m)); + EXPECT_EQ("not (is greater than 32 and 5)", DescribeNegation(m)); + EXPECT_EQ("", Explain(m, 36)); + EXPECT_EQ("", Explain(m, 5)); +} + +// Tests that the description is calculated correctly from the matcher name. +MATCHER_P(_is_Greater_Than32and_, n, "") { return arg > 32 && arg > n; } + +TEST(MatcherPMacroTest, GeneratesCorrectDescription) { + const Matcher m = _is_Greater_Than32and_(5); + + EXPECT_EQ("is greater than 32 and 5", Describe(m)); + EXPECT_EQ("not (is greater than 32 and 5)", DescribeNegation(m)); + EXPECT_EQ("", Explain(m, 36)); + EXPECT_EQ("", Explain(m, 5)); +} + +// Tests that a MATCHER_P matcher can be explicitly instantiated with +// a reference parameter type. + +class UncopyableFoo { + public: + explicit UncopyableFoo(char value) : value_(value) {} + private: + UncopyableFoo(const UncopyableFoo&); + void operator=(const UncopyableFoo&); + + char value_; +}; + +MATCHER_P(ReferencesUncopyable, variable, "") { return &arg == &variable; } + +TEST(MatcherPMacroTest, WorksWhenExplicitlyInstantiatedWithReference) { + UncopyableFoo foo1('1'), foo2('2'); + const Matcher m = + ReferencesUncopyable(foo1); + + EXPECT_TRUE(m.Matches(foo1)); + EXPECT_FALSE(m.Matches(foo2)); + + // We don't want the address of the parameter printed, as most + // likely it will just annoy the user. If the address is + // interesting, the user should consider passing the parameter by + // pointer instead. + EXPECT_EQ("references uncopyable 1-byte object <31>", Describe(m)); +} + + +// Tests that the body of MATCHER_Pn() can reference the parameter +// types. + +MATCHER_P3(ParamTypesAreIntLongAndChar, foo, bar, baz, "") { + StaticAssertTypeEq(); + StaticAssertTypeEq(); // NOLINT + StaticAssertTypeEq(); + return arg == 0; +} + +TEST(MatcherPnMacroTest, CanReferenceParamTypes) { + EXPECT_THAT(0, ParamTypesAreIntLongAndChar(10, 20L, 'a')); +} + +// Tests that a MATCHER_Pn matcher can be explicitly instantiated with +// reference parameter types. + +MATCHER_P2(ReferencesAnyOf, variable1, variable2, "") { + return &arg == &variable1 || &arg == &variable2; +} + +TEST(MatcherPnMacroTest, WorksWhenExplicitlyInstantiatedWithReferences) { + UncopyableFoo foo1('1'), foo2('2'), foo3('3'); + const Matcher m = + ReferencesAnyOf(foo1, foo2); + + EXPECT_TRUE(m.Matches(foo1)); + EXPECT_TRUE(m.Matches(foo2)); + EXPECT_FALSE(m.Matches(foo3)); +} + +TEST(MatcherPnMacroTest, + GeneratesCorretDescriptionWhenExplicitlyInstantiatedWithReferences) { + UncopyableFoo foo1('1'), foo2('2'); + const Matcher m = + ReferencesAnyOf(foo1, foo2); + + // We don't want the addresses of the parameters printed, as most + // likely they will just annoy the user. If the addresses are + // interesting, the user should consider passing the parameters by + // pointers instead. + EXPECT_EQ("references any of (1-byte object <31>, 1-byte object <32>)", + Describe(m)); +} + +// Tests that a simple MATCHER_P2() definition works. + +MATCHER_P2(IsNotInClosedRange, low, hi, "") { return arg < low || arg > hi; } + +TEST(MatcherPnMacroTest, Works) { + const Matcher m = IsNotInClosedRange(10, 20); // NOLINT + EXPECT_TRUE(m.Matches(36L)); + EXPECT_FALSE(m.Matches(15L)); + + EXPECT_EQ("is not in closed range (10, 20)", Describe(m)); + EXPECT_EQ("not (is not in closed range (10, 20))", DescribeNegation(m)); + EXPECT_EQ("", Explain(m, 36L)); + EXPECT_EQ("", Explain(m, 15L)); +} + +// Tests that MATCHER*() definitions can be overloaded on the number +// of parameters; also tests MATCHER_Pn() where n >= 3. + +MATCHER(EqualsSumOf, "") { return arg == 0; } +MATCHER_P(EqualsSumOf, a, "") { return arg == a; } +MATCHER_P2(EqualsSumOf, a, b, "") { return arg == a + b; } +MATCHER_P3(EqualsSumOf, a, b, c, "") { return arg == a + b + c; } +MATCHER_P4(EqualsSumOf, a, b, c, d, "") { return arg == a + b + c + d; } +MATCHER_P5(EqualsSumOf, a, b, c, d, e, "") { return arg == a + b + c + d + e; } +MATCHER_P6(EqualsSumOf, a, b, c, d, e, f, "") { + return arg == a + b + c + d + e + f; +} +MATCHER_P7(EqualsSumOf, a, b, c, d, e, f, g, "") { + return arg == a + b + c + d + e + f + g; +} +MATCHER_P8(EqualsSumOf, a, b, c, d, e, f, g, h, "") { + return arg == a + b + c + d + e + f + g + h; +} +MATCHER_P9(EqualsSumOf, a, b, c, d, e, f, g, h, i, "") { + return arg == a + b + c + d + e + f + g + h + i; +} +MATCHER_P10(EqualsSumOf, a, b, c, d, e, f, g, h, i, j, "") { + return arg == a + b + c + d + e + f + g + h + i + j; +} + +TEST(MatcherPnMacroTest, CanBeOverloadedOnNumberOfParameters) { + EXPECT_THAT(0, EqualsSumOf()); + EXPECT_THAT(1, EqualsSumOf(1)); + EXPECT_THAT(12, EqualsSumOf(10, 2)); + EXPECT_THAT(123, EqualsSumOf(100, 20, 3)); + EXPECT_THAT(1234, EqualsSumOf(1000, 200, 30, 4)); + EXPECT_THAT(12345, EqualsSumOf(10000, 2000, 300, 40, 5)); + EXPECT_THAT("abcdef", + EqualsSumOf(::std::string("a"), 'b', 'c', "d", "e", 'f')); + EXPECT_THAT("abcdefg", + EqualsSumOf(::std::string("a"), 'b', 'c', "d", "e", 'f', 'g')); + EXPECT_THAT("abcdefgh", + EqualsSumOf(::std::string("a"), 'b', 'c', "d", "e", 'f', 'g', + "h")); + EXPECT_THAT("abcdefghi", + EqualsSumOf(::std::string("a"), 'b', 'c', "d", "e", 'f', 'g', + "h", 'i')); + EXPECT_THAT("abcdefghij", + EqualsSumOf(::std::string("a"), 'b', 'c', "d", "e", 'f', 'g', + "h", 'i', ::std::string("j"))); + + EXPECT_THAT(1, Not(EqualsSumOf())); + EXPECT_THAT(-1, Not(EqualsSumOf(1))); + EXPECT_THAT(-12, Not(EqualsSumOf(10, 2))); + EXPECT_THAT(-123, Not(EqualsSumOf(100, 20, 3))); + EXPECT_THAT(-1234, Not(EqualsSumOf(1000, 200, 30, 4))); + EXPECT_THAT(-12345, Not(EqualsSumOf(10000, 2000, 300, 40, 5))); + EXPECT_THAT("abcdef ", + Not(EqualsSumOf(::std::string("a"), 'b', 'c', "d", "e", 'f'))); + EXPECT_THAT("abcdefg ", + Not(EqualsSumOf(::std::string("a"), 'b', 'c', "d", "e", 'f', + 'g'))); + EXPECT_THAT("abcdefgh ", + Not(EqualsSumOf(::std::string("a"), 'b', 'c', "d", "e", 'f', 'g', + "h"))); + EXPECT_THAT("abcdefghi ", + Not(EqualsSumOf(::std::string("a"), 'b', 'c', "d", "e", 'f', 'g', + "h", 'i'))); + EXPECT_THAT("abcdefghij ", + Not(EqualsSumOf(::std::string("a"), 'b', 'c', "d", "e", 'f', 'g', + "h", 'i', ::std::string("j")))); +} + +// Tests that a MATCHER_Pn() definition can be instantiated with any +// compatible parameter types. +TEST(MatcherPnMacroTest, WorksForDifferentParameterTypes) { + EXPECT_THAT(123, EqualsSumOf(100L, 20, static_cast(3))); + EXPECT_THAT("abcd", EqualsSumOf(::std::string("a"), "b", 'c', "d")); + + EXPECT_THAT(124, Not(EqualsSumOf(100L, 20, static_cast(3)))); + EXPECT_THAT("abcde", Not(EqualsSumOf(::std::string("a"), "b", 'c', "d"))); +} + +// Tests that the matcher body can promote the parameter types. + +MATCHER_P2(EqConcat, prefix, suffix, "") { + // The following lines promote the two parameters to desired types. + std::string prefix_str(prefix); + char suffix_char = static_cast(suffix); + return arg == prefix_str + suffix_char; +} + +TEST(MatcherPnMacroTest, SimpleTypePromotion) { + Matcher no_promo = + EqConcat(std::string("foo"), 't'); + Matcher promo = + EqConcat("foo", static_cast('t')); + EXPECT_FALSE(no_promo.Matches("fool")); + EXPECT_FALSE(promo.Matches("fool")); + EXPECT_TRUE(no_promo.Matches("foot")); + EXPECT_TRUE(promo.Matches("foot")); +} + +// Verifies the type of a MATCHER*. + +TEST(MatcherPnMacroTest, TypesAreCorrect) { + // EqualsSumOf() must be assignable to a EqualsSumOfMatcher variable. + EqualsSumOfMatcher a0 = EqualsSumOf(); + + // EqualsSumOf(1) must be assignable to a EqualsSumOfMatcherP variable. + EqualsSumOfMatcherP a1 = EqualsSumOf(1); + + // EqualsSumOf(p1, ..., pk) must be assignable to a EqualsSumOfMatcherPk + // variable, and so on. + EqualsSumOfMatcherP2 a2 = EqualsSumOf(1, '2'); + EqualsSumOfMatcherP3 a3 = EqualsSumOf(1, 2, '3'); + EqualsSumOfMatcherP4 a4 = EqualsSumOf(1, 2, 3, '4'); + EqualsSumOfMatcherP5 a5 = + EqualsSumOf(1, 2, 3, 4, '5'); + EqualsSumOfMatcherP6 a6 = + EqualsSumOf(1, 2, 3, 4, 5, '6'); + EqualsSumOfMatcherP7 a7 = + EqualsSumOf(1, 2, 3, 4, 5, 6, '7'); + EqualsSumOfMatcherP8 a8 = + EqualsSumOf(1, 2, 3, 4, 5, 6, 7, '8'); + EqualsSumOfMatcherP9 a9 = + EqualsSumOf(1, 2, 3, 4, 5, 6, 7, 8, '9'); + EqualsSumOfMatcherP10 a10 = + EqualsSumOf(1, 2, 3, 4, 5, 6, 7, 8, 9, '0'); +} + +// Tests that matcher-typed parameters can be used in Value() inside a +// MATCHER_Pn definition. + +// Succeeds if arg matches exactly 2 of the 3 matchers. +MATCHER_P3(TwoOf, m1, m2, m3, "") { + const int count = static_cast(Value(arg, m1)) + + static_cast(Value(arg, m2)) + static_cast(Value(arg, m3)); + return count == 2; +} + +TEST(MatcherPnMacroTest, CanUseMatcherTypedParameterInValue) { + EXPECT_THAT(42, TwoOf(Gt(0), Lt(50), Eq(10))); + EXPECT_THAT(0, Not(TwoOf(Gt(-1), Lt(1), Eq(0)))); +} + +// Tests Contains(). + +TEST(ContainsTest, ListMatchesWhenElementIsInContainer) { + list some_list; + some_list.push_back(3); + some_list.push_back(1); + some_list.push_back(2); + EXPECT_THAT(some_list, Contains(1)); + EXPECT_THAT(some_list, Contains(Gt(2.5))); + EXPECT_THAT(some_list, Contains(Eq(2.0f))); + + list another_list; + another_list.push_back("fee"); + another_list.push_back("fie"); + another_list.push_back("foe"); + another_list.push_back("fum"); + EXPECT_THAT(another_list, Contains(string("fee"))); +} + +TEST(ContainsTest, ListDoesNotMatchWhenElementIsNotInContainer) { + list some_list; + some_list.push_back(3); + some_list.push_back(1); + EXPECT_THAT(some_list, Not(Contains(4))); +} + +TEST(ContainsTest, SetMatchesWhenElementIsInContainer) { + set some_set; + some_set.insert(3); + some_set.insert(1); + some_set.insert(2); + EXPECT_THAT(some_set, Contains(Eq(1.0))); + EXPECT_THAT(some_set, Contains(Eq(3.0f))); + EXPECT_THAT(some_set, Contains(2)); + + set another_set; + another_set.insert("fee"); + another_set.insert("fie"); + another_set.insert("foe"); + another_set.insert("fum"); + EXPECT_THAT(another_set, Contains(Eq(string("fum")))); +} + +TEST(ContainsTest, SetDoesNotMatchWhenElementIsNotInContainer) { + set some_set; + some_set.insert(3); + some_set.insert(1); + EXPECT_THAT(some_set, Not(Contains(4))); + + set c_string_set; + c_string_set.insert("hello"); + EXPECT_THAT(c_string_set, Not(Contains(string("hello").c_str()))); +} + +TEST(ContainsTest, ExplainsMatchResultCorrectly) { + const int a[2] = { 1, 2 }; + Matcher m = Contains(2); + EXPECT_EQ("whose element #1 matches", Explain(m, a)); + + m = Contains(3); + EXPECT_EQ("", Explain(m, a)); + + m = Contains(GreaterThan(0)); + EXPECT_EQ("whose element #0 matches, which is 1 more than 0", Explain(m, a)); + + m = Contains(GreaterThan(10)); + EXPECT_EQ("", Explain(m, a)); +} + +TEST(ContainsTest, DescribesItselfCorrectly) { + Matcher > m = Contains(1); + EXPECT_EQ("contains at least one element that is equal to 1", Describe(m)); + + Matcher > m2 = Not(m); + EXPECT_EQ("doesn't contain any element that is equal to 1", Describe(m2)); +} + +TEST(ContainsTest, MapMatchesWhenElementIsInContainer) { + map my_map; + const char* bar = "a string"; + my_map[bar] = 2; + EXPECT_THAT(my_map, Contains(pair(bar, 2))); + + map another_map; + another_map["fee"] = 1; + another_map["fie"] = 2; + another_map["foe"] = 3; + another_map["fum"] = 4; + EXPECT_THAT(another_map, Contains(pair(string("fee"), 1))); + EXPECT_THAT(another_map, Contains(pair("fie", 2))); +} + +TEST(ContainsTest, MapDoesNotMatchWhenElementIsNotInContainer) { + map some_map; + some_map[1] = 11; + some_map[2] = 22; + EXPECT_THAT(some_map, Not(Contains(pair(2, 23)))); +} + +TEST(ContainsTest, ArrayMatchesWhenElementIsInContainer) { + const char* string_array[] = { "fee", "fie", "foe", "fum" }; + EXPECT_THAT(string_array, Contains(Eq(string("fum")))); +} + +TEST(ContainsTest, ArrayDoesNotMatchWhenElementIsNotInContainer) { + int int_array[] = { 1, 2, 3, 4 }; + EXPECT_THAT(int_array, Not(Contains(5))); +} + +TEST(ContainsTest, AcceptsMatcher) { + const int a[] = { 1, 2, 3 }; + EXPECT_THAT(a, Contains(Gt(2))); + EXPECT_THAT(a, Not(Contains(Gt(4)))); +} + +TEST(ContainsTest, WorksForNativeArrayAsTuple) { + const int a[] = { 1, 2 }; + const int* const pointer = a; + EXPECT_THAT(make_tuple(pointer, 2), Contains(1)); + EXPECT_THAT(make_tuple(pointer, 2), Not(Contains(Gt(3)))); +} + +TEST(ContainsTest, WorksForTwoDimensionalNativeArray) { + int a[][3] = { { 1, 2, 3 }, { 4, 5, 6 } }; + EXPECT_THAT(a, Contains(ElementsAre(4, 5, 6))); + EXPECT_THAT(a, Contains(Contains(5))); + EXPECT_THAT(a, Not(Contains(ElementsAre(3, 4, 5)))); + EXPECT_THAT(a, Contains(Not(Contains(5)))); +} + +namespace adl_test { + +// Verifies that the implementation of ::testing::AllOf and ::testing::AnyOf +// don't issue unqualified recursive calls. If they do, the argument dependent +// name lookup will cause AllOf/AnyOf in the 'adl_test' namespace to be found +// as a candidate and the compilation will break due to an ambiguous overload. + +// The matcher must be in the same namespace as AllOf/AnyOf to make argument +// dependent lookup find those. +MATCHER(M, "") { return true; } + +template +bool AllOf(const T1& t1, const T2& t2) { return true; } + +TEST(AllOfTest, DoesNotCallAllOfUnqualified) { + EXPECT_THAT(42, testing::AllOf( + M(), M(), M(), M(), M(), M(), M(), M(), M(), M())); +} + +template bool +AnyOf(const T1& t1, const T2& t2) { return true; } + +TEST(AnyOfTest, DoesNotCallAnyOfUnqualified) { + EXPECT_THAT(42, testing::AnyOf( + M(), M(), M(), M(), M(), M(), M(), M(), M(), M())); +} + +} // namespace adl_test + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + +} // namespace diff --git a/libwvdrmengine/test/gmock/test/gmock-internal-utils_test.cc b/libwvdrmengine/test/gmock/test/gmock-internal-utils_test.cc new file mode 100644 index 00000000..ae743c1c --- /dev/null +++ b/libwvdrmengine/test/gmock/test/gmock-internal-utils_test.cc @@ -0,0 +1,655 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file tests the internal utilities. + +#include "gmock/internal/gmock-internal-utils.h" +#include +#include +#include +#include +#include +#include "gmock/gmock.h" +#include "gmock/internal/gmock-port.h" +#include "gtest/gtest.h" +#include "gtest/gtest-spi.h" + +#if GTEST_OS_CYGWIN +# include // For ssize_t. NOLINT +#endif + +class ProtocolMessage; + +namespace proto2 { +class Message; +} // namespace proto2 + +namespace testing { +namespace internal { + +namespace { + +using ::std::tr1::make_tuple; +using ::std::tr1::tuple; + +TEST(ConvertIdentifierNameToWordsTest, WorksWhenNameContainsNoWord) { + EXPECT_EQ("", ConvertIdentifierNameToWords("")); + EXPECT_EQ("", ConvertIdentifierNameToWords("_")); + EXPECT_EQ("", ConvertIdentifierNameToWords("__")); +} + +TEST(ConvertIdentifierNameToWordsTest, WorksWhenNameContainsDigits) { + EXPECT_EQ("1", ConvertIdentifierNameToWords("_1")); + EXPECT_EQ("2", ConvertIdentifierNameToWords("2_")); + EXPECT_EQ("34", ConvertIdentifierNameToWords("_34_")); + EXPECT_EQ("34 56", ConvertIdentifierNameToWords("_34_56")); +} + +TEST(ConvertIdentifierNameToWordsTest, WorksWhenNameContainsCamelCaseWords) { + EXPECT_EQ("a big word", ConvertIdentifierNameToWords("ABigWord")); + EXPECT_EQ("foo bar", ConvertIdentifierNameToWords("FooBar")); + EXPECT_EQ("foo", ConvertIdentifierNameToWords("Foo_")); + EXPECT_EQ("foo bar", ConvertIdentifierNameToWords("_Foo_Bar_")); + EXPECT_EQ("foo and bar", ConvertIdentifierNameToWords("_Foo__And_Bar")); +} + +TEST(ConvertIdentifierNameToWordsTest, WorksWhenNameContains_SeparatedWords) { + EXPECT_EQ("foo bar", ConvertIdentifierNameToWords("foo_bar")); + EXPECT_EQ("foo", ConvertIdentifierNameToWords("_foo_")); + EXPECT_EQ("foo bar", ConvertIdentifierNameToWords("_foo_bar_")); + EXPECT_EQ("foo and bar", ConvertIdentifierNameToWords("_foo__and_bar")); +} + +TEST(ConvertIdentifierNameToWordsTest, WorksWhenNameIsMixture) { + EXPECT_EQ("foo bar 123", ConvertIdentifierNameToWords("Foo_bar123")); + EXPECT_EQ("chapter 11 section 1", + ConvertIdentifierNameToWords("_Chapter11Section_1_")); +} + +TEST(PointeeOfTest, WorksForSmartPointers) { + CompileAssertTypesEqual >::type>(); +} + +TEST(PointeeOfTest, WorksForRawPointers) { + CompileAssertTypesEqual::type>(); + CompileAssertTypesEqual::type>(); + CompileAssertTypesEqual::type>(); +} + +TEST(GetRawPointerTest, WorksForSmartPointers) { + const char* const raw_p4 = new const char('a'); // NOLINT + const internal::linked_ptr p4(raw_p4); + EXPECT_EQ(raw_p4, GetRawPointer(p4)); +} + +TEST(GetRawPointerTest, WorksForRawPointers) { + int* p = NULL; + // Don't use EXPECT_EQ as no NULL-testing magic on Symbian. + EXPECT_TRUE(NULL == GetRawPointer(p)); + int n = 1; + EXPECT_EQ(&n, GetRawPointer(&n)); +} + +// Tests KindOf. + +class Base {}; +class Derived : public Base {}; + +TEST(KindOfTest, Bool) { + EXPECT_EQ(kBool, GMOCK_KIND_OF_(bool)); // NOLINT +} + +TEST(KindOfTest, Integer) { + EXPECT_EQ(kInteger, GMOCK_KIND_OF_(char)); // NOLINT + EXPECT_EQ(kInteger, GMOCK_KIND_OF_(signed char)); // NOLINT + EXPECT_EQ(kInteger, GMOCK_KIND_OF_(unsigned char)); // NOLINT + EXPECT_EQ(kInteger, GMOCK_KIND_OF_(short)); // NOLINT + EXPECT_EQ(kInteger, GMOCK_KIND_OF_(unsigned short)); // NOLINT + EXPECT_EQ(kInteger, GMOCK_KIND_OF_(int)); // NOLINT + EXPECT_EQ(kInteger, GMOCK_KIND_OF_(unsigned int)); // NOLINT + EXPECT_EQ(kInteger, GMOCK_KIND_OF_(long)); // NOLINT + EXPECT_EQ(kInteger, GMOCK_KIND_OF_(unsigned long)); // NOLINT + EXPECT_EQ(kInteger, GMOCK_KIND_OF_(wchar_t)); // NOLINT + EXPECT_EQ(kInteger, GMOCK_KIND_OF_(Int64)); // NOLINT + EXPECT_EQ(kInteger, GMOCK_KIND_OF_(UInt64)); // NOLINT + EXPECT_EQ(kInteger, GMOCK_KIND_OF_(size_t)); // NOLINT +#if GTEST_OS_LINUX || GTEST_OS_MAC || GTEST_OS_CYGWIN + // ssize_t is not defined on Windows and possibly some other OSes. + EXPECT_EQ(kInteger, GMOCK_KIND_OF_(ssize_t)); // NOLINT +#endif +} + +TEST(KindOfTest, FloatingPoint) { + EXPECT_EQ(kFloatingPoint, GMOCK_KIND_OF_(float)); // NOLINT + EXPECT_EQ(kFloatingPoint, GMOCK_KIND_OF_(double)); // NOLINT + EXPECT_EQ(kFloatingPoint, GMOCK_KIND_OF_(long double)); // NOLINT +} + +TEST(KindOfTest, Other) { + EXPECT_EQ(kOther, GMOCK_KIND_OF_(void*)); // NOLINT + EXPECT_EQ(kOther, GMOCK_KIND_OF_(char**)); // NOLINT + EXPECT_EQ(kOther, GMOCK_KIND_OF_(Base)); // NOLINT +} + +// Tests LosslessArithmeticConvertible. + +TEST(LosslessArithmeticConvertibleTest, BoolToBool) { + EXPECT_TRUE((LosslessArithmeticConvertible::value)); +} + +TEST(LosslessArithmeticConvertibleTest, BoolToInteger) { + EXPECT_TRUE((LosslessArithmeticConvertible::value)); + EXPECT_TRUE((LosslessArithmeticConvertible::value)); + EXPECT_TRUE( + (LosslessArithmeticConvertible::value)); // NOLINT +} + +TEST(LosslessArithmeticConvertibleTest, BoolToFloatingPoint) { + EXPECT_TRUE((LosslessArithmeticConvertible::value)); + EXPECT_TRUE((LosslessArithmeticConvertible::value)); +} + +TEST(LosslessArithmeticConvertibleTest, IntegerToBool) { + EXPECT_FALSE((LosslessArithmeticConvertible::value)); + EXPECT_FALSE((LosslessArithmeticConvertible::value)); +} + +TEST(LosslessArithmeticConvertibleTest, IntegerToInteger) { + // Unsigned => larger signed is fine. + EXPECT_TRUE((LosslessArithmeticConvertible::value)); + + // Unsigned => larger unsigned is fine. + EXPECT_TRUE( + (LosslessArithmeticConvertible::value)); // NOLINT + + // Signed => unsigned is not fine. + EXPECT_FALSE((LosslessArithmeticConvertible::value)); // NOLINT + EXPECT_FALSE((LosslessArithmeticConvertible< + signed char, unsigned int>::value)); // NOLINT + + // Same size and same signedness: fine too. + EXPECT_TRUE((LosslessArithmeticConvertible< + unsigned char, unsigned char>::value)); + EXPECT_TRUE((LosslessArithmeticConvertible::value)); + EXPECT_TRUE((LosslessArithmeticConvertible::value)); + EXPECT_TRUE((LosslessArithmeticConvertible< + unsigned long, unsigned long>::value)); // NOLINT + + // Same size, different signedness: not fine. + EXPECT_FALSE((LosslessArithmeticConvertible< + unsigned char, signed char>::value)); + EXPECT_FALSE((LosslessArithmeticConvertible::value)); + EXPECT_FALSE((LosslessArithmeticConvertible::value)); + + // Larger size => smaller size is not fine. + EXPECT_FALSE((LosslessArithmeticConvertible::value)); // NOLINT + EXPECT_FALSE((LosslessArithmeticConvertible::value)); + EXPECT_FALSE((LosslessArithmeticConvertible::value)); +} + +TEST(LosslessArithmeticConvertibleTest, IntegerToFloatingPoint) { + // Integers cannot be losslessly converted to floating-points, as + // the format of the latter is implementation-defined. + EXPECT_FALSE((LosslessArithmeticConvertible::value)); + EXPECT_FALSE((LosslessArithmeticConvertible::value)); + EXPECT_FALSE((LosslessArithmeticConvertible< + short, long double>::value)); // NOLINT +} + +TEST(LosslessArithmeticConvertibleTest, FloatingPointToBool) { + EXPECT_FALSE((LosslessArithmeticConvertible::value)); + EXPECT_FALSE((LosslessArithmeticConvertible::value)); +} + +TEST(LosslessArithmeticConvertibleTest, FloatingPointToInteger) { + EXPECT_FALSE((LosslessArithmeticConvertible::value)); // NOLINT + EXPECT_FALSE((LosslessArithmeticConvertible::value)); + EXPECT_FALSE((LosslessArithmeticConvertible::value)); +} + +TEST(LosslessArithmeticConvertibleTest, FloatingPointToFloatingPoint) { + // Smaller size => larger size is fine. + EXPECT_TRUE((LosslessArithmeticConvertible::value)); + EXPECT_TRUE((LosslessArithmeticConvertible::value)); + EXPECT_TRUE((LosslessArithmeticConvertible::value)); + + // Same size: fine. + EXPECT_TRUE((LosslessArithmeticConvertible::value)); + EXPECT_TRUE((LosslessArithmeticConvertible::value)); + + // Larger size => smaller size is not fine. + EXPECT_FALSE((LosslessArithmeticConvertible::value)); + if (sizeof(double) == sizeof(long double)) { // NOLINT + // In some implementations (e.g. MSVC), double and long double + // have the same size. + EXPECT_TRUE((LosslessArithmeticConvertible::value)); + } else { + EXPECT_FALSE((LosslessArithmeticConvertible::value)); + } +} + +// Tests the TupleMatches() template function. + +TEST(TupleMatchesTest, WorksForSize0) { + tuple<> matchers; + tuple<> values; + + EXPECT_TRUE(TupleMatches(matchers, values)); +} + +TEST(TupleMatchesTest, WorksForSize1) { + tuple > matchers(Eq(1)); + tuple values1(1), + values2(2); + + EXPECT_TRUE(TupleMatches(matchers, values1)); + EXPECT_FALSE(TupleMatches(matchers, values2)); +} + +TEST(TupleMatchesTest, WorksForSize2) { + tuple, Matcher > matchers(Eq(1), Eq('a')); + tuple values1(1, 'a'), + values2(1, 'b'), + values3(2, 'a'), + values4(2, 'b'); + + EXPECT_TRUE(TupleMatches(matchers, values1)); + EXPECT_FALSE(TupleMatches(matchers, values2)); + EXPECT_FALSE(TupleMatches(matchers, values3)); + EXPECT_FALSE(TupleMatches(matchers, values4)); +} + +TEST(TupleMatchesTest, WorksForSize5) { + tuple, Matcher, Matcher, Matcher, // NOLINT + Matcher > + matchers(Eq(1), Eq('a'), Eq(true), Eq(2L), Eq("hi")); + tuple // NOLINT + values1(1, 'a', true, 2L, "hi"), + values2(1, 'a', true, 2L, "hello"), + values3(2, 'a', true, 2L, "hi"); + + EXPECT_TRUE(TupleMatches(matchers, values1)); + EXPECT_FALSE(TupleMatches(matchers, values2)); + EXPECT_FALSE(TupleMatches(matchers, values3)); +} + +// Tests that Assert(true, ...) succeeds. +TEST(AssertTest, SucceedsOnTrue) { + Assert(true, __FILE__, __LINE__, "This should succeed."); + Assert(true, __FILE__, __LINE__); // This should succeed too. +} + +// Tests that Assert(false, ...) generates a fatal failure. +TEST(AssertTest, FailsFatallyOnFalse) { + EXPECT_DEATH_IF_SUPPORTED({ + Assert(false, __FILE__, __LINE__, "This should fail."); + }, ""); + + EXPECT_DEATH_IF_SUPPORTED({ + Assert(false, __FILE__, __LINE__); + }, ""); +} + +// Tests that Expect(true, ...) succeeds. +TEST(ExpectTest, SucceedsOnTrue) { + Expect(true, __FILE__, __LINE__, "This should succeed."); + Expect(true, __FILE__, __LINE__); // This should succeed too. +} + +// Tests that Expect(false, ...) generates a non-fatal failure. +TEST(ExpectTest, FailsNonfatallyOnFalse) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + Expect(false, __FILE__, __LINE__, "This should fail."); + }, "This should fail"); + + EXPECT_NONFATAL_FAILURE({ // NOLINT + Expect(false, __FILE__, __LINE__); + }, "Expectation failed"); +} + +// Tests LogIsVisible(). + +class LogIsVisibleTest : public ::testing::Test { + protected: + virtual void SetUp() { + // The code needs to work when both ::string and ::std::string are + // defined and the flag is implemented as a + // testing::internal::String. In this case, without the call to + // c_str(), the compiler will complain that it cannot figure out + // whether the String flag should be converted to a ::string or an + // ::std::string before being assigned to original_verbose_. + original_verbose_ = GMOCK_FLAG(verbose).c_str(); + } + + virtual void TearDown() { GMOCK_FLAG(verbose) = original_verbose_; } + + string original_verbose_; +}; + +TEST_F(LogIsVisibleTest, AlwaysReturnsTrueIfVerbosityIsInfo) { + GMOCK_FLAG(verbose) = kInfoVerbosity; + EXPECT_TRUE(LogIsVisible(INFO)); + EXPECT_TRUE(LogIsVisible(WARNING)); +} + +TEST_F(LogIsVisibleTest, AlwaysReturnsFalseIfVerbosityIsError) { + GMOCK_FLAG(verbose) = kErrorVerbosity; + EXPECT_FALSE(LogIsVisible(INFO)); + EXPECT_FALSE(LogIsVisible(WARNING)); +} + +TEST_F(LogIsVisibleTest, WorksWhenVerbosityIsWarning) { + GMOCK_FLAG(verbose) = kWarningVerbosity; + EXPECT_FALSE(LogIsVisible(INFO)); + EXPECT_TRUE(LogIsVisible(WARNING)); +} + +#if GTEST_HAS_STREAM_REDIRECTION + +// Tests the Log() function. + +// Verifies that Log() behaves correctly for the given verbosity level +// and log severity. +void TestLogWithSeverity(const string& verbosity, LogSeverity severity, + bool should_print) { + const string old_flag = GMOCK_FLAG(verbose); + GMOCK_FLAG(verbose) = verbosity; + CaptureStdout(); + Log(severity, "Test log.\n", 0); + if (should_print) { + EXPECT_THAT(GetCapturedStdout().c_str(), + ContainsRegex( + severity == WARNING ? + "^\nGMOCK WARNING:\nTest log\\.\nStack trace:\n" : + "^\nTest log\\.\nStack trace:\n")); + } else { + EXPECT_STREQ("", GetCapturedStdout().c_str()); + } + GMOCK_FLAG(verbose) = old_flag; +} + +// Tests that when the stack_frames_to_skip parameter is negative, +// Log() doesn't include the stack trace in the output. +TEST(LogTest, NoStackTraceWhenStackFramesToSkipIsNegative) { + const string saved_flag = GMOCK_FLAG(verbose); + GMOCK_FLAG(verbose) = kInfoVerbosity; + CaptureStdout(); + Log(INFO, "Test log.\n", -1); + EXPECT_STREQ("\nTest log.\n", GetCapturedStdout().c_str()); + GMOCK_FLAG(verbose) = saved_flag; +} + +// Tests that in opt mode, a positive stack_frames_to_skip argument is +// treated as 0. +TEST(LogTest, NoSkippingStackFrameInOptMode) { + CaptureStdout(); + Log(WARNING, "Test log.\n", 100); + const String log = GetCapturedStdout(); + +# if defined(NDEBUG) && GTEST_GOOGLE3_MODE_ + + // In opt mode, no stack frame should be skipped. + EXPECT_THAT(log, ContainsRegex("\nGMOCK WARNING:\n" + "Test log\\.\n" + "Stack trace:\n" + ".+")); +# else + + // In dbg mode, the stack frames should be skipped. + EXPECT_STREQ("\nGMOCK WARNING:\n" + "Test log.\n" + "Stack trace:\n", log.c_str()); +# endif +} + +// Tests that all logs are printed when the value of the +// --gmock_verbose flag is "info". +TEST(LogTest, AllLogsArePrintedWhenVerbosityIsInfo) { + TestLogWithSeverity(kInfoVerbosity, INFO, true); + TestLogWithSeverity(kInfoVerbosity, WARNING, true); +} + +// Tests that only warnings are printed when the value of the +// --gmock_verbose flag is "warning". +TEST(LogTest, OnlyWarningsArePrintedWhenVerbosityIsWarning) { + TestLogWithSeverity(kWarningVerbosity, INFO, false); + TestLogWithSeverity(kWarningVerbosity, WARNING, true); +} + +// Tests that no logs are printed when the value of the +// --gmock_verbose flag is "error". +TEST(LogTest, NoLogsArePrintedWhenVerbosityIsError) { + TestLogWithSeverity(kErrorVerbosity, INFO, false); + TestLogWithSeverity(kErrorVerbosity, WARNING, false); +} + +// Tests that only warnings are printed when the value of the +// --gmock_verbose flag is invalid. +TEST(LogTest, OnlyWarningsArePrintedWhenVerbosityIsInvalid) { + TestLogWithSeverity("invalid", INFO, false); + TestLogWithSeverity("invalid", WARNING, true); +} + +#endif // GTEST_HAS_STREAM_REDIRECTION + +TEST(TypeTraitsTest, true_type) { + EXPECT_TRUE(true_type::value); +} + +TEST(TypeTraitsTest, false_type) { + EXPECT_FALSE(false_type::value); +} + +TEST(TypeTraitsTest, is_reference) { + EXPECT_FALSE(is_reference::value); + EXPECT_FALSE(is_reference::value); + EXPECT_TRUE(is_reference::value); +} + +TEST(TypeTraitsTest, is_pointer) { + EXPECT_FALSE(is_pointer::value); + EXPECT_FALSE(is_pointer::value); + EXPECT_TRUE(is_pointer::value); +} + +TEST(TypeTraitsTest, type_equals) { + EXPECT_FALSE((type_equals::value)); + EXPECT_FALSE((type_equals::value)); + EXPECT_FALSE((type_equals::value)); + EXPECT_TRUE((type_equals::value)); +} + +TEST(TypeTraitsTest, remove_reference) { + EXPECT_TRUE((type_equals::type>::value)); + EXPECT_TRUE((type_equals::type>::value)); + EXPECT_TRUE((type_equals::type>::value)); + EXPECT_TRUE((type_equals::type>::value)); +} + +#if GTEST_HAS_STREAM_REDIRECTION + +// Verifies that Log() behaves correctly for the given verbosity level +// and log severity. +String GrabOutput(void(*logger)(), const char* verbosity) { + const string saved_flag = GMOCK_FLAG(verbose); + GMOCK_FLAG(verbose) = verbosity; + CaptureStdout(); + logger(); + GMOCK_FLAG(verbose) = saved_flag; + return GetCapturedStdout(); +} + +class DummyMock { + public: + MOCK_METHOD0(TestMethod, void()); + MOCK_METHOD1(TestMethodArg, void(int dummy)); +}; + +void ExpectCallLogger() { + DummyMock mock; + EXPECT_CALL(mock, TestMethod()); + mock.TestMethod(); +}; + +// Verifies that EXPECT_CALL logs if the --gmock_verbose flag is set to "info". +TEST(ExpectCallTest, LogsWhenVerbosityIsInfo) { + EXPECT_THAT(GrabOutput(ExpectCallLogger, kInfoVerbosity), + HasSubstr("EXPECT_CALL(mock, TestMethod())")); +} + +// Verifies that EXPECT_CALL doesn't log +// if the --gmock_verbose flag is set to "warning". +TEST(ExpectCallTest, DoesNotLogWhenVerbosityIsWarning) { + EXPECT_STREQ("", GrabOutput(ExpectCallLogger, kWarningVerbosity).c_str()); +} + +// Verifies that EXPECT_CALL doesn't log +// if the --gmock_verbose flag is set to "error". +TEST(ExpectCallTest, DoesNotLogWhenVerbosityIsError) { + EXPECT_STREQ("", GrabOutput(ExpectCallLogger, kErrorVerbosity).c_str()); +} + +void OnCallLogger() { + DummyMock mock; + ON_CALL(mock, TestMethod()); +}; + +// Verifies that ON_CALL logs if the --gmock_verbose flag is set to "info". +TEST(OnCallTest, LogsWhenVerbosityIsInfo) { + EXPECT_THAT(GrabOutput(OnCallLogger, kInfoVerbosity), + HasSubstr("ON_CALL(mock, TestMethod())")); +} + +// Verifies that ON_CALL doesn't log +// if the --gmock_verbose flag is set to "warning". +TEST(OnCallTest, DoesNotLogWhenVerbosityIsWarning) { + EXPECT_STREQ("", GrabOutput(OnCallLogger, kWarningVerbosity).c_str()); +} + +// Verifies that ON_CALL doesn't log if +// the --gmock_verbose flag is set to "error". +TEST(OnCallTest, DoesNotLogWhenVerbosityIsError) { + EXPECT_STREQ("", GrabOutput(OnCallLogger, kErrorVerbosity).c_str()); +} + +void OnCallAnyArgumentLogger() { + DummyMock mock; + ON_CALL(mock, TestMethodArg(_)); +} + +// Verifies that ON_CALL prints provided _ argument. +TEST(OnCallTest, LogsAnythingArgument) { + EXPECT_THAT(GrabOutput(OnCallAnyArgumentLogger, kInfoVerbosity), + HasSubstr("ON_CALL(mock, TestMethodArg(_)")); +} + +#endif // GTEST_HAS_STREAM_REDIRECTION + +// Tests StlContainerView. + +TEST(StlContainerViewTest, WorksForStlContainer) { + StaticAssertTypeEq, + StlContainerView >::type>(); + StaticAssertTypeEq&, + StlContainerView >::const_reference>(); + + typedef std::vector Chars; + Chars v1; + const Chars& v2(StlContainerView::ConstReference(v1)); + EXPECT_EQ(&v1, &v2); + + v1.push_back('a'); + Chars v3 = StlContainerView::Copy(v1); + EXPECT_THAT(v3, Eq(v3)); +} + +TEST(StlContainerViewTest, WorksForStaticNativeArray) { + StaticAssertTypeEq, + StlContainerView::type>(); + StaticAssertTypeEq, + StlContainerView::type>(); + StaticAssertTypeEq, + StlContainerView::type>(); + + StaticAssertTypeEq, + StlContainerView::const_reference>(); + + int a1[3] = { 0, 1, 2 }; + NativeArray a2 = StlContainerView::ConstReference(a1); + EXPECT_EQ(3U, a2.size()); + EXPECT_EQ(a1, a2.begin()); + + const NativeArray a3 = StlContainerView::Copy(a1); + ASSERT_EQ(3U, a3.size()); + EXPECT_EQ(0, a3.begin()[0]); + EXPECT_EQ(1, a3.begin()[1]); + EXPECT_EQ(2, a3.begin()[2]); + + // Makes sure a1 and a3 aren't aliases. + a1[0] = 3; + EXPECT_EQ(0, a3.begin()[0]); +} + +TEST(StlContainerViewTest, WorksForDynamicNativeArray) { + StaticAssertTypeEq, + StlContainerView >::type>(); + StaticAssertTypeEq, + StlContainerView, int> >::type>(); + + StaticAssertTypeEq, + StlContainerView >::const_reference>(); + + int a1[3] = { 0, 1, 2 }; + const int* const p1 = a1; + NativeArray a2 = StlContainerView >:: + ConstReference(make_tuple(p1, 3)); + EXPECT_EQ(3U, a2.size()); + EXPECT_EQ(a1, a2.begin()); + + const NativeArray a3 = StlContainerView >:: + Copy(make_tuple(static_cast(a1), 3)); + ASSERT_EQ(3U, a3.size()); + EXPECT_EQ(0, a3.begin()[0]); + EXPECT_EQ(1, a3.begin()[1]); + EXPECT_EQ(2, a3.begin()[2]); + + // Makes sure a1 and a3 aren't aliases. + a1[0] = 3; + EXPECT_EQ(0, a3.begin()[0]); +} + +} // namespace +} // namespace internal +} // namespace testing diff --git a/libwvdrmengine/test/gmock/test/gmock-matchers_test.cc b/libwvdrmengine/test/gmock/test/gmock-matchers_test.cc new file mode 100644 index 00000000..9ad62c47 --- /dev/null +++ b/libwvdrmengine/test/gmock/test/gmock-matchers_test.cc @@ -0,0 +1,4040 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file tests some commonly used argument matchers. + +#include "gmock/gmock-matchers.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "gtest/gtest-spi.h" + +namespace testing { + +namespace internal { +string JoinAsTuple(const Strings& fields); +} // namespace internal + +namespace gmock_matchers_test { + +using std::list; +using std::make_pair; +using std::map; +using std::multimap; +using std::multiset; +using std::ostream; +using std::pair; +using std::set; +using std::stringstream; +using std::tr1::get; +using std::tr1::make_tuple; +using std::tr1::tuple; +using std::vector; +using testing::A; +using testing::AllArgs; +using testing::AllOf; +using testing::An; +using testing::AnyOf; +using testing::ByRef; +using testing::ContainsRegex; +using testing::DoubleEq; +using testing::EndsWith; +using testing::Eq; +using testing::ExplainMatchResult; +using testing::Field; +using testing::FloatEq; +using testing::Ge; +using testing::Gt; +using testing::HasSubstr; +using testing::IsNull; +using testing::Key; +using testing::Le; +using testing::Lt; +using testing::MakeMatcher; +using testing::MakePolymorphicMatcher; +using testing::MatchResultListener; +using testing::Matcher; +using testing::MatcherCast; +using testing::MatcherInterface; +using testing::Matches; +using testing::MatchesRegex; +using testing::NanSensitiveDoubleEq; +using testing::NanSensitiveFloatEq; +using testing::Ne; +using testing::Not; +using testing::NotNull; +using testing::Pair; +using testing::Pointee; +using testing::Pointwise; +using testing::PolymorphicMatcher; +using testing::Property; +using testing::Ref; +using testing::ResultOf; +using testing::StartsWith; +using testing::StrCaseEq; +using testing::StrCaseNe; +using testing::StrEq; +using testing::StrNe; +using testing::Truly; +using testing::TypedEq; +using testing::Value; +using testing::_; +using testing::internal::DummyMatchResultListener; +using testing::internal::ExplainMatchFailureTupleTo; +using testing::internal::FloatingEqMatcher; +using testing::internal::FormatMatcherDescription; +using testing::internal::IsReadableTypeName; +using testing::internal::JoinAsTuple; +using testing::internal::RE; +using testing::internal::StreamMatchResultListener; +using testing::internal::String; +using testing::internal::StringMatchResultListener; +using testing::internal::Strings; +using testing::internal::linked_ptr; +using testing::internal::scoped_ptr; +using testing::internal::string; + +// For testing ExplainMatchResultTo(). +class GreaterThanMatcher : public MatcherInterface { + public: + explicit GreaterThanMatcher(int rhs) : rhs_(rhs) {} + + virtual void DescribeTo(ostream* os) const { + *os << "is > " << rhs_; + } + + virtual bool MatchAndExplain(int lhs, + MatchResultListener* listener) const { + const int diff = lhs - rhs_; + if (diff > 0) { + *listener << "which is " << diff << " more than " << rhs_; + } else if (diff == 0) { + *listener << "which is the same as " << rhs_; + } else { + *listener << "which is " << -diff << " less than " << rhs_; + } + + return lhs > rhs_; + } + + private: + int rhs_; +}; + +Matcher GreaterThan(int n) { + return MakeMatcher(new GreaterThanMatcher(n)); +} + +string OfType(const string& type_name) { +#if GTEST_HAS_RTTI + return " (of type " + type_name + ")"; +#else + return ""; +#endif +} + +// Returns the description of the given matcher. +template +string Describe(const Matcher& m) { + stringstream ss; + m.DescribeTo(&ss); + return ss.str(); +} + +// Returns the description of the negation of the given matcher. +template +string DescribeNegation(const Matcher& m) { + stringstream ss; + m.DescribeNegationTo(&ss); + return ss.str(); +} + +// Returns the reason why x matches, or doesn't match, m. +template +string Explain(const MatcherType& m, const Value& x) { + StringMatchResultListener listener; + ExplainMatchResult(m, x, &listener); + return listener.str(); +} + +TEST(MatchResultListenerTest, StreamingWorks) { + StringMatchResultListener listener; + listener << "hi" << 5; + EXPECT_EQ("hi5", listener.str()); + + // Streaming shouldn't crash when the underlying ostream is NULL. + DummyMatchResultListener dummy; + dummy << "hi" << 5; +} + +TEST(MatchResultListenerTest, CanAccessUnderlyingStream) { + EXPECT_TRUE(DummyMatchResultListener().stream() == NULL); + EXPECT_TRUE(StreamMatchResultListener(NULL).stream() == NULL); + + EXPECT_EQ(&std::cout, StreamMatchResultListener(&std::cout).stream()); +} + +TEST(MatchResultListenerTest, IsInterestedWorks) { + EXPECT_TRUE(StringMatchResultListener().IsInterested()); + EXPECT_TRUE(StreamMatchResultListener(&std::cout).IsInterested()); + + EXPECT_FALSE(DummyMatchResultListener().IsInterested()); + EXPECT_FALSE(StreamMatchResultListener(NULL).IsInterested()); +} + +// Makes sure that the MatcherInterface interface doesn't +// change. +class EvenMatcherImpl : public MatcherInterface { + public: + virtual bool MatchAndExplain(int x, + MatchResultListener* /* listener */) const { + return x % 2 == 0; + } + + virtual void DescribeTo(ostream* os) const { + *os << "is an even number"; + } + + // We deliberately don't define DescribeNegationTo() and + // ExplainMatchResultTo() here, to make sure the definition of these + // two methods is optional. +}; + +// Makes sure that the MatcherInterface API doesn't change. +TEST(MatcherInterfaceTest, CanBeImplementedUsingPublishedAPI) { + EvenMatcherImpl m; +} + +// Tests implementing a monomorphic matcher using MatchAndExplain(). + +class NewEvenMatcherImpl : public MatcherInterface { + public: + virtual bool MatchAndExplain(int x, MatchResultListener* listener) const { + const bool match = x % 2 == 0; + // Verifies that we can stream to a listener directly. + *listener << "value % " << 2; + if (listener->stream() != NULL) { + // Verifies that we can stream to a listener's underlying stream + // too. + *listener->stream() << " == " << (x % 2); + } + return match; + } + + virtual void DescribeTo(ostream* os) const { + *os << "is an even number"; + } +}; + +TEST(MatcherInterfaceTest, CanBeImplementedUsingNewAPI) { + Matcher m = MakeMatcher(new NewEvenMatcherImpl); + EXPECT_TRUE(m.Matches(2)); + EXPECT_FALSE(m.Matches(3)); + EXPECT_EQ("value % 2 == 0", Explain(m, 2)); + EXPECT_EQ("value % 2 == 1", Explain(m, 3)); +} + +// Tests default-constructing a matcher. +TEST(MatcherTest, CanBeDefaultConstructed) { + Matcher m; +} + +// Tests that Matcher can be constructed from a MatcherInterface*. +TEST(MatcherTest, CanBeConstructedFromMatcherInterface) { + const MatcherInterface* impl = new EvenMatcherImpl; + Matcher m(impl); + EXPECT_TRUE(m.Matches(4)); + EXPECT_FALSE(m.Matches(5)); +} + +// Tests that value can be used in place of Eq(value). +TEST(MatcherTest, CanBeImplicitlyConstructedFromValue) { + Matcher m1 = 5; + EXPECT_TRUE(m1.Matches(5)); + EXPECT_FALSE(m1.Matches(6)); +} + +// Tests that NULL can be used in place of Eq(NULL). +TEST(MatcherTest, CanBeImplicitlyConstructedFromNULL) { + Matcher m1 = NULL; + EXPECT_TRUE(m1.Matches(NULL)); + int n = 0; + EXPECT_FALSE(m1.Matches(&n)); +} + +// Tests that matchers are copyable. +TEST(MatcherTest, IsCopyable) { + // Tests the copy constructor. + Matcher m1 = Eq(false); + EXPECT_TRUE(m1.Matches(false)); + EXPECT_FALSE(m1.Matches(true)); + + // Tests the assignment operator. + m1 = Eq(true); + EXPECT_TRUE(m1.Matches(true)); + EXPECT_FALSE(m1.Matches(false)); +} + +// Tests that Matcher::DescribeTo() calls +// MatcherInterface::DescribeTo(). +TEST(MatcherTest, CanDescribeItself) { + EXPECT_EQ("is an even number", + Describe(Matcher(new EvenMatcherImpl))); +} + +// Tests Matcher::MatchAndExplain(). +TEST(MatcherTest, MatchAndExplain) { + Matcher m = GreaterThan(0); + StringMatchResultListener listener1; + EXPECT_TRUE(m.MatchAndExplain(42, &listener1)); + EXPECT_EQ("which is 42 more than 0", listener1.str()); + + StringMatchResultListener listener2; + EXPECT_FALSE(m.MatchAndExplain(-9, &listener2)); + EXPECT_EQ("which is 9 less than 0", listener2.str()); +} + +// Tests that a C-string literal can be implicitly converted to a +// Matcher or Matcher. +TEST(StringMatcherTest, CanBeImplicitlyConstructedFromCStringLiteral) { + Matcher m1 = "hi"; + EXPECT_TRUE(m1.Matches("hi")); + EXPECT_FALSE(m1.Matches("hello")); + + Matcher m2 = "hi"; + EXPECT_TRUE(m2.Matches("hi")); + EXPECT_FALSE(m2.Matches("hello")); +} + +// Tests that a string object can be implicitly converted to a +// Matcher or Matcher. +TEST(StringMatcherTest, CanBeImplicitlyConstructedFromString) { + Matcher m1 = string("hi"); + EXPECT_TRUE(m1.Matches("hi")); + EXPECT_FALSE(m1.Matches("hello")); + + Matcher m2 = string("hi"); + EXPECT_TRUE(m2.Matches("hi")); + EXPECT_FALSE(m2.Matches("hello")); +} + +// Tests that MakeMatcher() constructs a Matcher from a +// MatcherInterface* without requiring the user to explicitly +// write the type. +TEST(MakeMatcherTest, ConstructsMatcherFromMatcherInterface) { + const MatcherInterface* dummy_impl = NULL; + Matcher m = MakeMatcher(dummy_impl); +} + +// Tests that MakePolymorphicMatcher() can construct a polymorphic +// matcher from its implementation using the old API. +const int g_bar = 1; +class ReferencesBarOrIsZeroImpl { + public: + template + bool MatchAndExplain(const T& x, + MatchResultListener* /* listener */) const { + const void* p = &x; + return p == &g_bar || x == 0; + } + + void DescribeTo(ostream* os) const { *os << "g_bar or zero"; } + + void DescribeNegationTo(ostream* os) const { + *os << "doesn't reference g_bar and is not zero"; + } +}; + +// This function verifies that MakePolymorphicMatcher() returns a +// PolymorphicMatcher where T is the argument's type. +PolymorphicMatcher ReferencesBarOrIsZero() { + return MakePolymorphicMatcher(ReferencesBarOrIsZeroImpl()); +} + +TEST(MakePolymorphicMatcherTest, ConstructsMatcherUsingOldAPI) { + // Using a polymorphic matcher to match a reference type. + Matcher m1 = ReferencesBarOrIsZero(); + EXPECT_TRUE(m1.Matches(0)); + // Verifies that the identity of a by-reference argument is preserved. + EXPECT_TRUE(m1.Matches(g_bar)); + EXPECT_FALSE(m1.Matches(1)); + EXPECT_EQ("g_bar or zero", Describe(m1)); + + // Using a polymorphic matcher to match a value type. + Matcher m2 = ReferencesBarOrIsZero(); + EXPECT_TRUE(m2.Matches(0.0)); + EXPECT_FALSE(m2.Matches(0.1)); + EXPECT_EQ("g_bar or zero", Describe(m2)); +} + +// Tests implementing a polymorphic matcher using MatchAndExplain(). + +class PolymorphicIsEvenImpl { + public: + void DescribeTo(ostream* os) const { *os << "is even"; } + + void DescribeNegationTo(ostream* os) const { + *os << "is odd"; + } + + template + bool MatchAndExplain(const T& x, MatchResultListener* listener) const { + // Verifies that we can stream to the listener directly. + *listener << "% " << 2; + if (listener->stream() != NULL) { + // Verifies that we can stream to the listener's underlying stream + // too. + *listener->stream() << " == " << (x % 2); + } + return (x % 2) == 0; + } +}; + +PolymorphicMatcher PolymorphicIsEven() { + return MakePolymorphicMatcher(PolymorphicIsEvenImpl()); +} + +TEST(MakePolymorphicMatcherTest, ConstructsMatcherUsingNewAPI) { + // Using PolymorphicIsEven() as a Matcher. + const Matcher m1 = PolymorphicIsEven(); + EXPECT_TRUE(m1.Matches(42)); + EXPECT_FALSE(m1.Matches(43)); + EXPECT_EQ("is even", Describe(m1)); + + const Matcher not_m1 = Not(m1); + EXPECT_EQ("is odd", Describe(not_m1)); + + EXPECT_EQ("% 2 == 0", Explain(m1, 42)); + + // Using PolymorphicIsEven() as a Matcher. + const Matcher m2 = PolymorphicIsEven(); + EXPECT_TRUE(m2.Matches('\x42')); + EXPECT_FALSE(m2.Matches('\x43')); + EXPECT_EQ("is even", Describe(m2)); + + const Matcher not_m2 = Not(m2); + EXPECT_EQ("is odd", Describe(not_m2)); + + EXPECT_EQ("% 2 == 0", Explain(m2, '\x42')); +} + +// Tests that MatcherCast(m) works when m is a polymorphic matcher. +TEST(MatcherCastTest, FromPolymorphicMatcher) { + Matcher m = MatcherCast(Eq(5)); + EXPECT_TRUE(m.Matches(5)); + EXPECT_FALSE(m.Matches(6)); +} + +// For testing casting matchers between compatible types. +class IntValue { + public: + // An int can be statically (although not implicitly) cast to a + // IntValue. + explicit IntValue(int a_value) : value_(a_value) {} + + int value() const { return value_; } + private: + int value_; +}; + +// For testing casting matchers between compatible types. +bool IsPositiveIntValue(const IntValue& foo) { + return foo.value() > 0; +} + +// Tests that MatcherCast(m) works when m is a Matcher where T +// can be statically converted to U. +TEST(MatcherCastTest, FromCompatibleType) { + Matcher m1 = Eq(2.0); + Matcher m2 = MatcherCast(m1); + EXPECT_TRUE(m2.Matches(2)); + EXPECT_FALSE(m2.Matches(3)); + + Matcher m3 = Truly(IsPositiveIntValue); + Matcher m4 = MatcherCast(m3); + // In the following, the arguments 1 and 0 are statically converted + // to IntValue objects, and then tested by the IsPositiveIntValue() + // predicate. + EXPECT_TRUE(m4.Matches(1)); + EXPECT_FALSE(m4.Matches(0)); +} + +// Tests that MatcherCast(m) works when m is a Matcher. +TEST(MatcherCastTest, FromConstReferenceToNonReference) { + Matcher m1 = Eq(0); + Matcher m2 = MatcherCast(m1); + EXPECT_TRUE(m2.Matches(0)); + EXPECT_FALSE(m2.Matches(1)); +} + +// Tests that MatcherCast(m) works when m is a Matcher. +TEST(MatcherCastTest, FromReferenceToNonReference) { + Matcher m1 = Eq(0); + Matcher m2 = MatcherCast(m1); + EXPECT_TRUE(m2.Matches(0)); + EXPECT_FALSE(m2.Matches(1)); +} + +// Tests that MatcherCast(m) works when m is a Matcher. +TEST(MatcherCastTest, FromNonReferenceToConstReference) { + Matcher m1 = Eq(0); + Matcher m2 = MatcherCast(m1); + EXPECT_TRUE(m2.Matches(0)); + EXPECT_FALSE(m2.Matches(1)); +} + +// Tests that MatcherCast(m) works when m is a Matcher. +TEST(MatcherCastTest, FromNonReferenceToReference) { + Matcher m1 = Eq(0); + Matcher m2 = MatcherCast(m1); + int n = 0; + EXPECT_TRUE(m2.Matches(n)); + n = 1; + EXPECT_FALSE(m2.Matches(n)); +} + +// Tests that MatcherCast(m) works when m is a Matcher. +TEST(MatcherCastTest, FromSameType) { + Matcher m1 = Eq(0); + Matcher m2 = MatcherCast(m1); + EXPECT_TRUE(m2.Matches(0)); + EXPECT_FALSE(m2.Matches(1)); +} + +class Base {}; +class Derived : public Base {}; + +// Tests that SafeMatcherCast(m) works when m is a polymorphic matcher. +TEST(SafeMatcherCastTest, FromPolymorphicMatcher) { + Matcher m2 = SafeMatcherCast(Eq(32)); + EXPECT_TRUE(m2.Matches(' ')); + EXPECT_FALSE(m2.Matches('\n')); +} + +// Tests that SafeMatcherCast(m) works when m is a Matcher where +// T and U are arithmetic types and T can be losslessly converted to +// U. +TEST(SafeMatcherCastTest, FromLosslesslyConvertibleArithmeticType) { + Matcher m1 = DoubleEq(1.0); + Matcher m2 = SafeMatcherCast(m1); + EXPECT_TRUE(m2.Matches(1.0f)); + EXPECT_FALSE(m2.Matches(2.0f)); + + Matcher m3 = SafeMatcherCast(TypedEq('a')); + EXPECT_TRUE(m3.Matches('a')); + EXPECT_FALSE(m3.Matches('b')); +} + +// Tests that SafeMatcherCast(m) works when m is a Matcher where T and U +// are pointers or references to a derived and a base class, correspondingly. +TEST(SafeMatcherCastTest, FromBaseClass) { + Derived d, d2; + Matcher m1 = Eq(&d); + Matcher m2 = SafeMatcherCast(m1); + EXPECT_TRUE(m2.Matches(&d)); + EXPECT_FALSE(m2.Matches(&d2)); + + Matcher m3 = Ref(d); + Matcher m4 = SafeMatcherCast(m3); + EXPECT_TRUE(m4.Matches(d)); + EXPECT_FALSE(m4.Matches(d2)); +} + +// Tests that SafeMatcherCast(m) works when m is a Matcher. +TEST(SafeMatcherCastTest, FromConstReferenceToReference) { + int n = 0; + Matcher m1 = Ref(n); + Matcher m2 = SafeMatcherCast(m1); + int n1 = 0; + EXPECT_TRUE(m2.Matches(n)); + EXPECT_FALSE(m2.Matches(n1)); +} + +// Tests that MatcherCast(m) works when m is a Matcher. +TEST(SafeMatcherCastTest, FromNonReferenceToConstReference) { + Matcher m1 = Eq(0); + Matcher m2 = SafeMatcherCast(m1); + EXPECT_TRUE(m2.Matches(0)); + EXPECT_FALSE(m2.Matches(1)); +} + +// Tests that SafeMatcherCast(m) works when m is a Matcher. +TEST(SafeMatcherCastTest, FromNonReferenceToReference) { + Matcher m1 = Eq(0); + Matcher m2 = SafeMatcherCast(m1); + int n = 0; + EXPECT_TRUE(m2.Matches(n)); + n = 1; + EXPECT_FALSE(m2.Matches(n)); +} + +// Tests that SafeMatcherCast(m) works when m is a Matcher. +TEST(SafeMatcherCastTest, FromSameType) { + Matcher m1 = Eq(0); + Matcher m2 = SafeMatcherCast(m1); + EXPECT_TRUE(m2.Matches(0)); + EXPECT_FALSE(m2.Matches(1)); +} + +// Tests that A() matches any value of type T. +TEST(ATest, MatchesAnyValue) { + // Tests a matcher for a value type. + Matcher m1 = A(); + EXPECT_TRUE(m1.Matches(91.43)); + EXPECT_TRUE(m1.Matches(-15.32)); + + // Tests a matcher for a reference type. + int a = 2; + int b = -6; + Matcher m2 = A(); + EXPECT_TRUE(m2.Matches(a)); + EXPECT_TRUE(m2.Matches(b)); +} + +// Tests that A() describes itself properly. +TEST(ATest, CanDescribeSelf) { + EXPECT_EQ("is anything", Describe(A())); +} + +// Tests that An() matches any value of type T. +TEST(AnTest, MatchesAnyValue) { + // Tests a matcher for a value type. + Matcher m1 = An(); + EXPECT_TRUE(m1.Matches(9143)); + EXPECT_TRUE(m1.Matches(-1532)); + + // Tests a matcher for a reference type. + int a = 2; + int b = -6; + Matcher m2 = An(); + EXPECT_TRUE(m2.Matches(a)); + EXPECT_TRUE(m2.Matches(b)); +} + +// Tests that An() describes itself properly. +TEST(AnTest, CanDescribeSelf) { + EXPECT_EQ("is anything", Describe(An())); +} + +// Tests that _ can be used as a matcher for any type and matches any +// value of that type. +TEST(UnderscoreTest, MatchesAnyValue) { + // Uses _ as a matcher for a value type. + Matcher m1 = _; + EXPECT_TRUE(m1.Matches(123)); + EXPECT_TRUE(m1.Matches(-242)); + + // Uses _ as a matcher for a reference type. + bool a = false; + const bool b = true; + Matcher m2 = _; + EXPECT_TRUE(m2.Matches(a)); + EXPECT_TRUE(m2.Matches(b)); +} + +// Tests that _ describes itself properly. +TEST(UnderscoreTest, CanDescribeSelf) { + Matcher m = _; + EXPECT_EQ("is anything", Describe(m)); +} + +// Tests that Eq(x) matches any value equal to x. +TEST(EqTest, MatchesEqualValue) { + // 2 C-strings with same content but different addresses. + const char a1[] = "hi"; + const char a2[] = "hi"; + + Matcher m1 = Eq(a1); + EXPECT_TRUE(m1.Matches(a1)); + EXPECT_FALSE(m1.Matches(a2)); +} + +// Tests that Eq(v) describes itself properly. + +class Unprintable { + public: + Unprintable() : c_('a') {} + + bool operator==(const Unprintable& /* rhs */) { return true; } + private: + char c_; +}; + +TEST(EqTest, CanDescribeSelf) { + Matcher m = Eq(Unprintable()); + EXPECT_EQ("is equal to 1-byte object <61>", Describe(m)); +} + +// Tests that Eq(v) can be used to match any type that supports +// comparing with type T, where T is v's type. +TEST(EqTest, IsPolymorphic) { + Matcher m1 = Eq(1); + EXPECT_TRUE(m1.Matches(1)); + EXPECT_FALSE(m1.Matches(2)); + + Matcher m2 = Eq(1); + EXPECT_TRUE(m2.Matches('\1')); + EXPECT_FALSE(m2.Matches('a')); +} + +// Tests that TypedEq(v) matches values of type T that's equal to v. +TEST(TypedEqTest, ChecksEqualityForGivenType) { + Matcher m1 = TypedEq('a'); + EXPECT_TRUE(m1.Matches('a')); + EXPECT_FALSE(m1.Matches('b')); + + Matcher m2 = TypedEq(6); + EXPECT_TRUE(m2.Matches(6)); + EXPECT_FALSE(m2.Matches(7)); +} + +// Tests that TypedEq(v) describes itself properly. +TEST(TypedEqTest, CanDescribeSelf) { + EXPECT_EQ("is equal to 2", Describe(TypedEq(2))); +} + +// Tests that TypedEq(v) has type Matcher. + +// Type::IsTypeOf(v) compiles iff the type of value v is T, where T +// is a "bare" type (i.e. not in the form of const U or U&). If v's +// type is not T, the compiler will generate a message about +// "undefined referece". +template +struct Type { + static bool IsTypeOf(const T& /* v */) { return true; } + + template + static void IsTypeOf(T2 v); +}; + +TEST(TypedEqTest, HasSpecifiedType) { + // Verfies that the type of TypedEq(v) is Matcher. + Type >::IsTypeOf(TypedEq(5)); + Type >::IsTypeOf(TypedEq(5)); +} + +// Tests that Ge(v) matches anything >= v. +TEST(GeTest, ImplementsGreaterThanOrEqual) { + Matcher m1 = Ge(0); + EXPECT_TRUE(m1.Matches(1)); + EXPECT_TRUE(m1.Matches(0)); + EXPECT_FALSE(m1.Matches(-1)); +} + +// Tests that Ge(v) describes itself properly. +TEST(GeTest, CanDescribeSelf) { + Matcher m = Ge(5); + EXPECT_EQ("is >= 5", Describe(m)); +} + +// Tests that Gt(v) matches anything > v. +TEST(GtTest, ImplementsGreaterThan) { + Matcher m1 = Gt(0); + EXPECT_TRUE(m1.Matches(1.0)); + EXPECT_FALSE(m1.Matches(0.0)); + EXPECT_FALSE(m1.Matches(-1.0)); +} + +// Tests that Gt(v) describes itself properly. +TEST(GtTest, CanDescribeSelf) { + Matcher m = Gt(5); + EXPECT_EQ("is > 5", Describe(m)); +} + +// Tests that Le(v) matches anything <= v. +TEST(LeTest, ImplementsLessThanOrEqual) { + Matcher m1 = Le('b'); + EXPECT_TRUE(m1.Matches('a')); + EXPECT_TRUE(m1.Matches('b')); + EXPECT_FALSE(m1.Matches('c')); +} + +// Tests that Le(v) describes itself properly. +TEST(LeTest, CanDescribeSelf) { + Matcher m = Le(5); + EXPECT_EQ("is <= 5", Describe(m)); +} + +// Tests that Lt(v) matches anything < v. +TEST(LtTest, ImplementsLessThan) { + Matcher m1 = Lt("Hello"); + EXPECT_TRUE(m1.Matches("Abc")); + EXPECT_FALSE(m1.Matches("Hello")); + EXPECT_FALSE(m1.Matches("Hello, world!")); +} + +// Tests that Lt(v) describes itself properly. +TEST(LtTest, CanDescribeSelf) { + Matcher m = Lt(5); + EXPECT_EQ("is < 5", Describe(m)); +} + +// Tests that Ne(v) matches anything != v. +TEST(NeTest, ImplementsNotEqual) { + Matcher m1 = Ne(0); + EXPECT_TRUE(m1.Matches(1)); + EXPECT_TRUE(m1.Matches(-1)); + EXPECT_FALSE(m1.Matches(0)); +} + +// Tests that Ne(v) describes itself properly. +TEST(NeTest, CanDescribeSelf) { + Matcher m = Ne(5); + EXPECT_EQ("isn't equal to 5", Describe(m)); +} + +// Tests that IsNull() matches any NULL pointer of any type. +TEST(IsNullTest, MatchesNullPointer) { + Matcher m1 = IsNull(); + int* p1 = NULL; + int n = 0; + EXPECT_TRUE(m1.Matches(p1)); + EXPECT_FALSE(m1.Matches(&n)); + + Matcher m2 = IsNull(); + const char* p2 = NULL; + EXPECT_TRUE(m2.Matches(p2)); + EXPECT_FALSE(m2.Matches("hi")); + +#if !GTEST_OS_SYMBIAN + // Nokia's Symbian compiler generates: + // gmock-matchers.h: ambiguous access to overloaded function + // gmock-matchers.h: 'testing::Matcher::Matcher(void *)' + // gmock-matchers.h: 'testing::Matcher::Matcher(const testing:: + // MatcherInterface *)' + // gmock-matchers.h: (point of instantiation: 'testing:: + // gmock_matchers_test::IsNullTest_MatchesNullPointer_Test::TestBody()') + // gmock-matchers.h: (instantiating: 'testing::PolymorphicMatc + Matcher m3 = IsNull(); + void* p3 = NULL; + EXPECT_TRUE(m3.Matches(p3)); + EXPECT_FALSE(m3.Matches(reinterpret_cast(0xbeef))); +#endif +} + +TEST(IsNullTest, LinkedPtr) { + const Matcher > m = IsNull(); + const linked_ptr null_p; + const linked_ptr non_null_p(new int); + + EXPECT_TRUE(m.Matches(null_p)); + EXPECT_FALSE(m.Matches(non_null_p)); +} + +TEST(IsNullTest, ReferenceToConstLinkedPtr) { + const Matcher&> m = IsNull(); + const linked_ptr null_p; + const linked_ptr non_null_p(new double); + + EXPECT_TRUE(m.Matches(null_p)); + EXPECT_FALSE(m.Matches(non_null_p)); +} + +TEST(IsNullTest, ReferenceToConstScopedPtr) { + const Matcher&> m = IsNull(); + const scoped_ptr null_p; + const scoped_ptr non_null_p(new double); + + EXPECT_TRUE(m.Matches(null_p)); + EXPECT_FALSE(m.Matches(non_null_p)); +} + +// Tests that IsNull() describes itself properly. +TEST(IsNullTest, CanDescribeSelf) { + Matcher m = IsNull(); + EXPECT_EQ("is NULL", Describe(m)); + EXPECT_EQ("isn't NULL", DescribeNegation(m)); +} + +// Tests that NotNull() matches any non-NULL pointer of any type. +TEST(NotNullTest, MatchesNonNullPointer) { + Matcher m1 = NotNull(); + int* p1 = NULL; + int n = 0; + EXPECT_FALSE(m1.Matches(p1)); + EXPECT_TRUE(m1.Matches(&n)); + + Matcher m2 = NotNull(); + const char* p2 = NULL; + EXPECT_FALSE(m2.Matches(p2)); + EXPECT_TRUE(m2.Matches("hi")); +} + +TEST(NotNullTest, LinkedPtr) { + const Matcher > m = NotNull(); + const linked_ptr null_p; + const linked_ptr non_null_p(new int); + + EXPECT_FALSE(m.Matches(null_p)); + EXPECT_TRUE(m.Matches(non_null_p)); +} + +TEST(NotNullTest, ReferenceToConstLinkedPtr) { + const Matcher&> m = NotNull(); + const linked_ptr null_p; + const linked_ptr non_null_p(new double); + + EXPECT_FALSE(m.Matches(null_p)); + EXPECT_TRUE(m.Matches(non_null_p)); +} + +TEST(NotNullTest, ReferenceToConstScopedPtr) { + const Matcher&> m = NotNull(); + const scoped_ptr null_p; + const scoped_ptr non_null_p(new double); + + EXPECT_FALSE(m.Matches(null_p)); + EXPECT_TRUE(m.Matches(non_null_p)); +} + +// Tests that NotNull() describes itself properly. +TEST(NotNullTest, CanDescribeSelf) { + Matcher m = NotNull(); + EXPECT_EQ("isn't NULL", Describe(m)); +} + +// Tests that Ref(variable) matches an argument that references +// 'variable'. +TEST(RefTest, MatchesSameVariable) { + int a = 0; + int b = 0; + Matcher m = Ref(a); + EXPECT_TRUE(m.Matches(a)); + EXPECT_FALSE(m.Matches(b)); +} + +// Tests that Ref(variable) describes itself properly. +TEST(RefTest, CanDescribeSelf) { + int n = 5; + Matcher m = Ref(n); + stringstream ss; + ss << "references the variable @" << &n << " 5"; + EXPECT_EQ(string(ss.str()), Describe(m)); +} + +// Test that Ref(non_const_varialbe) can be used as a matcher for a +// const reference. +TEST(RefTest, CanBeUsedAsMatcherForConstReference) { + int a = 0; + int b = 0; + Matcher m = Ref(a); + EXPECT_TRUE(m.Matches(a)); + EXPECT_FALSE(m.Matches(b)); +} + +// Tests that Ref(variable) is covariant, i.e. Ref(derived) can be +// used wherever Ref(base) can be used (Ref(derived) is a sub-type +// of Ref(base), but not vice versa. + +TEST(RefTest, IsCovariant) { + Base base, base2; + Derived derived; + Matcher m1 = Ref(base); + EXPECT_TRUE(m1.Matches(base)); + EXPECT_FALSE(m1.Matches(base2)); + EXPECT_FALSE(m1.Matches(derived)); + + m1 = Ref(derived); + EXPECT_TRUE(m1.Matches(derived)); + EXPECT_FALSE(m1.Matches(base)); + EXPECT_FALSE(m1.Matches(base2)); +} + +TEST(RefTest, ExplainsResult) { + int n = 0; + EXPECT_THAT(Explain(Matcher(Ref(n)), n), + StartsWith("which is located @")); + + int m = 0; + EXPECT_THAT(Explain(Matcher(Ref(n)), m), + StartsWith("which is located @")); +} + +// Tests string comparison matchers. + +TEST(StrEqTest, MatchesEqualString) { + Matcher m = StrEq(string("Hello")); + EXPECT_TRUE(m.Matches("Hello")); + EXPECT_FALSE(m.Matches("hello")); + EXPECT_FALSE(m.Matches(NULL)); + + Matcher m2 = StrEq("Hello"); + EXPECT_TRUE(m2.Matches("Hello")); + EXPECT_FALSE(m2.Matches("Hi")); +} + +TEST(StrEqTest, CanDescribeSelf) { + Matcher m = StrEq("Hi-\'\"?\\\a\b\f\n\r\t\v\xD3"); + EXPECT_EQ("is equal to \"Hi-\'\\\"?\\\\\\a\\b\\f\\n\\r\\t\\v\\xD3\"", + Describe(m)); + + string str("01204500800"); + str[3] = '\0'; + Matcher m2 = StrEq(str); + EXPECT_EQ("is equal to \"012\\04500800\"", Describe(m2)); + str[0] = str[6] = str[7] = str[9] = str[10] = '\0'; + Matcher m3 = StrEq(str); + EXPECT_EQ("is equal to \"\\012\\045\\0\\08\\0\\0\"", Describe(m3)); +} + +TEST(StrNeTest, MatchesUnequalString) { + Matcher m = StrNe("Hello"); + EXPECT_TRUE(m.Matches("")); + EXPECT_TRUE(m.Matches(NULL)); + EXPECT_FALSE(m.Matches("Hello")); + + Matcher m2 = StrNe(string("Hello")); + EXPECT_TRUE(m2.Matches("hello")); + EXPECT_FALSE(m2.Matches("Hello")); +} + +TEST(StrNeTest, CanDescribeSelf) { + Matcher m = StrNe("Hi"); + EXPECT_EQ("isn't equal to \"Hi\"", Describe(m)); +} + +TEST(StrCaseEqTest, MatchesEqualStringIgnoringCase) { + Matcher m = StrCaseEq(string("Hello")); + EXPECT_TRUE(m.Matches("Hello")); + EXPECT_TRUE(m.Matches("hello")); + EXPECT_FALSE(m.Matches("Hi")); + EXPECT_FALSE(m.Matches(NULL)); + + Matcher m2 = StrCaseEq("Hello"); + EXPECT_TRUE(m2.Matches("hello")); + EXPECT_FALSE(m2.Matches("Hi")); +} + +TEST(StrCaseEqTest, MatchesEqualStringWith0IgnoringCase) { + string str1("oabocdooeoo"); + string str2("OABOCDOOEOO"); + Matcher m0 = StrCaseEq(str1); + EXPECT_FALSE(m0.Matches(str2 + string(1, '\0'))); + + str1[3] = str2[3] = '\0'; + Matcher m1 = StrCaseEq(str1); + EXPECT_TRUE(m1.Matches(str2)); + + str1[0] = str1[6] = str1[7] = str1[10] = '\0'; + str2[0] = str2[6] = str2[7] = str2[10] = '\0'; + Matcher m2 = StrCaseEq(str1); + str1[9] = str2[9] = '\0'; + EXPECT_FALSE(m2.Matches(str2)); + + Matcher m3 = StrCaseEq(str1); + EXPECT_TRUE(m3.Matches(str2)); + + EXPECT_FALSE(m3.Matches(str2 + "x")); + str2.append(1, '\0'); + EXPECT_FALSE(m3.Matches(str2)); + EXPECT_FALSE(m3.Matches(string(str2, 0, 9))); +} + +TEST(StrCaseEqTest, CanDescribeSelf) { + Matcher m = StrCaseEq("Hi"); + EXPECT_EQ("is equal to (ignoring case) \"Hi\"", Describe(m)); +} + +TEST(StrCaseNeTest, MatchesUnequalStringIgnoringCase) { + Matcher m = StrCaseNe("Hello"); + EXPECT_TRUE(m.Matches("Hi")); + EXPECT_TRUE(m.Matches(NULL)); + EXPECT_FALSE(m.Matches("Hello")); + EXPECT_FALSE(m.Matches("hello")); + + Matcher m2 = StrCaseNe(string("Hello")); + EXPECT_TRUE(m2.Matches("")); + EXPECT_FALSE(m2.Matches("Hello")); +} + +TEST(StrCaseNeTest, CanDescribeSelf) { + Matcher m = StrCaseNe("Hi"); + EXPECT_EQ("isn't equal to (ignoring case) \"Hi\"", Describe(m)); +} + +// Tests that HasSubstr() works for matching string-typed values. +TEST(HasSubstrTest, WorksForStringClasses) { + const Matcher m1 = HasSubstr("foo"); + EXPECT_TRUE(m1.Matches(string("I love food."))); + EXPECT_FALSE(m1.Matches(string("tofo"))); + + const Matcher m2 = HasSubstr("foo"); + EXPECT_TRUE(m2.Matches(std::string("I love food."))); + EXPECT_FALSE(m2.Matches(std::string("tofo"))); +} + +// Tests that HasSubstr() works for matching C-string-typed values. +TEST(HasSubstrTest, WorksForCStrings) { + const Matcher m1 = HasSubstr("foo"); + EXPECT_TRUE(m1.Matches(const_cast("I love food."))); + EXPECT_FALSE(m1.Matches(const_cast("tofo"))); + EXPECT_FALSE(m1.Matches(NULL)); + + const Matcher m2 = HasSubstr("foo"); + EXPECT_TRUE(m2.Matches("I love food.")); + EXPECT_FALSE(m2.Matches("tofo")); + EXPECT_FALSE(m2.Matches(NULL)); +} + +// Tests that HasSubstr(s) describes itself properly. +TEST(HasSubstrTest, CanDescribeSelf) { + Matcher m = HasSubstr("foo\n\""); + EXPECT_EQ("has substring \"foo\\n\\\"\"", Describe(m)); +} + +TEST(KeyTest, CanDescribeSelf) { + Matcher&> m = Key("foo"); + EXPECT_EQ("has a key that is equal to \"foo\"", Describe(m)); + EXPECT_EQ("doesn't have a key that is equal to \"foo\"", DescribeNegation(m)); +} + +TEST(KeyTest, ExplainsResult) { + Matcher > m = Key(GreaterThan(10)); + EXPECT_EQ("whose first field is a value which is 5 less than 10", + Explain(m, make_pair(5, true))); + EXPECT_EQ("whose first field is a value which is 5 more than 10", + Explain(m, make_pair(15, true))); +} + +TEST(KeyTest, MatchesCorrectly) { + pair p(25, "foo"); + EXPECT_THAT(p, Key(25)); + EXPECT_THAT(p, Not(Key(42))); + EXPECT_THAT(p, Key(Ge(20))); + EXPECT_THAT(p, Not(Key(Lt(25)))); +} + +TEST(KeyTest, SafelyCastsInnerMatcher) { + Matcher is_positive = Gt(0); + Matcher is_negative = Lt(0); + pair p('a', true); + EXPECT_THAT(p, Key(is_positive)); + EXPECT_THAT(p, Not(Key(is_negative))); +} + +TEST(KeyTest, InsideContainsUsingMap) { + map container; + container.insert(make_pair(1, 'a')); + container.insert(make_pair(2, 'b')); + container.insert(make_pair(4, 'c')); + EXPECT_THAT(container, Contains(Key(1))); + EXPECT_THAT(container, Not(Contains(Key(3)))); +} + +TEST(KeyTest, InsideContainsUsingMultimap) { + multimap container; + container.insert(make_pair(1, 'a')); + container.insert(make_pair(2, 'b')); + container.insert(make_pair(4, 'c')); + + EXPECT_THAT(container, Not(Contains(Key(25)))); + container.insert(make_pair(25, 'd')); + EXPECT_THAT(container, Contains(Key(25))); + container.insert(make_pair(25, 'e')); + EXPECT_THAT(container, Contains(Key(25))); + + EXPECT_THAT(container, Contains(Key(1))); + EXPECT_THAT(container, Not(Contains(Key(3)))); +} + +TEST(PairTest, Typing) { + // Test verifies the following type conversions can be compiled. + Matcher&> m1 = Pair("foo", 42); + Matcher > m2 = Pair("foo", 42); + Matcher > m3 = Pair("foo", 42); + + Matcher > m4 = Pair(25, "42"); + Matcher > m5 = Pair("25", 42); +} + +TEST(PairTest, CanDescribeSelf) { + Matcher&> m1 = Pair("foo", 42); + EXPECT_EQ("has a first field that is equal to \"foo\"" + ", and has a second field that is equal to 42", + Describe(m1)); + EXPECT_EQ("has a first field that isn't equal to \"foo\"" + ", or has a second field that isn't equal to 42", + DescribeNegation(m1)); + // Double and triple negation (1 or 2 times not and description of negation). + Matcher&> m2 = Not(Pair(Not(13), 42)); + EXPECT_EQ("has a first field that isn't equal to 13" + ", and has a second field that is equal to 42", + DescribeNegation(m2)); +} + +TEST(PairTest, CanExplainMatchResultTo) { + // If neither field matches, Pair() should explain about the first + // field. + const Matcher > m = Pair(GreaterThan(0), GreaterThan(0)); + EXPECT_EQ("whose first field does not match, which is 1 less than 0", + Explain(m, make_pair(-1, -2))); + + // If the first field matches but the second doesn't, Pair() should + // explain about the second field. + EXPECT_EQ("whose second field does not match, which is 2 less than 0", + Explain(m, make_pair(1, -2))); + + // If the first field doesn't match but the second does, Pair() + // should explain about the first field. + EXPECT_EQ("whose first field does not match, which is 1 less than 0", + Explain(m, make_pair(-1, 2))); + + // If both fields match, Pair() should explain about them both. + EXPECT_EQ("whose both fields match, where the first field is a value " + "which is 1 more than 0, and the second field is a value " + "which is 2 more than 0", + Explain(m, make_pair(1, 2))); + + // If only the first match has an explanation, only this explanation should + // be printed. + const Matcher > explain_first = Pair(GreaterThan(0), 0); + EXPECT_EQ("whose both fields match, where the first field is a value " + "which is 1 more than 0", + Explain(explain_first, make_pair(1, 0))); + + // If only the second match has an explanation, only this explanation should + // be printed. + const Matcher > explain_second = Pair(0, GreaterThan(0)); + EXPECT_EQ("whose both fields match, where the second field is a value " + "which is 1 more than 0", + Explain(explain_second, make_pair(0, 1))); +} + +TEST(PairTest, MatchesCorrectly) { + pair p(25, "foo"); + + // Both fields match. + EXPECT_THAT(p, Pair(25, "foo")); + EXPECT_THAT(p, Pair(Ge(20), HasSubstr("o"))); + + // 'first' doesnt' match, but 'second' matches. + EXPECT_THAT(p, Not(Pair(42, "foo"))); + EXPECT_THAT(p, Not(Pair(Lt(25), "foo"))); + + // 'first' matches, but 'second' doesn't match. + EXPECT_THAT(p, Not(Pair(25, "bar"))); + EXPECT_THAT(p, Not(Pair(25, Not("foo")))); + + // Neither field matches. + EXPECT_THAT(p, Not(Pair(13, "bar"))); + EXPECT_THAT(p, Not(Pair(Lt(13), HasSubstr("a")))); +} + +TEST(PairTest, SafelyCastsInnerMatchers) { + Matcher is_positive = Gt(0); + Matcher is_negative = Lt(0); + pair p('a', true); + EXPECT_THAT(p, Pair(is_positive, _)); + EXPECT_THAT(p, Not(Pair(is_negative, _))); + EXPECT_THAT(p, Pair(_, is_positive)); + EXPECT_THAT(p, Not(Pair(_, is_negative))); +} + +TEST(PairTest, InsideContainsUsingMap) { + map container; + container.insert(make_pair(1, 'a')); + container.insert(make_pair(2, 'b')); + container.insert(make_pair(4, 'c')); + EXPECT_THAT(container, Contains(Pair(1, 'a'))); + EXPECT_THAT(container, Contains(Pair(1, _))); + EXPECT_THAT(container, Contains(Pair(_, 'a'))); + EXPECT_THAT(container, Not(Contains(Pair(3, _)))); +} + +// Tests StartsWith(s). + +TEST(StartsWithTest, MatchesStringWithGivenPrefix) { + const Matcher m1 = StartsWith(string("")); + EXPECT_TRUE(m1.Matches("Hi")); + EXPECT_TRUE(m1.Matches("")); + EXPECT_FALSE(m1.Matches(NULL)); + + const Matcher m2 = StartsWith("Hi"); + EXPECT_TRUE(m2.Matches("Hi")); + EXPECT_TRUE(m2.Matches("Hi Hi!")); + EXPECT_TRUE(m2.Matches("High")); + EXPECT_FALSE(m2.Matches("H")); + EXPECT_FALSE(m2.Matches(" Hi")); +} + +TEST(StartsWithTest, CanDescribeSelf) { + Matcher m = StartsWith("Hi"); + EXPECT_EQ("starts with \"Hi\"", Describe(m)); +} + +// Tests EndsWith(s). + +TEST(EndsWithTest, MatchesStringWithGivenSuffix) { + const Matcher m1 = EndsWith(""); + EXPECT_TRUE(m1.Matches("Hi")); + EXPECT_TRUE(m1.Matches("")); + EXPECT_FALSE(m1.Matches(NULL)); + + const Matcher m2 = EndsWith(string("Hi")); + EXPECT_TRUE(m2.Matches("Hi")); + EXPECT_TRUE(m2.Matches("Wow Hi Hi")); + EXPECT_TRUE(m2.Matches("Super Hi")); + EXPECT_FALSE(m2.Matches("i")); + EXPECT_FALSE(m2.Matches("Hi ")); +} + +TEST(EndsWithTest, CanDescribeSelf) { + Matcher m = EndsWith("Hi"); + EXPECT_EQ("ends with \"Hi\"", Describe(m)); +} + +// Tests MatchesRegex(). + +TEST(MatchesRegexTest, MatchesStringMatchingGivenRegex) { + const Matcher m1 = MatchesRegex("a.*z"); + EXPECT_TRUE(m1.Matches("az")); + EXPECT_TRUE(m1.Matches("abcz")); + EXPECT_FALSE(m1.Matches(NULL)); + + const Matcher m2 = MatchesRegex(new RE("a.*z")); + EXPECT_TRUE(m2.Matches("azbz")); + EXPECT_FALSE(m2.Matches("az1")); + EXPECT_FALSE(m2.Matches("1az")); +} + +TEST(MatchesRegexTest, CanDescribeSelf) { + Matcher m1 = MatchesRegex(string("Hi.*")); + EXPECT_EQ("matches regular expression \"Hi.*\"", Describe(m1)); + + Matcher m2 = MatchesRegex(new RE("a.*")); + EXPECT_EQ("matches regular expression \"a.*\"", Describe(m2)); +} + +// Tests ContainsRegex(). + +TEST(ContainsRegexTest, MatchesStringContainingGivenRegex) { + const Matcher m1 = ContainsRegex(string("a.*z")); + EXPECT_TRUE(m1.Matches("az")); + EXPECT_TRUE(m1.Matches("0abcz1")); + EXPECT_FALSE(m1.Matches(NULL)); + + const Matcher m2 = ContainsRegex(new RE("a.*z")); + EXPECT_TRUE(m2.Matches("azbz")); + EXPECT_TRUE(m2.Matches("az1")); + EXPECT_FALSE(m2.Matches("1a")); +} + +TEST(ContainsRegexTest, CanDescribeSelf) { + Matcher m1 = ContainsRegex("Hi.*"); + EXPECT_EQ("contains regular expression \"Hi.*\"", Describe(m1)); + + Matcher m2 = ContainsRegex(new RE("a.*")); + EXPECT_EQ("contains regular expression \"a.*\"", Describe(m2)); +} + +// Tests for wide strings. +#if GTEST_HAS_STD_WSTRING +TEST(StdWideStrEqTest, MatchesEqual) { + Matcher m = StrEq(::std::wstring(L"Hello")); + EXPECT_TRUE(m.Matches(L"Hello")); + EXPECT_FALSE(m.Matches(L"hello")); + EXPECT_FALSE(m.Matches(NULL)); + + Matcher m2 = StrEq(L"Hello"); + EXPECT_TRUE(m2.Matches(L"Hello")); + EXPECT_FALSE(m2.Matches(L"Hi")); + + Matcher m3 = StrEq(L"\xD3\x576\x8D3\xC74D"); + EXPECT_TRUE(m3.Matches(L"\xD3\x576\x8D3\xC74D")); + EXPECT_FALSE(m3.Matches(L"\xD3\x576\x8D3\xC74E")); + + ::std::wstring str(L"01204500800"); + str[3] = L'\0'; + Matcher m4 = StrEq(str); + EXPECT_TRUE(m4.Matches(str)); + str[0] = str[6] = str[7] = str[9] = str[10] = L'\0'; + Matcher m5 = StrEq(str); + EXPECT_TRUE(m5.Matches(str)); +} + +TEST(StdWideStrEqTest, CanDescribeSelf) { + Matcher< ::std::wstring> m = StrEq(L"Hi-\'\"?\\\a\b\f\n\r\t\v"); + EXPECT_EQ("is equal to L\"Hi-\'\\\"?\\\\\\a\\b\\f\\n\\r\\t\\v\"", + Describe(m)); + + Matcher< ::std::wstring> m2 = StrEq(L"\xD3\x576\x8D3\xC74D"); + EXPECT_EQ("is equal to L\"\\xD3\\x576\\x8D3\\xC74D\"", + Describe(m2)); + + ::std::wstring str(L"01204500800"); + str[3] = L'\0'; + Matcher m4 = StrEq(str); + EXPECT_EQ("is equal to L\"012\\04500800\"", Describe(m4)); + str[0] = str[6] = str[7] = str[9] = str[10] = L'\0'; + Matcher m5 = StrEq(str); + EXPECT_EQ("is equal to L\"\\012\\045\\0\\08\\0\\0\"", Describe(m5)); +} + +TEST(StdWideStrNeTest, MatchesUnequalString) { + Matcher m = StrNe(L"Hello"); + EXPECT_TRUE(m.Matches(L"")); + EXPECT_TRUE(m.Matches(NULL)); + EXPECT_FALSE(m.Matches(L"Hello")); + + Matcher< ::std::wstring> m2 = StrNe(::std::wstring(L"Hello")); + EXPECT_TRUE(m2.Matches(L"hello")); + EXPECT_FALSE(m2.Matches(L"Hello")); +} + +TEST(StdWideStrNeTest, CanDescribeSelf) { + Matcher m = StrNe(L"Hi"); + EXPECT_EQ("isn't equal to L\"Hi\"", Describe(m)); +} + +TEST(StdWideStrCaseEqTest, MatchesEqualStringIgnoringCase) { + Matcher m = StrCaseEq(::std::wstring(L"Hello")); + EXPECT_TRUE(m.Matches(L"Hello")); + EXPECT_TRUE(m.Matches(L"hello")); + EXPECT_FALSE(m.Matches(L"Hi")); + EXPECT_FALSE(m.Matches(NULL)); + + Matcher m2 = StrCaseEq(L"Hello"); + EXPECT_TRUE(m2.Matches(L"hello")); + EXPECT_FALSE(m2.Matches(L"Hi")); +} + +TEST(StdWideStrCaseEqTest, MatchesEqualStringWith0IgnoringCase) { + ::std::wstring str1(L"oabocdooeoo"); + ::std::wstring str2(L"OABOCDOOEOO"); + Matcher m0 = StrCaseEq(str1); + EXPECT_FALSE(m0.Matches(str2 + ::std::wstring(1, L'\0'))); + + str1[3] = str2[3] = L'\0'; + Matcher m1 = StrCaseEq(str1); + EXPECT_TRUE(m1.Matches(str2)); + + str1[0] = str1[6] = str1[7] = str1[10] = L'\0'; + str2[0] = str2[6] = str2[7] = str2[10] = L'\0'; + Matcher m2 = StrCaseEq(str1); + str1[9] = str2[9] = L'\0'; + EXPECT_FALSE(m2.Matches(str2)); + + Matcher m3 = StrCaseEq(str1); + EXPECT_TRUE(m3.Matches(str2)); + + EXPECT_FALSE(m3.Matches(str2 + L"x")); + str2.append(1, L'\0'); + EXPECT_FALSE(m3.Matches(str2)); + EXPECT_FALSE(m3.Matches(::std::wstring(str2, 0, 9))); +} + +TEST(StdWideStrCaseEqTest, CanDescribeSelf) { + Matcher< ::std::wstring> m = StrCaseEq(L"Hi"); + EXPECT_EQ("is equal to (ignoring case) L\"Hi\"", Describe(m)); +} + +TEST(StdWideStrCaseNeTest, MatchesUnequalStringIgnoringCase) { + Matcher m = StrCaseNe(L"Hello"); + EXPECT_TRUE(m.Matches(L"Hi")); + EXPECT_TRUE(m.Matches(NULL)); + EXPECT_FALSE(m.Matches(L"Hello")); + EXPECT_FALSE(m.Matches(L"hello")); + + Matcher< ::std::wstring> m2 = StrCaseNe(::std::wstring(L"Hello")); + EXPECT_TRUE(m2.Matches(L"")); + EXPECT_FALSE(m2.Matches(L"Hello")); +} + +TEST(StdWideStrCaseNeTest, CanDescribeSelf) { + Matcher m = StrCaseNe(L"Hi"); + EXPECT_EQ("isn't equal to (ignoring case) L\"Hi\"", Describe(m)); +} + +// Tests that HasSubstr() works for matching wstring-typed values. +TEST(StdWideHasSubstrTest, WorksForStringClasses) { + const Matcher< ::std::wstring> m1 = HasSubstr(L"foo"); + EXPECT_TRUE(m1.Matches(::std::wstring(L"I love food."))); + EXPECT_FALSE(m1.Matches(::std::wstring(L"tofo"))); + + const Matcher m2 = HasSubstr(L"foo"); + EXPECT_TRUE(m2.Matches(::std::wstring(L"I love food."))); + EXPECT_FALSE(m2.Matches(::std::wstring(L"tofo"))); +} + +// Tests that HasSubstr() works for matching C-wide-string-typed values. +TEST(StdWideHasSubstrTest, WorksForCStrings) { + const Matcher m1 = HasSubstr(L"foo"); + EXPECT_TRUE(m1.Matches(const_cast(L"I love food."))); + EXPECT_FALSE(m1.Matches(const_cast(L"tofo"))); + EXPECT_FALSE(m1.Matches(NULL)); + + const Matcher m2 = HasSubstr(L"foo"); + EXPECT_TRUE(m2.Matches(L"I love food.")); + EXPECT_FALSE(m2.Matches(L"tofo")); + EXPECT_FALSE(m2.Matches(NULL)); +} + +// Tests that HasSubstr(s) describes itself properly. +TEST(StdWideHasSubstrTest, CanDescribeSelf) { + Matcher< ::std::wstring> m = HasSubstr(L"foo\n\""); + EXPECT_EQ("has substring L\"foo\\n\\\"\"", Describe(m)); +} + +// Tests StartsWith(s). + +TEST(StdWideStartsWithTest, MatchesStringWithGivenPrefix) { + const Matcher m1 = StartsWith(::std::wstring(L"")); + EXPECT_TRUE(m1.Matches(L"Hi")); + EXPECT_TRUE(m1.Matches(L"")); + EXPECT_FALSE(m1.Matches(NULL)); + + const Matcher m2 = StartsWith(L"Hi"); + EXPECT_TRUE(m2.Matches(L"Hi")); + EXPECT_TRUE(m2.Matches(L"Hi Hi!")); + EXPECT_TRUE(m2.Matches(L"High")); + EXPECT_FALSE(m2.Matches(L"H")); + EXPECT_FALSE(m2.Matches(L" Hi")); +} + +TEST(StdWideStartsWithTest, CanDescribeSelf) { + Matcher m = StartsWith(L"Hi"); + EXPECT_EQ("starts with L\"Hi\"", Describe(m)); +} + +// Tests EndsWith(s). + +TEST(StdWideEndsWithTest, MatchesStringWithGivenSuffix) { + const Matcher m1 = EndsWith(L""); + EXPECT_TRUE(m1.Matches(L"Hi")); + EXPECT_TRUE(m1.Matches(L"")); + EXPECT_FALSE(m1.Matches(NULL)); + + const Matcher m2 = EndsWith(::std::wstring(L"Hi")); + EXPECT_TRUE(m2.Matches(L"Hi")); + EXPECT_TRUE(m2.Matches(L"Wow Hi Hi")); + EXPECT_TRUE(m2.Matches(L"Super Hi")); + EXPECT_FALSE(m2.Matches(L"i")); + EXPECT_FALSE(m2.Matches(L"Hi ")); +} + +TEST(StdWideEndsWithTest, CanDescribeSelf) { + Matcher m = EndsWith(L"Hi"); + EXPECT_EQ("ends with L\"Hi\"", Describe(m)); +} + +#endif // GTEST_HAS_STD_WSTRING + +#if GTEST_HAS_GLOBAL_WSTRING +TEST(GlobalWideStrEqTest, MatchesEqual) { + Matcher m = StrEq(::wstring(L"Hello")); + EXPECT_TRUE(m.Matches(L"Hello")); + EXPECT_FALSE(m.Matches(L"hello")); + EXPECT_FALSE(m.Matches(NULL)); + + Matcher m2 = StrEq(L"Hello"); + EXPECT_TRUE(m2.Matches(L"Hello")); + EXPECT_FALSE(m2.Matches(L"Hi")); + + Matcher m3 = StrEq(L"\xD3\x576\x8D3\xC74D"); + EXPECT_TRUE(m3.Matches(L"\xD3\x576\x8D3\xC74D")); + EXPECT_FALSE(m3.Matches(L"\xD3\x576\x8D3\xC74E")); + + ::wstring str(L"01204500800"); + str[3] = L'\0'; + Matcher m4 = StrEq(str); + EXPECT_TRUE(m4.Matches(str)); + str[0] = str[6] = str[7] = str[9] = str[10] = L'\0'; + Matcher m5 = StrEq(str); + EXPECT_TRUE(m5.Matches(str)); +} + +TEST(GlobalWideStrEqTest, CanDescribeSelf) { + Matcher< ::wstring> m = StrEq(L"Hi-\'\"?\\\a\b\f\n\r\t\v"); + EXPECT_EQ("is equal to L\"Hi-\'\\\"?\\\\\\a\\b\\f\\n\\r\\t\\v\"", + Describe(m)); + + Matcher< ::wstring> m2 = StrEq(L"\xD3\x576\x8D3\xC74D"); + EXPECT_EQ("is equal to L\"\\xD3\\x576\\x8D3\\xC74D\"", + Describe(m2)); + + ::wstring str(L"01204500800"); + str[3] = L'\0'; + Matcher m4 = StrEq(str); + EXPECT_EQ("is equal to L\"012\\04500800\"", Describe(m4)); + str[0] = str[6] = str[7] = str[9] = str[10] = L'\0'; + Matcher m5 = StrEq(str); + EXPECT_EQ("is equal to L\"\\012\\045\\0\\08\\0\\0\"", Describe(m5)); +} + +TEST(GlobalWideStrNeTest, MatchesUnequalString) { + Matcher m = StrNe(L"Hello"); + EXPECT_TRUE(m.Matches(L"")); + EXPECT_TRUE(m.Matches(NULL)); + EXPECT_FALSE(m.Matches(L"Hello")); + + Matcher< ::wstring> m2 = StrNe(::wstring(L"Hello")); + EXPECT_TRUE(m2.Matches(L"hello")); + EXPECT_FALSE(m2.Matches(L"Hello")); +} + +TEST(GlobalWideStrNeTest, CanDescribeSelf) { + Matcher m = StrNe(L"Hi"); + EXPECT_EQ("isn't equal to L\"Hi\"", Describe(m)); +} + +TEST(GlobalWideStrCaseEqTest, MatchesEqualStringIgnoringCase) { + Matcher m = StrCaseEq(::wstring(L"Hello")); + EXPECT_TRUE(m.Matches(L"Hello")); + EXPECT_TRUE(m.Matches(L"hello")); + EXPECT_FALSE(m.Matches(L"Hi")); + EXPECT_FALSE(m.Matches(NULL)); + + Matcher m2 = StrCaseEq(L"Hello"); + EXPECT_TRUE(m2.Matches(L"hello")); + EXPECT_FALSE(m2.Matches(L"Hi")); +} + +TEST(GlobalWideStrCaseEqTest, MatchesEqualStringWith0IgnoringCase) { + ::wstring str1(L"oabocdooeoo"); + ::wstring str2(L"OABOCDOOEOO"); + Matcher m0 = StrCaseEq(str1); + EXPECT_FALSE(m0.Matches(str2 + ::wstring(1, L'\0'))); + + str1[3] = str2[3] = L'\0'; + Matcher m1 = StrCaseEq(str1); + EXPECT_TRUE(m1.Matches(str2)); + + str1[0] = str1[6] = str1[7] = str1[10] = L'\0'; + str2[0] = str2[6] = str2[7] = str2[10] = L'\0'; + Matcher m2 = StrCaseEq(str1); + str1[9] = str2[9] = L'\0'; + EXPECT_FALSE(m2.Matches(str2)); + + Matcher m3 = StrCaseEq(str1); + EXPECT_TRUE(m3.Matches(str2)); + + EXPECT_FALSE(m3.Matches(str2 + L"x")); + str2.append(1, L'\0'); + EXPECT_FALSE(m3.Matches(str2)); + EXPECT_FALSE(m3.Matches(::wstring(str2, 0, 9))); +} + +TEST(GlobalWideStrCaseEqTest, CanDescribeSelf) { + Matcher< ::wstring> m = StrCaseEq(L"Hi"); + EXPECT_EQ("is equal to (ignoring case) L\"Hi\"", Describe(m)); +} + +TEST(GlobalWideStrCaseNeTest, MatchesUnequalStringIgnoringCase) { + Matcher m = StrCaseNe(L"Hello"); + EXPECT_TRUE(m.Matches(L"Hi")); + EXPECT_TRUE(m.Matches(NULL)); + EXPECT_FALSE(m.Matches(L"Hello")); + EXPECT_FALSE(m.Matches(L"hello")); + + Matcher< ::wstring> m2 = StrCaseNe(::wstring(L"Hello")); + EXPECT_TRUE(m2.Matches(L"")); + EXPECT_FALSE(m2.Matches(L"Hello")); +} + +TEST(GlobalWideStrCaseNeTest, CanDescribeSelf) { + Matcher m = StrCaseNe(L"Hi"); + EXPECT_EQ("isn't equal to (ignoring case) L\"Hi\"", Describe(m)); +} + +// Tests that HasSubstr() works for matching wstring-typed values. +TEST(GlobalWideHasSubstrTest, WorksForStringClasses) { + const Matcher< ::wstring> m1 = HasSubstr(L"foo"); + EXPECT_TRUE(m1.Matches(::wstring(L"I love food."))); + EXPECT_FALSE(m1.Matches(::wstring(L"tofo"))); + + const Matcher m2 = HasSubstr(L"foo"); + EXPECT_TRUE(m2.Matches(::wstring(L"I love food."))); + EXPECT_FALSE(m2.Matches(::wstring(L"tofo"))); +} + +// Tests that HasSubstr() works for matching C-wide-string-typed values. +TEST(GlobalWideHasSubstrTest, WorksForCStrings) { + const Matcher m1 = HasSubstr(L"foo"); + EXPECT_TRUE(m1.Matches(const_cast(L"I love food."))); + EXPECT_FALSE(m1.Matches(const_cast(L"tofo"))); + EXPECT_FALSE(m1.Matches(NULL)); + + const Matcher m2 = HasSubstr(L"foo"); + EXPECT_TRUE(m2.Matches(L"I love food.")); + EXPECT_FALSE(m2.Matches(L"tofo")); + EXPECT_FALSE(m2.Matches(NULL)); +} + +// Tests that HasSubstr(s) describes itself properly. +TEST(GlobalWideHasSubstrTest, CanDescribeSelf) { + Matcher< ::wstring> m = HasSubstr(L"foo\n\""); + EXPECT_EQ("has substring L\"foo\\n\\\"\"", Describe(m)); +} + +// Tests StartsWith(s). + +TEST(GlobalWideStartsWithTest, MatchesStringWithGivenPrefix) { + const Matcher m1 = StartsWith(::wstring(L"")); + EXPECT_TRUE(m1.Matches(L"Hi")); + EXPECT_TRUE(m1.Matches(L"")); + EXPECT_FALSE(m1.Matches(NULL)); + + const Matcher m2 = StartsWith(L"Hi"); + EXPECT_TRUE(m2.Matches(L"Hi")); + EXPECT_TRUE(m2.Matches(L"Hi Hi!")); + EXPECT_TRUE(m2.Matches(L"High")); + EXPECT_FALSE(m2.Matches(L"H")); + EXPECT_FALSE(m2.Matches(L" Hi")); +} + +TEST(GlobalWideStartsWithTest, CanDescribeSelf) { + Matcher m = StartsWith(L"Hi"); + EXPECT_EQ("starts with L\"Hi\"", Describe(m)); +} + +// Tests EndsWith(s). + +TEST(GlobalWideEndsWithTest, MatchesStringWithGivenSuffix) { + const Matcher m1 = EndsWith(L""); + EXPECT_TRUE(m1.Matches(L"Hi")); + EXPECT_TRUE(m1.Matches(L"")); + EXPECT_FALSE(m1.Matches(NULL)); + + const Matcher m2 = EndsWith(::wstring(L"Hi")); + EXPECT_TRUE(m2.Matches(L"Hi")); + EXPECT_TRUE(m2.Matches(L"Wow Hi Hi")); + EXPECT_TRUE(m2.Matches(L"Super Hi")); + EXPECT_FALSE(m2.Matches(L"i")); + EXPECT_FALSE(m2.Matches(L"Hi ")); +} + +TEST(GlobalWideEndsWithTest, CanDescribeSelf) { + Matcher m = EndsWith(L"Hi"); + EXPECT_EQ("ends with L\"Hi\"", Describe(m)); +} + +#endif // GTEST_HAS_GLOBAL_WSTRING + + +typedef ::std::tr1::tuple Tuple2; // NOLINT + +// Tests that Eq() matches a 2-tuple where the first field == the +// second field. +TEST(Eq2Test, MatchesEqualArguments) { + Matcher m = Eq(); + EXPECT_TRUE(m.Matches(Tuple2(5L, 5))); + EXPECT_FALSE(m.Matches(Tuple2(5L, 6))); +} + +// Tests that Eq() describes itself properly. +TEST(Eq2Test, CanDescribeSelf) { + Matcher m = Eq(); + EXPECT_EQ("are an equal pair", Describe(m)); +} + +// Tests that Ge() matches a 2-tuple where the first field >= the +// second field. +TEST(Ge2Test, MatchesGreaterThanOrEqualArguments) { + Matcher m = Ge(); + EXPECT_TRUE(m.Matches(Tuple2(5L, 4))); + EXPECT_TRUE(m.Matches(Tuple2(5L, 5))); + EXPECT_FALSE(m.Matches(Tuple2(5L, 6))); +} + +// Tests that Ge() describes itself properly. +TEST(Ge2Test, CanDescribeSelf) { + Matcher m = Ge(); + EXPECT_EQ("are a pair where the first >= the second", Describe(m)); +} + +// Tests that Gt() matches a 2-tuple where the first field > the +// second field. +TEST(Gt2Test, MatchesGreaterThanArguments) { + Matcher m = Gt(); + EXPECT_TRUE(m.Matches(Tuple2(5L, 4))); + EXPECT_FALSE(m.Matches(Tuple2(5L, 5))); + EXPECT_FALSE(m.Matches(Tuple2(5L, 6))); +} + +// Tests that Gt() describes itself properly. +TEST(Gt2Test, CanDescribeSelf) { + Matcher m = Gt(); + EXPECT_EQ("are a pair where the first > the second", Describe(m)); +} + +// Tests that Le() matches a 2-tuple where the first field <= the +// second field. +TEST(Le2Test, MatchesLessThanOrEqualArguments) { + Matcher m = Le(); + EXPECT_TRUE(m.Matches(Tuple2(5L, 6))); + EXPECT_TRUE(m.Matches(Tuple2(5L, 5))); + EXPECT_FALSE(m.Matches(Tuple2(5L, 4))); +} + +// Tests that Le() describes itself properly. +TEST(Le2Test, CanDescribeSelf) { + Matcher m = Le(); + EXPECT_EQ("are a pair where the first <= the second", Describe(m)); +} + +// Tests that Lt() matches a 2-tuple where the first field < the +// second field. +TEST(Lt2Test, MatchesLessThanArguments) { + Matcher m = Lt(); + EXPECT_TRUE(m.Matches(Tuple2(5L, 6))); + EXPECT_FALSE(m.Matches(Tuple2(5L, 5))); + EXPECT_FALSE(m.Matches(Tuple2(5L, 4))); +} + +// Tests that Lt() describes itself properly. +TEST(Lt2Test, CanDescribeSelf) { + Matcher m = Lt(); + EXPECT_EQ("are a pair where the first < the second", Describe(m)); +} + +// Tests that Ne() matches a 2-tuple where the first field != the +// second field. +TEST(Ne2Test, MatchesUnequalArguments) { + Matcher m = Ne(); + EXPECT_TRUE(m.Matches(Tuple2(5L, 6))); + EXPECT_TRUE(m.Matches(Tuple2(5L, 4))); + EXPECT_FALSE(m.Matches(Tuple2(5L, 5))); +} + +// Tests that Ne() describes itself properly. +TEST(Ne2Test, CanDescribeSelf) { + Matcher m = Ne(); + EXPECT_EQ("are an unequal pair", Describe(m)); +} + +// Tests that Not(m) matches any value that doesn't match m. +TEST(NotTest, NegatesMatcher) { + Matcher m; + m = Not(Eq(2)); + EXPECT_TRUE(m.Matches(3)); + EXPECT_FALSE(m.Matches(2)); +} + +// Tests that Not(m) describes itself properly. +TEST(NotTest, CanDescribeSelf) { + Matcher m = Not(Eq(5)); + EXPECT_EQ("isn't equal to 5", Describe(m)); +} + +// Tests that monomorphic matchers are safely cast by the Not matcher. +TEST(NotTest, NotMatcherSafelyCastsMonomorphicMatchers) { + // greater_than_5 is a monomorphic matcher. + Matcher greater_than_5 = Gt(5); + + Matcher m = Not(greater_than_5); + Matcher m2 = Not(greater_than_5); + Matcher m3 = Not(m); +} + +// Helper to allow easy testing of AllOf matchers with num parameters. +void AllOfMatches(int num, const Matcher& m) { + SCOPED_TRACE(Describe(m)); + EXPECT_TRUE(m.Matches(0)); + for (int i = 1; i <= num; ++i) { + EXPECT_FALSE(m.Matches(i)); + } + EXPECT_TRUE(m.Matches(num + 1)); +} + +// Tests that AllOf(m1, ..., mn) matches any value that matches all of +// the given matchers. +TEST(AllOfTest, MatchesWhenAllMatch) { + Matcher m; + m = AllOf(Le(2), Ge(1)); + EXPECT_TRUE(m.Matches(1)); + EXPECT_TRUE(m.Matches(2)); + EXPECT_FALSE(m.Matches(0)); + EXPECT_FALSE(m.Matches(3)); + + m = AllOf(Gt(0), Ne(1), Ne(2)); + EXPECT_TRUE(m.Matches(3)); + EXPECT_FALSE(m.Matches(2)); + EXPECT_FALSE(m.Matches(1)); + EXPECT_FALSE(m.Matches(0)); + + m = AllOf(Gt(0), Ne(1), Ne(2), Ne(3)); + EXPECT_TRUE(m.Matches(4)); + EXPECT_FALSE(m.Matches(3)); + EXPECT_FALSE(m.Matches(2)); + EXPECT_FALSE(m.Matches(1)); + EXPECT_FALSE(m.Matches(0)); + + m = AllOf(Ge(0), Lt(10), Ne(3), Ne(5), Ne(7)); + EXPECT_TRUE(m.Matches(0)); + EXPECT_TRUE(m.Matches(1)); + EXPECT_FALSE(m.Matches(3)); + + // The following tests for varying number of sub-matchers. Due to the way + // the sub-matchers are handled it is enough to test every sub-matcher once + // with sub-matchers using the same matcher type. Varying matcher types are + // checked for above. + AllOfMatches(2, AllOf(Ne(1), Ne(2))); + AllOfMatches(3, AllOf(Ne(1), Ne(2), Ne(3))); + AllOfMatches(4, AllOf(Ne(1), Ne(2), Ne(3), Ne(4))); + AllOfMatches(5, AllOf(Ne(1), Ne(2), Ne(3), Ne(4), Ne(5))); + AllOfMatches(6, AllOf(Ne(1), Ne(2), Ne(3), Ne(4), Ne(5), Ne(6))); + AllOfMatches(7, AllOf(Ne(1), Ne(2), Ne(3), Ne(4), Ne(5), Ne(6), Ne(7))); + AllOfMatches(8, AllOf(Ne(1), Ne(2), Ne(3), Ne(4), Ne(5), Ne(6), Ne(7), + Ne(8))); + AllOfMatches(9, AllOf(Ne(1), Ne(2), Ne(3), Ne(4), Ne(5), Ne(6), Ne(7), + Ne(8), Ne(9))); + AllOfMatches(10, AllOf(Ne(1), Ne(2), Ne(3), Ne(4), Ne(5), Ne(6), Ne(7), Ne(8), + Ne(9), Ne(10))); +} + +// Tests that AllOf(m1, ..., mn) describes itself properly. +TEST(AllOfTest, CanDescribeSelf) { + Matcher m; + m = AllOf(Le(2), Ge(1)); + EXPECT_EQ("(is <= 2) and (is >= 1)", Describe(m)); + + m = AllOf(Gt(0), Ne(1), Ne(2)); + EXPECT_EQ("(is > 0) and " + "((isn't equal to 1) and " + "(isn't equal to 2))", + Describe(m)); + + + m = AllOf(Gt(0), Ne(1), Ne(2), Ne(3)); + EXPECT_EQ("(is > 0) and " + "((isn't equal to 1) and " + "((isn't equal to 2) and " + "(isn't equal to 3)))", + Describe(m)); + + + m = AllOf(Ge(0), Lt(10), Ne(3), Ne(5), Ne(7)); + EXPECT_EQ("(is >= 0) and " + "((is < 10) and " + "((isn't equal to 3) and " + "((isn't equal to 5) and " + "(isn't equal to 7))))", + Describe(m)); +} + +// Tests that AllOf(m1, ..., mn) describes its negation properly. +TEST(AllOfTest, CanDescribeNegation) { + Matcher m; + m = AllOf(Le(2), Ge(1)); + EXPECT_EQ("(isn't <= 2) or " + "(isn't >= 1)", + DescribeNegation(m)); + + m = AllOf(Gt(0), Ne(1), Ne(2)); + EXPECT_EQ("(isn't > 0) or " + "((is equal to 1) or " + "(is equal to 2))", + DescribeNegation(m)); + + + m = AllOf(Gt(0), Ne(1), Ne(2), Ne(3)); + EXPECT_EQ("(isn't > 0) or " + "((is equal to 1) or " + "((is equal to 2) or " + "(is equal to 3)))", + DescribeNegation(m)); + + + m = AllOf(Ge(0), Lt(10), Ne(3), Ne(5), Ne(7)); + EXPECT_EQ("(isn't >= 0) or " + "((isn't < 10) or " + "((is equal to 3) or " + "((is equal to 5) or " + "(is equal to 7))))", + DescribeNegation(m)); +} + +// Tests that monomorphic matchers are safely cast by the AllOf matcher. +TEST(AllOfTest, AllOfMatcherSafelyCastsMonomorphicMatchers) { + // greater_than_5 and less_than_10 are monomorphic matchers. + Matcher greater_than_5 = Gt(5); + Matcher less_than_10 = Lt(10); + + Matcher m = AllOf(greater_than_5, less_than_10); + Matcher m2 = AllOf(greater_than_5, less_than_10); + Matcher m3 = AllOf(greater_than_5, m2); + + // Tests that BothOf works when composing itself. + Matcher m4 = AllOf(greater_than_5, less_than_10, less_than_10); + Matcher m5 = AllOf(greater_than_5, less_than_10, less_than_10); +} + +TEST(AllOfTest, ExplainsResult) { + Matcher m; + + // Successful match. Both matchers need to explain. The second + // matcher doesn't give an explanation, so only the first matcher's + // explanation is printed. + m = AllOf(GreaterThan(10), Lt(30)); + EXPECT_EQ("which is 15 more than 10", Explain(m, 25)); + + // Successful match. Both matchers need to explain. + m = AllOf(GreaterThan(10), GreaterThan(20)); + EXPECT_EQ("which is 20 more than 10, and which is 10 more than 20", + Explain(m, 30)); + + // Successful match. All matchers need to explain. The second + // matcher doesn't given an explanation. + m = AllOf(GreaterThan(10), Lt(30), GreaterThan(20)); + EXPECT_EQ("which is 15 more than 10, and which is 5 more than 20", + Explain(m, 25)); + + // Successful match. All matchers need to explain. + m = AllOf(GreaterThan(10), GreaterThan(20), GreaterThan(30)); + EXPECT_EQ("which is 30 more than 10, and which is 20 more than 20, " + "and which is 10 more than 30", + Explain(m, 40)); + + // Failed match. The first matcher, which failed, needs to + // explain. + m = AllOf(GreaterThan(10), GreaterThan(20)); + EXPECT_EQ("which is 5 less than 10", Explain(m, 5)); + + // Failed match. The second matcher, which failed, needs to + // explain. Since it doesn't given an explanation, nothing is + // printed. + m = AllOf(GreaterThan(10), Lt(30)); + EXPECT_EQ("", Explain(m, 40)); + + // Failed match. The second matcher, which failed, needs to + // explain. + m = AllOf(GreaterThan(10), GreaterThan(20)); + EXPECT_EQ("which is 5 less than 20", Explain(m, 15)); +} + +// Helper to allow easy testing of AnyOf matchers with num parameters. +void AnyOfMatches(int num, const Matcher& m) { + SCOPED_TRACE(Describe(m)); + EXPECT_FALSE(m.Matches(0)); + for (int i = 1; i <= num; ++i) { + EXPECT_TRUE(m.Matches(i)); + } + EXPECT_FALSE(m.Matches(num + 1)); +} + +// Tests that AnyOf(m1, ..., mn) matches any value that matches at +// least one of the given matchers. +TEST(AnyOfTest, MatchesWhenAnyMatches) { + Matcher m; + m = AnyOf(Le(1), Ge(3)); + EXPECT_TRUE(m.Matches(1)); + EXPECT_TRUE(m.Matches(4)); + EXPECT_FALSE(m.Matches(2)); + + m = AnyOf(Lt(0), Eq(1), Eq(2)); + EXPECT_TRUE(m.Matches(-1)); + EXPECT_TRUE(m.Matches(1)); + EXPECT_TRUE(m.Matches(2)); + EXPECT_FALSE(m.Matches(0)); + + m = AnyOf(Lt(0), Eq(1), Eq(2), Eq(3)); + EXPECT_TRUE(m.Matches(-1)); + EXPECT_TRUE(m.Matches(1)); + EXPECT_TRUE(m.Matches(2)); + EXPECT_TRUE(m.Matches(3)); + EXPECT_FALSE(m.Matches(0)); + + m = AnyOf(Le(0), Gt(10), 3, 5, 7); + EXPECT_TRUE(m.Matches(0)); + EXPECT_TRUE(m.Matches(11)); + EXPECT_TRUE(m.Matches(3)); + EXPECT_FALSE(m.Matches(2)); + + // The following tests for varying number of sub-matchers. Due to the way + // the sub-matchers are handled it is enough to test every sub-matcher once + // with sub-matchers using the same matcher type. Varying matcher types are + // checked for above. + AnyOfMatches(2, AnyOf(1, 2)); + AnyOfMatches(3, AnyOf(1, 2, 3)); + AnyOfMatches(4, AnyOf(1, 2, 3, 4)); + AnyOfMatches(5, AnyOf(1, 2, 3, 4, 5)); + AnyOfMatches(6, AnyOf(1, 2, 3, 4, 5, 6)); + AnyOfMatches(7, AnyOf(1, 2, 3, 4, 5, 6, 7)); + AnyOfMatches(8, AnyOf(1, 2, 3, 4, 5, 6, 7, 8)); + AnyOfMatches(9, AnyOf(1, 2, 3, 4, 5, 6, 7, 8, 9)); + AnyOfMatches(10, AnyOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); +} + +// Tests that AnyOf(m1, ..., mn) describes itself properly. +TEST(AnyOfTest, CanDescribeSelf) { + Matcher m; + m = AnyOf(Le(1), Ge(3)); + EXPECT_EQ("(is <= 1) or (is >= 3)", + Describe(m)); + + m = AnyOf(Lt(0), Eq(1), Eq(2)); + EXPECT_EQ("(is < 0) or " + "((is equal to 1) or (is equal to 2))", + Describe(m)); + + m = AnyOf(Lt(0), Eq(1), Eq(2), Eq(3)); + EXPECT_EQ("(is < 0) or " + "((is equal to 1) or " + "((is equal to 2) or " + "(is equal to 3)))", + Describe(m)); + + m = AnyOf(Le(0), Gt(10), 3, 5, 7); + EXPECT_EQ("(is <= 0) or " + "((is > 10) or " + "((is equal to 3) or " + "((is equal to 5) or " + "(is equal to 7))))", + Describe(m)); +} + +// Tests that AnyOf(m1, ..., mn) describes its negation properly. +TEST(AnyOfTest, CanDescribeNegation) { + Matcher m; + m = AnyOf(Le(1), Ge(3)); + EXPECT_EQ("(isn't <= 1) and (isn't >= 3)", + DescribeNegation(m)); + + m = AnyOf(Lt(0), Eq(1), Eq(2)); + EXPECT_EQ("(isn't < 0) and " + "((isn't equal to 1) and (isn't equal to 2))", + DescribeNegation(m)); + + m = AnyOf(Lt(0), Eq(1), Eq(2), Eq(3)); + EXPECT_EQ("(isn't < 0) and " + "((isn't equal to 1) and " + "((isn't equal to 2) and " + "(isn't equal to 3)))", + DescribeNegation(m)); + + m = AnyOf(Le(0), Gt(10), 3, 5, 7); + EXPECT_EQ("(isn't <= 0) and " + "((isn't > 10) and " + "((isn't equal to 3) and " + "((isn't equal to 5) and " + "(isn't equal to 7))))", + DescribeNegation(m)); +} + +// Tests that monomorphic matchers are safely cast by the AnyOf matcher. +TEST(AnyOfTest, AnyOfMatcherSafelyCastsMonomorphicMatchers) { + // greater_than_5 and less_than_10 are monomorphic matchers. + Matcher greater_than_5 = Gt(5); + Matcher less_than_10 = Lt(10); + + Matcher m = AnyOf(greater_than_5, less_than_10); + Matcher m2 = AnyOf(greater_than_5, less_than_10); + Matcher m3 = AnyOf(greater_than_5, m2); + + // Tests that EitherOf works when composing itself. + Matcher m4 = AnyOf(greater_than_5, less_than_10, less_than_10); + Matcher m5 = AnyOf(greater_than_5, less_than_10, less_than_10); +} + +TEST(AnyOfTest, ExplainsResult) { + Matcher m; + + // Failed match. Both matchers need to explain. The second + // matcher doesn't give an explanation, so only the first matcher's + // explanation is printed. + m = AnyOf(GreaterThan(10), Lt(0)); + EXPECT_EQ("which is 5 less than 10", Explain(m, 5)); + + // Failed match. Both matchers need to explain. + m = AnyOf(GreaterThan(10), GreaterThan(20)); + EXPECT_EQ("which is 5 less than 10, and which is 15 less than 20", + Explain(m, 5)); + + // Failed match. All matchers need to explain. The second + // matcher doesn't given an explanation. + m = AnyOf(GreaterThan(10), Gt(20), GreaterThan(30)); + EXPECT_EQ("which is 5 less than 10, and which is 25 less than 30", + Explain(m, 5)); + + // Failed match. All matchers need to explain. + m = AnyOf(GreaterThan(10), GreaterThan(20), GreaterThan(30)); + EXPECT_EQ("which is 5 less than 10, and which is 15 less than 20, " + "and which is 25 less than 30", + Explain(m, 5)); + + // Successful match. The first matcher, which succeeded, needs to + // explain. + m = AnyOf(GreaterThan(10), GreaterThan(20)); + EXPECT_EQ("which is 5 more than 10", Explain(m, 15)); + + // Successful match. The second matcher, which succeeded, needs to + // explain. Since it doesn't given an explanation, nothing is + // printed. + m = AnyOf(GreaterThan(10), Lt(30)); + EXPECT_EQ("", Explain(m, 0)); + + // Successful match. The second matcher, which succeeded, needs to + // explain. + m = AnyOf(GreaterThan(30), GreaterThan(20)); + EXPECT_EQ("which is 5 more than 20", Explain(m, 25)); +} + +// The following predicate function and predicate functor are for +// testing the Truly(predicate) matcher. + +// Returns non-zero if the input is positive. Note that the return +// type of this function is not bool. It's OK as Truly() accepts any +// unary function or functor whose return type can be implicitly +// converted to bool. +int IsPositive(double x) { + return x > 0 ? 1 : 0; +} + +// This functor returns true if the input is greater than the given +// number. +class IsGreaterThan { + public: + explicit IsGreaterThan(int threshold) : threshold_(threshold) {} + + bool operator()(int n) const { return n > threshold_; } + + private: + int threshold_; +}; + +// For testing Truly(). +const int foo = 0; + +// This predicate returns true iff the argument references foo and has +// a zero value. +bool ReferencesFooAndIsZero(const int& n) { + return (&n == &foo) && (n == 0); +} + +// Tests that Truly(predicate) matches what satisfies the given +// predicate. +TEST(TrulyTest, MatchesWhatSatisfiesThePredicate) { + Matcher m = Truly(IsPositive); + EXPECT_TRUE(m.Matches(2.0)); + EXPECT_FALSE(m.Matches(-1.5)); +} + +// Tests that Truly(predicate_functor) works too. +TEST(TrulyTest, CanBeUsedWithFunctor) { + Matcher m = Truly(IsGreaterThan(5)); + EXPECT_TRUE(m.Matches(6)); + EXPECT_FALSE(m.Matches(4)); +} + +// A class that can be implicitly converted to bool. +class ConvertibleToBool { + public: + explicit ConvertibleToBool(int number) : number_(number) {} + operator bool() const { return number_ != 0; } + + private: + int number_; +}; + +ConvertibleToBool IsNotZero(int number) { + return ConvertibleToBool(number); +} + +// Tests that the predicate used in Truly() may return a class that's +// implicitly convertible to bool, even when the class has no +// operator!(). +TEST(TrulyTest, PredicateCanReturnAClassConvertibleToBool) { + Matcher m = Truly(IsNotZero); + EXPECT_TRUE(m.Matches(1)); + EXPECT_FALSE(m.Matches(0)); +} + +// Tests that Truly(predicate) can describe itself properly. +TEST(TrulyTest, CanDescribeSelf) { + Matcher m = Truly(IsPositive); + EXPECT_EQ("satisfies the given predicate", + Describe(m)); +} + +// Tests that Truly(predicate) works when the matcher takes its +// argument by reference. +TEST(TrulyTest, WorksForByRefArguments) { + Matcher m = Truly(ReferencesFooAndIsZero); + EXPECT_TRUE(m.Matches(foo)); + int n = 0; + EXPECT_FALSE(m.Matches(n)); +} + +// Tests that Matches(m) is a predicate satisfied by whatever that +// matches matcher m. +TEST(MatchesTest, IsSatisfiedByWhatMatchesTheMatcher) { + EXPECT_TRUE(Matches(Ge(0))(1)); + EXPECT_FALSE(Matches(Eq('a'))('b')); +} + +// Tests that Matches(m) works when the matcher takes its argument by +// reference. +TEST(MatchesTest, WorksOnByRefArguments) { + int m = 0, n = 0; + EXPECT_TRUE(Matches(AllOf(Ref(n), Eq(0)))(n)); + EXPECT_FALSE(Matches(Ref(m))(n)); +} + +// Tests that a Matcher on non-reference type can be used in +// Matches(). +TEST(MatchesTest, WorksWithMatcherOnNonRefType) { + Matcher eq5 = Eq(5); + EXPECT_TRUE(Matches(eq5)(5)); + EXPECT_FALSE(Matches(eq5)(2)); +} + +// Tests Value(value, matcher). Since Value() is a simple wrapper for +// Matches(), which has been tested already, we don't spend a lot of +// effort on testing Value(). +TEST(ValueTest, WorksWithPolymorphicMatcher) { + EXPECT_TRUE(Value("hi", StartsWith("h"))); + EXPECT_FALSE(Value(5, Gt(10))); +} + +TEST(ValueTest, WorksWithMonomorphicMatcher) { + const Matcher is_zero = Eq(0); + EXPECT_TRUE(Value(0, is_zero)); + EXPECT_FALSE(Value('a', is_zero)); + + int n = 0; + const Matcher ref_n = Ref(n); + EXPECT_TRUE(Value(n, ref_n)); + EXPECT_FALSE(Value(1, ref_n)); +} + +TEST(ExplainMatchResultTest, WorksWithPolymorphicMatcher) { + StringMatchResultListener listener1; + EXPECT_TRUE(ExplainMatchResult(PolymorphicIsEven(), 42, &listener1)); + EXPECT_EQ("% 2 == 0", listener1.str()); + + StringMatchResultListener listener2; + EXPECT_FALSE(ExplainMatchResult(Ge(42), 1.5, &listener2)); + EXPECT_EQ("", listener2.str()); +} + +TEST(ExplainMatchResultTest, WorksWithMonomorphicMatcher) { + const Matcher is_even = PolymorphicIsEven(); + StringMatchResultListener listener1; + EXPECT_TRUE(ExplainMatchResult(is_even, 42, &listener1)); + EXPECT_EQ("% 2 == 0", listener1.str()); + + const Matcher is_zero = Eq(0); + StringMatchResultListener listener2; + EXPECT_FALSE(ExplainMatchResult(is_zero, 1.5, &listener2)); + EXPECT_EQ("", listener2.str()); +} + +MATCHER_P(Really, inner_matcher, "") { + return ExplainMatchResult(inner_matcher, arg, result_listener); +} + +TEST(ExplainMatchResultTest, WorksInsideMATCHER) { + EXPECT_THAT(0, Really(Eq(0))); +} + +TEST(AllArgsTest, WorksForTuple) { + EXPECT_THAT(make_tuple(1, 2L), AllArgs(Lt())); + EXPECT_THAT(make_tuple(2L, 1), Not(AllArgs(Lt()))); +} + +TEST(AllArgsTest, WorksForNonTuple) { + EXPECT_THAT(42, AllArgs(Gt(0))); + EXPECT_THAT('a', Not(AllArgs(Eq('b')))); +} + +class AllArgsHelper { + public: + AllArgsHelper() {} + + MOCK_METHOD2(Helper, int(char x, int y)); + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(AllArgsHelper); +}; + +TEST(AllArgsTest, WorksInWithClause) { + AllArgsHelper helper; + ON_CALL(helper, Helper(_, _)) + .With(AllArgs(Lt())) + .WillByDefault(Return(1)); + EXPECT_CALL(helper, Helper(_, _)); + EXPECT_CALL(helper, Helper(_, _)) + .With(AllArgs(Gt())) + .WillOnce(Return(2)); + + EXPECT_EQ(1, helper.Helper('\1', 2)); + EXPECT_EQ(2, helper.Helper('a', 1)); +} + +// Tests that ASSERT_THAT() and EXPECT_THAT() work when the value +// matches the matcher. +TEST(MatcherAssertionTest, WorksWhenMatcherIsSatisfied) { + ASSERT_THAT(5, Ge(2)) << "This should succeed."; + ASSERT_THAT("Foo", EndsWith("oo")); + EXPECT_THAT(2, AllOf(Le(7), Ge(0))) << "This should succeed too."; + EXPECT_THAT("Hello", StartsWith("Hell")); +} + +// Tests that ASSERT_THAT() and EXPECT_THAT() work when the value +// doesn't match the matcher. +TEST(MatcherAssertionTest, WorksWhenMatcherIsNotSatisfied) { + // 'n' must be static as it is used in an EXPECT_FATAL_FAILURE(), + // which cannot reference auto variables. + static unsigned short n; // NOLINT + n = 5; + + // VC++ prior to version 8.0 SP1 has a bug where it will not see any + // functions declared in the namespace scope from within nested classes. + // EXPECT/ASSERT_(NON)FATAL_FAILURE macros use nested classes so that all + // namespace-level functions invoked inside them need to be explicitly + // resolved. + EXPECT_FATAL_FAILURE(ASSERT_THAT(n, ::testing::Gt(10)), + "Value of: n\n" + "Expected: is > 10\n" + " Actual: 5" + OfType("unsigned short")); + n = 0; + EXPECT_NONFATAL_FAILURE( + EXPECT_THAT(n, ::testing::AllOf(::testing::Le(7), ::testing::Ge(5))), + "Value of: n\n" + "Expected: (is <= 7) and (is >= 5)\n" + " Actual: 0" + OfType("unsigned short")); +} + +// Tests that ASSERT_THAT() and EXPECT_THAT() work when the argument +// has a reference type. +TEST(MatcherAssertionTest, WorksForByRefArguments) { + // We use a static variable here as EXPECT_FATAL_FAILURE() cannot + // reference auto variables. + static int n; + n = 0; + EXPECT_THAT(n, AllOf(Le(7), Ref(n))); + EXPECT_FATAL_FAILURE(ASSERT_THAT(n, ::testing::Not(::testing::Ref(n))), + "Value of: n\n" + "Expected: does not reference the variable @"); + // Tests the "Actual" part. + EXPECT_FATAL_FAILURE(ASSERT_THAT(n, ::testing::Not(::testing::Ref(n))), + "Actual: 0" + OfType("int") + ", which is located @"); +} + +#if !GTEST_OS_SYMBIAN +// Tests that ASSERT_THAT() and EXPECT_THAT() work when the matcher is +// monomorphic. + +// ASSERT_THAT("hello", starts_with_he) fails to compile with Nokia's +// Symbian compiler: it tries to compile +// template class MatcherCastImpl { ... +// virtual bool MatchAndExplain(T x, ...) const { +// return source_matcher_.MatchAndExplain(static_cast(x), ...); +// with U == string and T == const char* +// With ASSERT_THAT("hello"...) changed to ASSERT_THAT(string("hello") ... ) +// the compiler silently crashes with no output. +// If MatcherCastImpl is changed to use U(x) instead of static_cast(x) +// the code compiles but the converted string is bogus. +TEST(MatcherAssertionTest, WorksForMonomorphicMatcher) { + Matcher starts_with_he = StartsWith("he"); + ASSERT_THAT("hello", starts_with_he); + + Matcher ends_with_ok = EndsWith("ok"); + ASSERT_THAT("book", ends_with_ok); + const string bad = "bad"; + EXPECT_NONFATAL_FAILURE(EXPECT_THAT(bad, ends_with_ok), + "Value of: bad\n" + "Expected: ends with \"ok\"\n" + " Actual: \"bad\""); + Matcher is_greater_than_5 = Gt(5); + EXPECT_NONFATAL_FAILURE(EXPECT_THAT(5, is_greater_than_5), + "Value of: 5\n" + "Expected: is > 5\n" + " Actual: 5" + OfType("int")); +} +#endif // !GTEST_OS_SYMBIAN + +// Tests floating-point matchers. +template +class FloatingPointTest : public testing::Test { + protected: + typedef typename testing::internal::FloatingPoint Floating; + typedef typename Floating::Bits Bits; + + virtual void SetUp() { + const size_t max_ulps = Floating::kMaxUlps; + + // The bits that represent 0.0. + const Bits zero_bits = Floating(0).bits(); + + // Makes some numbers close to 0.0. + close_to_positive_zero_ = Floating::ReinterpretBits(zero_bits + max_ulps/2); + close_to_negative_zero_ = -Floating::ReinterpretBits( + zero_bits + max_ulps - max_ulps/2); + further_from_negative_zero_ = -Floating::ReinterpretBits( + zero_bits + max_ulps + 1 - max_ulps/2); + + // The bits that represent 1.0. + const Bits one_bits = Floating(1).bits(); + + // Makes some numbers close to 1.0. + close_to_one_ = Floating::ReinterpretBits(one_bits + max_ulps); + further_from_one_ = Floating::ReinterpretBits(one_bits + max_ulps + 1); + + // +infinity. + infinity_ = Floating::Infinity(); + + // The bits that represent +infinity. + const Bits infinity_bits = Floating(infinity_).bits(); + + // Makes some numbers close to infinity. + close_to_infinity_ = Floating::ReinterpretBits(infinity_bits - max_ulps); + further_from_infinity_ = Floating::ReinterpretBits( + infinity_bits - max_ulps - 1); + + // Makes some NAN's. + nan1_ = Floating::ReinterpretBits(Floating::kExponentBitMask | 1); + nan2_ = Floating::ReinterpretBits(Floating::kExponentBitMask | 200); + } + + void TestSize() { + EXPECT_EQ(sizeof(RawType), sizeof(Bits)); + } + + // A battery of tests for FloatingEqMatcher::Matches. + // matcher_maker is a pointer to a function which creates a FloatingEqMatcher. + void TestMatches( + testing::internal::FloatingEqMatcher (*matcher_maker)(RawType)) { + Matcher m1 = matcher_maker(0.0); + EXPECT_TRUE(m1.Matches(-0.0)); + EXPECT_TRUE(m1.Matches(close_to_positive_zero_)); + EXPECT_TRUE(m1.Matches(close_to_negative_zero_)); + EXPECT_FALSE(m1.Matches(1.0)); + + Matcher m2 = matcher_maker(close_to_positive_zero_); + EXPECT_FALSE(m2.Matches(further_from_negative_zero_)); + + Matcher m3 = matcher_maker(1.0); + EXPECT_TRUE(m3.Matches(close_to_one_)); + EXPECT_FALSE(m3.Matches(further_from_one_)); + + // Test commutativity: matcher_maker(0.0).Matches(1.0) was tested above. + EXPECT_FALSE(m3.Matches(0.0)); + + Matcher m4 = matcher_maker(-infinity_); + EXPECT_TRUE(m4.Matches(-close_to_infinity_)); + + Matcher m5 = matcher_maker(infinity_); + EXPECT_TRUE(m5.Matches(close_to_infinity_)); + + // This is interesting as the representations of infinity_ and nan1_ + // are only 1 DLP apart. + EXPECT_FALSE(m5.Matches(nan1_)); + + // matcher_maker can produce a Matcher, which is needed in + // some cases. + Matcher m6 = matcher_maker(0.0); + EXPECT_TRUE(m6.Matches(-0.0)); + EXPECT_TRUE(m6.Matches(close_to_positive_zero_)); + EXPECT_FALSE(m6.Matches(1.0)); + + // matcher_maker can produce a Matcher, which is needed in some + // cases. + Matcher m7 = matcher_maker(0.0); + RawType x = 0.0; + EXPECT_TRUE(m7.Matches(x)); + x = 0.01f; + EXPECT_FALSE(m7.Matches(x)); + } + + // Pre-calculated numbers to be used by the tests. + + static RawType close_to_positive_zero_; + static RawType close_to_negative_zero_; + static RawType further_from_negative_zero_; + + static RawType close_to_one_; + static RawType further_from_one_; + + static RawType infinity_; + static RawType close_to_infinity_; + static RawType further_from_infinity_; + + static RawType nan1_; + static RawType nan2_; +}; + +template +RawType FloatingPointTest::close_to_positive_zero_; + +template +RawType FloatingPointTest::close_to_negative_zero_; + +template +RawType FloatingPointTest::further_from_negative_zero_; + +template +RawType FloatingPointTest::close_to_one_; + +template +RawType FloatingPointTest::further_from_one_; + +template +RawType FloatingPointTest::infinity_; + +template +RawType FloatingPointTest::close_to_infinity_; + +template +RawType FloatingPointTest::further_from_infinity_; + +template +RawType FloatingPointTest::nan1_; + +template +RawType FloatingPointTest::nan2_; + +// Instantiate FloatingPointTest for testing floats. +typedef FloatingPointTest FloatTest; + +TEST_F(FloatTest, FloatEqApproximatelyMatchesFloats) { + TestMatches(&FloatEq); +} + +TEST_F(FloatTest, NanSensitiveFloatEqApproximatelyMatchesFloats) { + TestMatches(&NanSensitiveFloatEq); +} + +TEST_F(FloatTest, FloatEqCannotMatchNaN) { + // FloatEq never matches NaN. + Matcher m = FloatEq(nan1_); + EXPECT_FALSE(m.Matches(nan1_)); + EXPECT_FALSE(m.Matches(nan2_)); + EXPECT_FALSE(m.Matches(1.0)); +} + +TEST_F(FloatTest, NanSensitiveFloatEqCanMatchNaN) { + // NanSensitiveFloatEq will match NaN. + Matcher m = NanSensitiveFloatEq(nan1_); + EXPECT_TRUE(m.Matches(nan1_)); + EXPECT_TRUE(m.Matches(nan2_)); + EXPECT_FALSE(m.Matches(1.0)); +} + +TEST_F(FloatTest, FloatEqCanDescribeSelf) { + Matcher m1 = FloatEq(2.0f); + EXPECT_EQ("is approximately 2", Describe(m1)); + EXPECT_EQ("isn't approximately 2", DescribeNegation(m1)); + + Matcher m2 = FloatEq(0.5f); + EXPECT_EQ("is approximately 0.5", Describe(m2)); + EXPECT_EQ("isn't approximately 0.5", DescribeNegation(m2)); + + Matcher m3 = FloatEq(nan1_); + EXPECT_EQ("never matches", Describe(m3)); + EXPECT_EQ("is anything", DescribeNegation(m3)); +} + +TEST_F(FloatTest, NanSensitiveFloatEqCanDescribeSelf) { + Matcher m1 = NanSensitiveFloatEq(2.0f); + EXPECT_EQ("is approximately 2", Describe(m1)); + EXPECT_EQ("isn't approximately 2", DescribeNegation(m1)); + + Matcher m2 = NanSensitiveFloatEq(0.5f); + EXPECT_EQ("is approximately 0.5", Describe(m2)); + EXPECT_EQ("isn't approximately 0.5", DescribeNegation(m2)); + + Matcher m3 = NanSensitiveFloatEq(nan1_); + EXPECT_EQ("is NaN", Describe(m3)); + EXPECT_EQ("isn't NaN", DescribeNegation(m3)); +} + +// Instantiate FloatingPointTest for testing doubles. +typedef FloatingPointTest DoubleTest; + +TEST_F(DoubleTest, DoubleEqApproximatelyMatchesDoubles) { + TestMatches(&DoubleEq); +} + +TEST_F(DoubleTest, NanSensitiveDoubleEqApproximatelyMatchesDoubles) { + TestMatches(&NanSensitiveDoubleEq); +} + +TEST_F(DoubleTest, DoubleEqCannotMatchNaN) { + // DoubleEq never matches NaN. + Matcher m = DoubleEq(nan1_); + EXPECT_FALSE(m.Matches(nan1_)); + EXPECT_FALSE(m.Matches(nan2_)); + EXPECT_FALSE(m.Matches(1.0)); +} + +TEST_F(DoubleTest, NanSensitiveDoubleEqCanMatchNaN) { + // NanSensitiveDoubleEq will match NaN. + Matcher m = NanSensitiveDoubleEq(nan1_); + EXPECT_TRUE(m.Matches(nan1_)); + EXPECT_TRUE(m.Matches(nan2_)); + EXPECT_FALSE(m.Matches(1.0)); +} + +TEST_F(DoubleTest, DoubleEqCanDescribeSelf) { + Matcher m1 = DoubleEq(2.0); + EXPECT_EQ("is approximately 2", Describe(m1)); + EXPECT_EQ("isn't approximately 2", DescribeNegation(m1)); + + Matcher m2 = DoubleEq(0.5); + EXPECT_EQ("is approximately 0.5", Describe(m2)); + EXPECT_EQ("isn't approximately 0.5", DescribeNegation(m2)); + + Matcher m3 = DoubleEq(nan1_); + EXPECT_EQ("never matches", Describe(m3)); + EXPECT_EQ("is anything", DescribeNegation(m3)); +} + +TEST_F(DoubleTest, NanSensitiveDoubleEqCanDescribeSelf) { + Matcher m1 = NanSensitiveDoubleEq(2.0); + EXPECT_EQ("is approximately 2", Describe(m1)); + EXPECT_EQ("isn't approximately 2", DescribeNegation(m1)); + + Matcher m2 = NanSensitiveDoubleEq(0.5); + EXPECT_EQ("is approximately 0.5", Describe(m2)); + EXPECT_EQ("isn't approximately 0.5", DescribeNegation(m2)); + + Matcher m3 = NanSensitiveDoubleEq(nan1_); + EXPECT_EQ("is NaN", Describe(m3)); + EXPECT_EQ("isn't NaN", DescribeNegation(m3)); +} + +TEST(PointeeTest, RawPointer) { + const Matcher m = Pointee(Ge(0)); + + int n = 1; + EXPECT_TRUE(m.Matches(&n)); + n = -1; + EXPECT_FALSE(m.Matches(&n)); + EXPECT_FALSE(m.Matches(NULL)); +} + +TEST(PointeeTest, RawPointerToConst) { + const Matcher m = Pointee(Ge(0)); + + double x = 1; + EXPECT_TRUE(m.Matches(&x)); + x = -1; + EXPECT_FALSE(m.Matches(&x)); + EXPECT_FALSE(m.Matches(NULL)); +} + +TEST(PointeeTest, ReferenceToConstRawPointer) { + const Matcher m = Pointee(Ge(0)); + + int n = 1; + EXPECT_TRUE(m.Matches(&n)); + n = -1; + EXPECT_FALSE(m.Matches(&n)); + EXPECT_FALSE(m.Matches(NULL)); +} + +TEST(PointeeTest, ReferenceToNonConstRawPointer) { + const Matcher m = Pointee(Ge(0)); + + double x = 1.0; + double* p = &x; + EXPECT_TRUE(m.Matches(p)); + x = -1; + EXPECT_FALSE(m.Matches(p)); + p = NULL; + EXPECT_FALSE(m.Matches(p)); +} + +TEST(PointeeTest, NeverMatchesNull) { + const Matcher m = Pointee(_); + EXPECT_FALSE(m.Matches(NULL)); +} + +// Tests that we can write Pointee(value) instead of Pointee(Eq(value)). +TEST(PointeeTest, MatchesAgainstAValue) { + const Matcher m = Pointee(5); + + int n = 5; + EXPECT_TRUE(m.Matches(&n)); + n = -1; + EXPECT_FALSE(m.Matches(&n)); + EXPECT_FALSE(m.Matches(NULL)); +} + +TEST(PointeeTest, CanDescribeSelf) { + const Matcher m = Pointee(Gt(3)); + EXPECT_EQ("points to a value that is > 3", Describe(m)); + EXPECT_EQ("does not point to a value that is > 3", + DescribeNegation(m)); +} + +TEST(PointeeTest, CanExplainMatchResult) { + const Matcher m = Pointee(StartsWith("Hi")); + + EXPECT_EQ("", Explain(m, static_cast(NULL))); + + const Matcher m2 = Pointee(GreaterThan(1)); // NOLINT + long n = 3; // NOLINT + EXPECT_EQ("which points to 3" + OfType("long") + ", which is 2 more than 1", + Explain(m2, &n)); +} + +TEST(PointeeTest, AlwaysExplainsPointee) { + const Matcher m = Pointee(0); + int n = 42; + EXPECT_EQ("which points to 42" + OfType("int"), Explain(m, &n)); +} + +// An uncopyable class. +class Uncopyable { + public: + explicit Uncopyable(int a_value) : value_(a_value) {} + + int value() const { return value_; } + private: + const int value_; + GTEST_DISALLOW_COPY_AND_ASSIGN_(Uncopyable); +}; + +// Returns true iff x.value() is positive. +bool ValueIsPositive(const Uncopyable& x) { return x.value() > 0; } + +// A user-defined struct for testing Field(). +struct AStruct { + AStruct() : x(0), y(1.0), z(5), p(NULL) {} + AStruct(const AStruct& rhs) + : x(rhs.x), y(rhs.y), z(rhs.z.value()), p(rhs.p) {} + + int x; // A non-const field. + const double y; // A const field. + Uncopyable z; // An uncopyable field. + const char* p; // A pointer field. + + private: + GTEST_DISALLOW_ASSIGN_(AStruct); +}; + +// A derived struct for testing Field(). +struct DerivedStruct : public AStruct { + char ch; + + private: + GTEST_DISALLOW_ASSIGN_(DerivedStruct); +}; + +// Tests that Field(&Foo::field, ...) works when field is non-const. +TEST(FieldTest, WorksForNonConstField) { + Matcher m = Field(&AStruct::x, Ge(0)); + + AStruct a; + EXPECT_TRUE(m.Matches(a)); + a.x = -1; + EXPECT_FALSE(m.Matches(a)); +} + +// Tests that Field(&Foo::field, ...) works when field is const. +TEST(FieldTest, WorksForConstField) { + AStruct a; + + Matcher m = Field(&AStruct::y, Ge(0.0)); + EXPECT_TRUE(m.Matches(a)); + m = Field(&AStruct::y, Le(0.0)); + EXPECT_FALSE(m.Matches(a)); +} + +// Tests that Field(&Foo::field, ...) works when field is not copyable. +TEST(FieldTest, WorksForUncopyableField) { + AStruct a; + + Matcher m = Field(&AStruct::z, Truly(ValueIsPositive)); + EXPECT_TRUE(m.Matches(a)); + m = Field(&AStruct::z, Not(Truly(ValueIsPositive))); + EXPECT_FALSE(m.Matches(a)); +} + +// Tests that Field(&Foo::field, ...) works when field is a pointer. +TEST(FieldTest, WorksForPointerField) { + // Matching against NULL. + Matcher m = Field(&AStruct::p, static_cast(NULL)); + AStruct a; + EXPECT_TRUE(m.Matches(a)); + a.p = "hi"; + EXPECT_FALSE(m.Matches(a)); + + // Matching a pointer that is not NULL. + m = Field(&AStruct::p, StartsWith("hi")); + a.p = "hill"; + EXPECT_TRUE(m.Matches(a)); + a.p = "hole"; + EXPECT_FALSE(m.Matches(a)); +} + +// Tests that Field() works when the object is passed by reference. +TEST(FieldTest, WorksForByRefArgument) { + Matcher m = Field(&AStruct::x, Ge(0)); + + AStruct a; + EXPECT_TRUE(m.Matches(a)); + a.x = -1; + EXPECT_FALSE(m.Matches(a)); +} + +// Tests that Field(&Foo::field, ...) works when the argument's type +// is a sub-type of Foo. +TEST(FieldTest, WorksForArgumentOfSubType) { + // Note that the matcher expects DerivedStruct but we say AStruct + // inside Field(). + Matcher m = Field(&AStruct::x, Ge(0)); + + DerivedStruct d; + EXPECT_TRUE(m.Matches(d)); + d.x = -1; + EXPECT_FALSE(m.Matches(d)); +} + +// Tests that Field(&Foo::field, m) works when field's type and m's +// argument type are compatible but not the same. +TEST(FieldTest, WorksForCompatibleMatcherType) { + // The field is an int, but the inner matcher expects a signed char. + Matcher m = Field(&AStruct::x, + Matcher(Ge(0))); + + AStruct a; + EXPECT_TRUE(m.Matches(a)); + a.x = -1; + EXPECT_FALSE(m.Matches(a)); +} + +// Tests that Field() can describe itself. +TEST(FieldTest, CanDescribeSelf) { + Matcher m = Field(&AStruct::x, Ge(0)); + + EXPECT_EQ("is an object whose given field is >= 0", Describe(m)); + EXPECT_EQ("is an object whose given field isn't >= 0", DescribeNegation(m)); +} + +// Tests that Field() can explain the match result. +TEST(FieldTest, CanExplainMatchResult) { + Matcher m = Field(&AStruct::x, Ge(0)); + + AStruct a; + a.x = 1; + EXPECT_EQ("whose given field is 1" + OfType("int"), Explain(m, a)); + + m = Field(&AStruct::x, GreaterThan(0)); + EXPECT_EQ( + "whose given field is 1" + OfType("int") + ", which is 1 more than 0", + Explain(m, a)); +} + +// Tests that Field() works when the argument is a pointer to const. +TEST(FieldForPointerTest, WorksForPointerToConst) { + Matcher m = Field(&AStruct::x, Ge(0)); + + AStruct a; + EXPECT_TRUE(m.Matches(&a)); + a.x = -1; + EXPECT_FALSE(m.Matches(&a)); +} + +// Tests that Field() works when the argument is a pointer to non-const. +TEST(FieldForPointerTest, WorksForPointerToNonConst) { + Matcher m = Field(&AStruct::x, Ge(0)); + + AStruct a; + EXPECT_TRUE(m.Matches(&a)); + a.x = -1; + EXPECT_FALSE(m.Matches(&a)); +} + +// Tests that Field() works when the argument is a reference to a const pointer. +TEST(FieldForPointerTest, WorksForReferenceToConstPointer) { + Matcher m = Field(&AStruct::x, Ge(0)); + + AStruct a; + EXPECT_TRUE(m.Matches(&a)); + a.x = -1; + EXPECT_FALSE(m.Matches(&a)); +} + +// Tests that Field() does not match the NULL pointer. +TEST(FieldForPointerTest, DoesNotMatchNull) { + Matcher m = Field(&AStruct::x, _); + EXPECT_FALSE(m.Matches(NULL)); +} + +// Tests that Field(&Foo::field, ...) works when the argument's type +// is a sub-type of const Foo*. +TEST(FieldForPointerTest, WorksForArgumentOfSubType) { + // Note that the matcher expects DerivedStruct but we say AStruct + // inside Field(). + Matcher m = Field(&AStruct::x, Ge(0)); + + DerivedStruct d; + EXPECT_TRUE(m.Matches(&d)); + d.x = -1; + EXPECT_FALSE(m.Matches(&d)); +} + +// Tests that Field() can describe itself when used to match a pointer. +TEST(FieldForPointerTest, CanDescribeSelf) { + Matcher m = Field(&AStruct::x, Ge(0)); + + EXPECT_EQ("is an object whose given field is >= 0", Describe(m)); + EXPECT_EQ("is an object whose given field isn't >= 0", DescribeNegation(m)); +} + +// Tests that Field() can explain the result of matching a pointer. +TEST(FieldForPointerTest, CanExplainMatchResult) { + Matcher m = Field(&AStruct::x, Ge(0)); + + AStruct a; + a.x = 1; + EXPECT_EQ("", Explain(m, static_cast(NULL))); + EXPECT_EQ("which points to an object whose given field is 1" + OfType("int"), + Explain(m, &a)); + + m = Field(&AStruct::x, GreaterThan(0)); + EXPECT_EQ("which points to an object whose given field is 1" + OfType("int") + + ", which is 1 more than 0", Explain(m, &a)); +} + +// A user-defined class for testing Property(). +class AClass { + public: + AClass() : n_(0) {} + + // A getter that returns a non-reference. + int n() const { return n_; } + + void set_n(int new_n) { n_ = new_n; } + + // A getter that returns a reference to const. + const string& s() const { return s_; } + + void set_s(const string& new_s) { s_ = new_s; } + + // A getter that returns a reference to non-const. + double& x() const { return x_; } + private: + int n_; + string s_; + + static double x_; +}; + +double AClass::x_ = 0.0; + +// A derived class for testing Property(). +class DerivedClass : public AClass { + private: + int k_; +}; + +// Tests that Property(&Foo::property, ...) works when property() +// returns a non-reference. +TEST(PropertyTest, WorksForNonReferenceProperty) { + Matcher m = Property(&AClass::n, Ge(0)); + + AClass a; + a.set_n(1); + EXPECT_TRUE(m.Matches(a)); + + a.set_n(-1); + EXPECT_FALSE(m.Matches(a)); +} + +// Tests that Property(&Foo::property, ...) works when property() +// returns a reference to const. +TEST(PropertyTest, WorksForReferenceToConstProperty) { + Matcher m = Property(&AClass::s, StartsWith("hi")); + + AClass a; + a.set_s("hill"); + EXPECT_TRUE(m.Matches(a)); + + a.set_s("hole"); + EXPECT_FALSE(m.Matches(a)); +} + +// Tests that Property(&Foo::property, ...) works when property() +// returns a reference to non-const. +TEST(PropertyTest, WorksForReferenceToNonConstProperty) { + double x = 0.0; + AClass a; + + Matcher m = Property(&AClass::x, Ref(x)); + EXPECT_FALSE(m.Matches(a)); + + m = Property(&AClass::x, Not(Ref(x))); + EXPECT_TRUE(m.Matches(a)); +} + +// Tests that Property(&Foo::property, ...) works when the argument is +// passed by value. +TEST(PropertyTest, WorksForByValueArgument) { + Matcher m = Property(&AClass::s, StartsWith("hi")); + + AClass a; + a.set_s("hill"); + EXPECT_TRUE(m.Matches(a)); + + a.set_s("hole"); + EXPECT_FALSE(m.Matches(a)); +} + +// Tests that Property(&Foo::property, ...) works when the argument's +// type is a sub-type of Foo. +TEST(PropertyTest, WorksForArgumentOfSubType) { + // The matcher expects a DerivedClass, but inside the Property() we + // say AClass. + Matcher m = Property(&AClass::n, Ge(0)); + + DerivedClass d; + d.set_n(1); + EXPECT_TRUE(m.Matches(d)); + + d.set_n(-1); + EXPECT_FALSE(m.Matches(d)); +} + +// Tests that Property(&Foo::property, m) works when property()'s type +// and m's argument type are compatible but different. +TEST(PropertyTest, WorksForCompatibleMatcherType) { + // n() returns an int but the inner matcher expects a signed char. + Matcher m = Property(&AClass::n, + Matcher(Ge(0))); + + AClass a; + EXPECT_TRUE(m.Matches(a)); + a.set_n(-1); + EXPECT_FALSE(m.Matches(a)); +} + +// Tests that Property() can describe itself. +TEST(PropertyTest, CanDescribeSelf) { + Matcher m = Property(&AClass::n, Ge(0)); + + EXPECT_EQ("is an object whose given property is >= 0", Describe(m)); + EXPECT_EQ("is an object whose given property isn't >= 0", + DescribeNegation(m)); +} + +// Tests that Property() can explain the match result. +TEST(PropertyTest, CanExplainMatchResult) { + Matcher m = Property(&AClass::n, Ge(0)); + + AClass a; + a.set_n(1); + EXPECT_EQ("whose given property is 1" + OfType("int"), Explain(m, a)); + + m = Property(&AClass::n, GreaterThan(0)); + EXPECT_EQ( + "whose given property is 1" + OfType("int") + ", which is 1 more than 0", + Explain(m, a)); +} + +// Tests that Property() works when the argument is a pointer to const. +TEST(PropertyForPointerTest, WorksForPointerToConst) { + Matcher m = Property(&AClass::n, Ge(0)); + + AClass a; + a.set_n(1); + EXPECT_TRUE(m.Matches(&a)); + + a.set_n(-1); + EXPECT_FALSE(m.Matches(&a)); +} + +// Tests that Property() works when the argument is a pointer to non-const. +TEST(PropertyForPointerTest, WorksForPointerToNonConst) { + Matcher m = Property(&AClass::s, StartsWith("hi")); + + AClass a; + a.set_s("hill"); + EXPECT_TRUE(m.Matches(&a)); + + a.set_s("hole"); + EXPECT_FALSE(m.Matches(&a)); +} + +// Tests that Property() works when the argument is a reference to a +// const pointer. +TEST(PropertyForPointerTest, WorksForReferenceToConstPointer) { + Matcher m = Property(&AClass::s, StartsWith("hi")); + + AClass a; + a.set_s("hill"); + EXPECT_TRUE(m.Matches(&a)); + + a.set_s("hole"); + EXPECT_FALSE(m.Matches(&a)); +} + +// Tests that Property() does not match the NULL pointer. +TEST(PropertyForPointerTest, WorksForReferenceToNonConstProperty) { + Matcher m = Property(&AClass::x, _); + EXPECT_FALSE(m.Matches(NULL)); +} + +// Tests that Property(&Foo::property, ...) works when the argument's +// type is a sub-type of const Foo*. +TEST(PropertyForPointerTest, WorksForArgumentOfSubType) { + // The matcher expects a DerivedClass, but inside the Property() we + // say AClass. + Matcher m = Property(&AClass::n, Ge(0)); + + DerivedClass d; + d.set_n(1); + EXPECT_TRUE(m.Matches(&d)); + + d.set_n(-1); + EXPECT_FALSE(m.Matches(&d)); +} + +// Tests that Property() can describe itself when used to match a pointer. +TEST(PropertyForPointerTest, CanDescribeSelf) { + Matcher m = Property(&AClass::n, Ge(0)); + + EXPECT_EQ("is an object whose given property is >= 0", Describe(m)); + EXPECT_EQ("is an object whose given property isn't >= 0", + DescribeNegation(m)); +} + +// Tests that Property() can explain the result of matching a pointer. +TEST(PropertyForPointerTest, CanExplainMatchResult) { + Matcher m = Property(&AClass::n, Ge(0)); + + AClass a; + a.set_n(1); + EXPECT_EQ("", Explain(m, static_cast(NULL))); + EXPECT_EQ( + "which points to an object whose given property is 1" + OfType("int"), + Explain(m, &a)); + + m = Property(&AClass::n, GreaterThan(0)); + EXPECT_EQ("which points to an object whose given property is 1" + + OfType("int") + ", which is 1 more than 0", + Explain(m, &a)); +} + +// Tests ResultOf. + +// Tests that ResultOf(f, ...) compiles and works as expected when f is a +// function pointer. +string IntToStringFunction(int input) { return input == 1 ? "foo" : "bar"; } + +TEST(ResultOfTest, WorksForFunctionPointers) { + Matcher matcher = ResultOf(&IntToStringFunction, Eq(string("foo"))); + + EXPECT_TRUE(matcher.Matches(1)); + EXPECT_FALSE(matcher.Matches(2)); +} + +// Tests that ResultOf() can describe itself. +TEST(ResultOfTest, CanDescribeItself) { + Matcher matcher = ResultOf(&IntToStringFunction, StrEq("foo")); + + EXPECT_EQ("is mapped by the given callable to a value that " + "is equal to \"foo\"", Describe(matcher)); + EXPECT_EQ("is mapped by the given callable to a value that " + "isn't equal to \"foo\"", DescribeNegation(matcher)); +} + +// Tests that ResultOf() can explain the match result. +int IntFunction(int input) { return input == 42 ? 80 : 90; } + +TEST(ResultOfTest, CanExplainMatchResult) { + Matcher matcher = ResultOf(&IntFunction, Ge(85)); + EXPECT_EQ("which is mapped by the given callable to 90" + OfType("int"), + Explain(matcher, 36)); + + matcher = ResultOf(&IntFunction, GreaterThan(85)); + EXPECT_EQ("which is mapped by the given callable to 90" + OfType("int") + + ", which is 5 more than 85", Explain(matcher, 36)); +} + +// Tests that ResultOf(f, ...) compiles and works as expected when f(x) +// returns a non-reference. +TEST(ResultOfTest, WorksForNonReferenceResults) { + Matcher matcher = ResultOf(&IntFunction, Eq(80)); + + EXPECT_TRUE(matcher.Matches(42)); + EXPECT_FALSE(matcher.Matches(36)); +} + +// Tests that ResultOf(f, ...) compiles and works as expected when f(x) +// returns a reference to non-const. +double& DoubleFunction(double& input) { return input; } // NOLINT + +Uncopyable& RefUncopyableFunction(Uncopyable& obj) { // NOLINT + return obj; +} + +TEST(ResultOfTest, WorksForReferenceToNonConstResults) { + double x = 3.14; + double x2 = x; + Matcher matcher = ResultOf(&DoubleFunction, Ref(x)); + + EXPECT_TRUE(matcher.Matches(x)); + EXPECT_FALSE(matcher.Matches(x2)); + + // Test that ResultOf works with uncopyable objects + Uncopyable obj(0); + Uncopyable obj2(0); + Matcher matcher2 = + ResultOf(&RefUncopyableFunction, Ref(obj)); + + EXPECT_TRUE(matcher2.Matches(obj)); + EXPECT_FALSE(matcher2.Matches(obj2)); +} + +// Tests that ResultOf(f, ...) compiles and works as expected when f(x) +// returns a reference to const. +const string& StringFunction(const string& input) { return input; } + +TEST(ResultOfTest, WorksForReferenceToConstResults) { + string s = "foo"; + string s2 = s; + Matcher matcher = ResultOf(&StringFunction, Ref(s)); + + EXPECT_TRUE(matcher.Matches(s)); + EXPECT_FALSE(matcher.Matches(s2)); +} + +// Tests that ResultOf(f, m) works when f(x) and m's +// argument types are compatible but different. +TEST(ResultOfTest, WorksForCompatibleMatcherTypes) { + // IntFunction() returns int but the inner matcher expects a signed char. + Matcher matcher = ResultOf(IntFunction, Matcher(Ge(85))); + + EXPECT_TRUE(matcher.Matches(36)); + EXPECT_FALSE(matcher.Matches(42)); +} + +// Tests that the program aborts when ResultOf is passed +// a NULL function pointer. +TEST(ResultOfDeathTest, DiesOnNullFunctionPointers) { + EXPECT_DEATH_IF_SUPPORTED( + ResultOf(static_cast(NULL), Eq(string("foo"))), + "NULL function pointer is passed into ResultOf\\(\\)\\."); +} + +// Tests that ResultOf(f, ...) compiles and works as expected when f is a +// function reference. +TEST(ResultOfTest, WorksForFunctionReferences) { + Matcher matcher = ResultOf(IntToStringFunction, StrEq("foo")); + EXPECT_TRUE(matcher.Matches(1)); + EXPECT_FALSE(matcher.Matches(2)); +} + +// Tests that ResultOf(f, ...) compiles and works as expected when f is a +// function object. +struct Functor : public ::std::unary_function { + result_type operator()(argument_type input) const { + return IntToStringFunction(input); + } +}; + +TEST(ResultOfTest, WorksForFunctors) { + Matcher matcher = ResultOf(Functor(), Eq(string("foo"))); + + EXPECT_TRUE(matcher.Matches(1)); + EXPECT_FALSE(matcher.Matches(2)); +} + +// Tests that ResultOf(f, ...) compiles and works as expected when f is a +// functor with more then one operator() defined. ResultOf() must work +// for each defined operator(). +struct PolymorphicFunctor { + typedef int result_type; + int operator()(int n) { return n; } + int operator()(const char* s) { return static_cast(strlen(s)); } +}; + +TEST(ResultOfTest, WorksForPolymorphicFunctors) { + Matcher matcher_int = ResultOf(PolymorphicFunctor(), Ge(5)); + + EXPECT_TRUE(matcher_int.Matches(10)); + EXPECT_FALSE(matcher_int.Matches(2)); + + Matcher matcher_string = ResultOf(PolymorphicFunctor(), Ge(5)); + + EXPECT_TRUE(matcher_string.Matches("long string")); + EXPECT_FALSE(matcher_string.Matches("shrt")); +} + +const int* ReferencingFunction(const int& n) { return &n; } + +struct ReferencingFunctor { + typedef const int* result_type; + result_type operator()(const int& n) { return &n; } +}; + +TEST(ResultOfTest, WorksForReferencingCallables) { + const int n = 1; + const int n2 = 1; + Matcher matcher2 = ResultOf(ReferencingFunction, Eq(&n)); + EXPECT_TRUE(matcher2.Matches(n)); + EXPECT_FALSE(matcher2.Matches(n2)); + + Matcher matcher3 = ResultOf(ReferencingFunctor(), Eq(&n)); + EXPECT_TRUE(matcher3.Matches(n)); + EXPECT_FALSE(matcher3.Matches(n2)); +} + +class DivisibleByImpl { + public: + explicit DivisibleByImpl(int a_divider) : divider_(a_divider) {} + + // For testing using ExplainMatchResultTo() with polymorphic matchers. + template + bool MatchAndExplain(const T& n, MatchResultListener* listener) const { + *listener << "which is " << (n % divider_) << " modulo " + << divider_; + return (n % divider_) == 0; + } + + void DescribeTo(ostream* os) const { + *os << "is divisible by " << divider_; + } + + void DescribeNegationTo(ostream* os) const { + *os << "is not divisible by " << divider_; + } + + void set_divider(int a_divider) { divider_ = a_divider; } + int divider() const { return divider_; } + + private: + int divider_; +}; + +PolymorphicMatcher DivisibleBy(int n) { + return MakePolymorphicMatcher(DivisibleByImpl(n)); +} + +// Tests that when AllOf() fails, only the first failing matcher is +// asked to explain why. +TEST(ExplainMatchResultTest, AllOf_False_False) { + const Matcher m = AllOf(DivisibleBy(4), DivisibleBy(3)); + EXPECT_EQ("which is 1 modulo 4", Explain(m, 5)); +} + +// Tests that when AllOf() fails, only the first failing matcher is +// asked to explain why. +TEST(ExplainMatchResultTest, AllOf_False_True) { + const Matcher m = AllOf(DivisibleBy(4), DivisibleBy(3)); + EXPECT_EQ("which is 2 modulo 4", Explain(m, 6)); +} + +// Tests that when AllOf() fails, only the first failing matcher is +// asked to explain why. +TEST(ExplainMatchResultTest, AllOf_True_False) { + const Matcher m = AllOf(Ge(1), DivisibleBy(3)); + EXPECT_EQ("which is 2 modulo 3", Explain(m, 5)); +} + +// Tests that when AllOf() succeeds, all matchers are asked to explain +// why. +TEST(ExplainMatchResultTest, AllOf_True_True) { + const Matcher m = AllOf(DivisibleBy(2), DivisibleBy(3)); + EXPECT_EQ("which is 0 modulo 2, and which is 0 modulo 3", Explain(m, 6)); +} + +TEST(ExplainMatchResultTest, AllOf_True_True_2) { + const Matcher m = AllOf(Ge(2), Le(3)); + EXPECT_EQ("", Explain(m, 2)); +} + +TEST(ExplainmatcherResultTest, MonomorphicMatcher) { + const Matcher m = GreaterThan(5); + EXPECT_EQ("which is 1 more than 5", Explain(m, 6)); +} + +// The following two tests verify that values without a public copy +// ctor can be used as arguments to matchers like Eq(), Ge(), and etc +// with the help of ByRef(). + +class NotCopyable { + public: + explicit NotCopyable(int a_value) : value_(a_value) {} + + int value() const { return value_; } + + bool operator==(const NotCopyable& rhs) const { + return value() == rhs.value(); + } + + bool operator>=(const NotCopyable& rhs) const { + return value() >= rhs.value(); + } + private: + int value_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(NotCopyable); +}; + +TEST(ByRefTest, AllowsNotCopyableConstValueInMatchers) { + const NotCopyable const_value1(1); + const Matcher m = Eq(ByRef(const_value1)); + + const NotCopyable n1(1), n2(2); + EXPECT_TRUE(m.Matches(n1)); + EXPECT_FALSE(m.Matches(n2)); +} + +TEST(ByRefTest, AllowsNotCopyableValueInMatchers) { + NotCopyable value2(2); + const Matcher m = Ge(ByRef(value2)); + + NotCopyable n1(1), n2(2); + EXPECT_FALSE(m.Matches(n1)); + EXPECT_TRUE(m.Matches(n2)); +} + +#if GTEST_HAS_TYPED_TEST +// Tests ContainerEq with different container types, and +// different element types. + +template +class ContainerEqTest : public testing::Test {}; + +typedef testing::Types< + set, + vector, + multiset, + list > + ContainerEqTestTypes; + +TYPED_TEST_CASE(ContainerEqTest, ContainerEqTestTypes); + +// Tests that the filled container is equal to itself. +TYPED_TEST(ContainerEqTest, EqualsSelf) { + static const int vals[] = {1, 1, 2, 3, 5, 8}; + TypeParam my_set(vals, vals + 6); + const Matcher m = ContainerEq(my_set); + EXPECT_TRUE(m.Matches(my_set)); + EXPECT_EQ("", Explain(m, my_set)); +} + +// Tests that missing values are reported. +TYPED_TEST(ContainerEqTest, ValueMissing) { + static const int vals[] = {1, 1, 2, 3, 5, 8}; + static const int test_vals[] = {2, 1, 8, 5}; + TypeParam my_set(vals, vals + 6); + TypeParam test_set(test_vals, test_vals + 4); + const Matcher m = ContainerEq(my_set); + EXPECT_FALSE(m.Matches(test_set)); + EXPECT_EQ("which doesn't have these expected elements: 3", + Explain(m, test_set)); +} + +// Tests that added values are reported. +TYPED_TEST(ContainerEqTest, ValueAdded) { + static const int vals[] = {1, 1, 2, 3, 5, 8}; + static const int test_vals[] = {1, 2, 3, 5, 8, 46}; + TypeParam my_set(vals, vals + 6); + TypeParam test_set(test_vals, test_vals + 6); + const Matcher m = ContainerEq(my_set); + EXPECT_FALSE(m.Matches(test_set)); + EXPECT_EQ("which has these unexpected elements: 46", Explain(m, test_set)); +} + +// Tests that added and missing values are reported together. +TYPED_TEST(ContainerEqTest, ValueAddedAndRemoved) { + static const int vals[] = {1, 1, 2, 3, 5, 8}; + static const int test_vals[] = {1, 2, 3, 8, 46}; + TypeParam my_set(vals, vals + 6); + TypeParam test_set(test_vals, test_vals + 5); + const Matcher m = ContainerEq(my_set); + EXPECT_FALSE(m.Matches(test_set)); + EXPECT_EQ("which has these unexpected elements: 46,\n" + "and doesn't have these expected elements: 5", + Explain(m, test_set)); +} + +// Tests duplicated value -- expect no explanation. +TYPED_TEST(ContainerEqTest, DuplicateDifference) { + static const int vals[] = {1, 1, 2, 3, 5, 8}; + static const int test_vals[] = {1, 2, 3, 5, 8}; + TypeParam my_set(vals, vals + 6); + TypeParam test_set(test_vals, test_vals + 5); + const Matcher m = ContainerEq(my_set); + // Depending on the container, match may be true or false + // But in any case there should be no explanation. + EXPECT_EQ("", Explain(m, test_set)); +} +#endif // GTEST_HAS_TYPED_TEST + +// Tests that mutliple missing values are reported. +// Using just vector here, so order is predicatble. +TEST(ContainerEqExtraTest, MultipleValuesMissing) { + static const int vals[] = {1, 1, 2, 3, 5, 8}; + static const int test_vals[] = {2, 1, 5}; + vector my_set(vals, vals + 6); + vector test_set(test_vals, test_vals + 3); + const Matcher > m = ContainerEq(my_set); + EXPECT_FALSE(m.Matches(test_set)); + EXPECT_EQ("which doesn't have these expected elements: 3, 8", + Explain(m, test_set)); +} + +// Tests that added values are reported. +// Using just vector here, so order is predicatble. +TEST(ContainerEqExtraTest, MultipleValuesAdded) { + static const int vals[] = {1, 1, 2, 3, 5, 8}; + static const int test_vals[] = {1, 2, 92, 3, 5, 8, 46}; + list my_set(vals, vals + 6); + list test_set(test_vals, test_vals + 7); + const Matcher&> m = ContainerEq(my_set); + EXPECT_FALSE(m.Matches(test_set)); + EXPECT_EQ("which has these unexpected elements: 92, 46", + Explain(m, test_set)); +} + +// Tests that added and missing values are reported together. +TEST(ContainerEqExtraTest, MultipleValuesAddedAndRemoved) { + static const int vals[] = {1, 1, 2, 3, 5, 8}; + static const int test_vals[] = {1, 2, 3, 92, 46}; + list my_set(vals, vals + 6); + list test_set(test_vals, test_vals + 5); + const Matcher > m = ContainerEq(my_set); + EXPECT_FALSE(m.Matches(test_set)); + EXPECT_EQ("which has these unexpected elements: 92, 46,\n" + "and doesn't have these expected elements: 5, 8", + Explain(m, test_set)); +} + +// Tests to see that duplicate elements are detected, +// but (as above) not reported in the explanation. +TEST(ContainerEqExtraTest, MultiSetOfIntDuplicateDifference) { + static const int vals[] = {1, 1, 2, 3, 5, 8}; + static const int test_vals[] = {1, 2, 3, 5, 8}; + vector my_set(vals, vals + 6); + vector test_set(test_vals, test_vals + 5); + const Matcher > m = ContainerEq(my_set); + EXPECT_TRUE(m.Matches(my_set)); + EXPECT_FALSE(m.Matches(test_set)); + // There is nothing to report when both sets contain all the same values. + EXPECT_EQ("", Explain(m, test_set)); +} + +// Tests that ContainerEq works for non-trivial associative containers, +// like maps. +TEST(ContainerEqExtraTest, WorksForMaps) { + map my_map; + my_map[0] = "a"; + my_map[1] = "b"; + + map test_map; + test_map[0] = "aa"; + test_map[1] = "b"; + + const Matcher&> m = ContainerEq(my_map); + EXPECT_TRUE(m.Matches(my_map)); + EXPECT_FALSE(m.Matches(test_map)); + + EXPECT_EQ("which has these unexpected elements: (0, \"aa\"),\n" + "and doesn't have these expected elements: (0, \"a\")", + Explain(m, test_map)); +} + +TEST(ContainerEqExtraTest, WorksForNativeArray) { + int a1[] = { 1, 2, 3 }; + int a2[] = { 1, 2, 3 }; + int b[] = { 1, 2, 4 }; + + EXPECT_THAT(a1, ContainerEq(a2)); + EXPECT_THAT(a1, Not(ContainerEq(b))); +} + +TEST(ContainerEqExtraTest, WorksForTwoDimensionalNativeArray) { + const char a1[][3] = { "hi", "lo" }; + const char a2[][3] = { "hi", "lo" }; + const char b[][3] = { "lo", "hi" }; + + // Tests using ContainerEq() in the first dimension. + EXPECT_THAT(a1, ContainerEq(a2)); + EXPECT_THAT(a1, Not(ContainerEq(b))); + + // Tests using ContainerEq() in the second dimension. + EXPECT_THAT(a1, ElementsAre(ContainerEq(a2[0]), ContainerEq(a2[1]))); + EXPECT_THAT(a1, ElementsAre(Not(ContainerEq(b[0])), ContainerEq(a2[1]))); +} + +TEST(ContainerEqExtraTest, WorksForNativeArrayAsTuple) { + const int a1[] = { 1, 2, 3 }; + const int a2[] = { 1, 2, 3 }; + const int b[] = { 1, 2, 3, 4 }; + + const int* const p1 = a1; + EXPECT_THAT(make_tuple(p1, 3), ContainerEq(a2)); + EXPECT_THAT(make_tuple(p1, 3), Not(ContainerEq(b))); + + const int c[] = { 1, 3, 2 }; + EXPECT_THAT(make_tuple(p1, 3), Not(ContainerEq(c))); +} + +TEST(ContainerEqExtraTest, CopiesNativeArrayParameter) { + std::string a1[][3] = { + { "hi", "hello", "ciao" }, + { "bye", "see you", "ciao" } + }; + + std::string a2[][3] = { + { "hi", "hello", "ciao" }, + { "bye", "see you", "ciao" } + }; + + const Matcher m = ContainerEq(a2); + EXPECT_THAT(a1, m); + + a2[0][0] = "ha"; + EXPECT_THAT(a1, m); +} + +// Tests IsReadableTypeName(). + +TEST(IsReadableTypeNameTest, ReturnsTrueForShortNames) { + EXPECT_TRUE(IsReadableTypeName("int")); + EXPECT_TRUE(IsReadableTypeName("const unsigned char*")); + EXPECT_TRUE(IsReadableTypeName("MyMap")); + EXPECT_TRUE(IsReadableTypeName("void (*)(int, bool)")); +} + +TEST(IsReadableTypeNameTest, ReturnsTrueForLongNonTemplateNonFunctionNames) { + EXPECT_TRUE(IsReadableTypeName("my_long_namespace::MyClassName")); + EXPECT_TRUE(IsReadableTypeName("int [5][6][7][8][9][10][11]")); + EXPECT_TRUE(IsReadableTypeName("my_namespace::MyOuterClass::MyInnerClass")); +} + +TEST(IsReadableTypeNameTest, ReturnsFalseForLongTemplateNames) { + EXPECT_FALSE( + IsReadableTypeName("basic_string >")); + EXPECT_FALSE(IsReadableTypeName("std::vector >")); +} + +TEST(IsReadableTypeNameTest, ReturnsFalseForLongFunctionTypeNames) { + EXPECT_FALSE(IsReadableTypeName("void (&)(int, bool, char, float)")); +} + +// Tests JoinAsTuple(). + +TEST(JoinAsTupleTest, JoinsEmptyTuple) { + EXPECT_EQ("", JoinAsTuple(Strings())); +} + +TEST(JoinAsTupleTest, JoinsOneTuple) { + const char* fields[] = { "1" }; + EXPECT_EQ("1", JoinAsTuple(Strings(fields, fields + 1))); +} + +TEST(JoinAsTupleTest, JoinsTwoTuple) { + const char* fields[] = { "1", "a" }; + EXPECT_EQ("(1, a)", JoinAsTuple(Strings(fields, fields + 2))); +} + +TEST(JoinAsTupleTest, JoinsTenTuple) { + const char* fields[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10" }; + EXPECT_EQ("(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)", + JoinAsTuple(Strings(fields, fields + 10))); +} + +// Tests FormatMatcherDescription(). + +TEST(FormatMatcherDescriptionTest, WorksForEmptyDescription) { + EXPECT_EQ("is even", + FormatMatcherDescription(false, "IsEven", Strings())); + EXPECT_EQ("not (is even)", + FormatMatcherDescription(true, "IsEven", Strings())); + + const char* params[] = { "5" }; + EXPECT_EQ("equals 5", + FormatMatcherDescription(false, "Equals", + Strings(params, params + 1))); + + const char* params2[] = { "5", "8" }; + EXPECT_EQ("is in range (5, 8)", + FormatMatcherDescription(false, "IsInRange", + Strings(params2, params2 + 2))); +} + +// Tests PolymorphicMatcher::mutable_impl(). +TEST(PolymorphicMatcherTest, CanAccessMutableImpl) { + PolymorphicMatcher m(DivisibleByImpl(42)); + DivisibleByImpl& impl = m.mutable_impl(); + EXPECT_EQ(42, impl.divider()); + + impl.set_divider(0); + EXPECT_EQ(0, m.mutable_impl().divider()); +} + +// Tests PolymorphicMatcher::impl(). +TEST(PolymorphicMatcherTest, CanAccessImpl) { + const PolymorphicMatcher m(DivisibleByImpl(42)); + const DivisibleByImpl& impl = m.impl(); + EXPECT_EQ(42, impl.divider()); +} + +TEST(MatcherTupleTest, ExplainsMatchFailure) { + stringstream ss1; + ExplainMatchFailureTupleTo(make_tuple(Matcher(Eq('a')), GreaterThan(5)), + make_tuple('a', 10), &ss1); + EXPECT_EQ("", ss1.str()); // Successful match. + + stringstream ss2; + ExplainMatchFailureTupleTo(make_tuple(GreaterThan(5), Matcher(Eq('a'))), + make_tuple(2, 'b'), &ss2); + EXPECT_EQ(" Expected arg #0: is > 5\n" + " Actual: 2, which is 3 less than 5\n" + " Expected arg #1: is equal to 'a' (97, 0x61)\n" + " Actual: 'b' (98, 0x62)\n", + ss2.str()); // Failed match where both arguments need explanation. + + stringstream ss3; + ExplainMatchFailureTupleTo(make_tuple(GreaterThan(5), Matcher(Eq('a'))), + make_tuple(2, 'a'), &ss3); + EXPECT_EQ(" Expected arg #0: is > 5\n" + " Actual: 2, which is 3 less than 5\n", + ss3.str()); // Failed match where only one argument needs + // explanation. +} + +// Tests Each(). + +TEST(EachTest, ExplainsMatchResultCorrectly) { + set a; // empty + + Matcher > m = Each(2); + EXPECT_EQ("", Explain(m, a)); + + Matcher n = Each(1); // NOLINT + + const int b[1] = { 1 }; + EXPECT_EQ("", Explain(n, b)); + + n = Each(3); + EXPECT_EQ("whose element #0 doesn't match", Explain(n, b)); + + a.insert(1); + a.insert(2); + a.insert(3); + m = Each(GreaterThan(0)); + EXPECT_EQ("", Explain(m, a)); + + m = Each(GreaterThan(10)); + EXPECT_EQ("whose element #0 doesn't match, which is 9 less than 10", + Explain(m, a)); +} + +TEST(EachTest, DescribesItselfCorrectly) { + Matcher > m = Each(1); + EXPECT_EQ("only contains elements that is equal to 1", Describe(m)); + + Matcher > m2 = Not(m); + EXPECT_EQ("contains some element that isn't equal to 1", Describe(m2)); +} + +TEST(EachTest, MatchesVectorWhenAllElementsMatch) { + vector some_vector; + EXPECT_THAT(some_vector, Each(1)); + some_vector.push_back(3); + EXPECT_THAT(some_vector, Not(Each(1))); + EXPECT_THAT(some_vector, Each(3)); + some_vector.push_back(1); + some_vector.push_back(2); + EXPECT_THAT(some_vector, Not(Each(3))); + EXPECT_THAT(some_vector, Each(Lt(3.5))); + + vector another_vector; + another_vector.push_back("fee"); + EXPECT_THAT(another_vector, Each(string("fee"))); + another_vector.push_back("fie"); + another_vector.push_back("foe"); + another_vector.push_back("fum"); + EXPECT_THAT(another_vector, Not(Each(string("fee")))); +} + +TEST(EachTest, MatchesMapWhenAllElementsMatch) { + map my_map; + const char* bar = "a string"; + my_map[bar] = 2; + EXPECT_THAT(my_map, Each(make_pair(bar, 2))); + + map another_map; + EXPECT_THAT(another_map, Each(make_pair(string("fee"), 1))); + another_map["fee"] = 1; + EXPECT_THAT(another_map, Each(make_pair(string("fee"), 1))); + another_map["fie"] = 2; + another_map["foe"] = 3; + another_map["fum"] = 4; + EXPECT_THAT(another_map, Not(Each(make_pair(string("fee"), 1)))); + EXPECT_THAT(another_map, Not(Each(make_pair(string("fum"), 1)))); + EXPECT_THAT(another_map, Each(Pair(_, Gt(0)))); +} + +TEST(EachTest, AcceptsMatcher) { + const int a[] = { 1, 2, 3 }; + EXPECT_THAT(a, Each(Gt(0))); + EXPECT_THAT(a, Not(Each(Gt(1)))); +} + +TEST(EachTest, WorksForNativeArrayAsTuple) { + const int a[] = { 1, 2 }; + const int* const pointer = a; + EXPECT_THAT(make_tuple(pointer, 2), Each(Gt(0))); + EXPECT_THAT(make_tuple(pointer, 2), Not(Each(Gt(1)))); +} + +// For testing Pointwise(). +class IsHalfOfMatcher { + public: + template + bool MatchAndExplain(const tuple& a_pair, + MatchResultListener* listener) const { + if (get<0>(a_pair) == get<1>(a_pair)/2) { + *listener << "where the second is " << get<1>(a_pair); + return true; + } else { + *listener << "where the second/2 is " << get<1>(a_pair)/2; + return false; + } + } + + void DescribeTo(ostream* os) const { + *os << "are a pair where the first is half of the second"; + } + + void DescribeNegationTo(ostream* os) const { + *os << "are a pair where the first isn't half of the second"; + } +}; + +PolymorphicMatcher IsHalfOf() { + return MakePolymorphicMatcher(IsHalfOfMatcher()); +} + +TEST(PointwiseTest, DescribesSelf) { + vector rhs; + rhs.push_back(1); + rhs.push_back(2); + rhs.push_back(3); + const Matcher&> m = Pointwise(IsHalfOf(), rhs); + EXPECT_EQ("contains 3 values, where each value and its corresponding value " + "in { 1, 2, 3 } are a pair where the first is half of the second", + Describe(m)); + EXPECT_EQ("doesn't contain exactly 3 values, or contains a value x at some " + "index i where x and the i-th value of { 1, 2, 3 } are a pair " + "where the first isn't half of the second", + DescribeNegation(m)); +} + +TEST(PointwiseTest, MakesCopyOfRhs) { + list rhs; + rhs.push_back(2); + rhs.push_back(4); + + int lhs[] = { 1, 2 }; + const Matcher m = Pointwise(IsHalfOf(), rhs); + EXPECT_THAT(lhs, m); + + // Changing rhs now shouldn't affect m, which made a copy of rhs. + rhs.push_back(6); + EXPECT_THAT(lhs, m); +} + +TEST(PointwiseTest, WorksForLhsNativeArray) { + const int lhs[] = { 1, 2, 3 }; + vector rhs; + rhs.push_back(2); + rhs.push_back(4); + rhs.push_back(6); + EXPECT_THAT(lhs, Pointwise(Lt(), rhs)); + EXPECT_THAT(lhs, Not(Pointwise(Gt(), rhs))); +} + +TEST(PointwiseTest, WorksForRhsNativeArray) { + const int rhs[] = { 1, 2, 3 }; + vector lhs; + lhs.push_back(2); + lhs.push_back(4); + lhs.push_back(6); + EXPECT_THAT(lhs, Pointwise(Gt(), rhs)); + EXPECT_THAT(lhs, Not(Pointwise(Lt(), rhs))); +} + +TEST(PointwiseTest, RejectsWrongSize) { + const double lhs[2] = { 1, 2 }; + const int rhs[1] = { 0 }; + EXPECT_THAT(lhs, Not(Pointwise(Gt(), rhs))); + EXPECT_EQ("which contains 2 values", + Explain(Pointwise(Gt(), rhs), lhs)); + + const int rhs2[3] = { 0, 1, 2 }; + EXPECT_THAT(lhs, Not(Pointwise(Gt(), rhs2))); +} + +TEST(PointwiseTest, RejectsWrongContent) { + const double lhs[3] = { 1, 2, 3 }; + const int rhs[3] = { 2, 6, 4 }; + EXPECT_THAT(lhs, Not(Pointwise(IsHalfOf(), rhs))); + EXPECT_EQ("where the value pair (2, 6) at index #1 don't match, " + "where the second/2 is 3", + Explain(Pointwise(IsHalfOf(), rhs), lhs)); +} + +TEST(PointwiseTest, AcceptsCorrectContent) { + const double lhs[3] = { 1, 2, 3 }; + const int rhs[3] = { 2, 4, 6 }; + EXPECT_THAT(lhs, Pointwise(IsHalfOf(), rhs)); + EXPECT_EQ("", Explain(Pointwise(IsHalfOf(), rhs), lhs)); +} + +TEST(PointwiseTest, AllowsMonomorphicInnerMatcher) { + const double lhs[3] = { 1, 2, 3 }; + const int rhs[3] = { 2, 4, 6 }; + const Matcher > m1 = IsHalfOf(); + EXPECT_THAT(lhs, Pointwise(m1, rhs)); + EXPECT_EQ("", Explain(Pointwise(m1, rhs), lhs)); + + // This type works as a tuple can be + // implicitly cast to tuple. + const Matcher > m2 = IsHalfOf(); + EXPECT_THAT(lhs, Pointwise(m2, rhs)); + EXPECT_EQ("", Explain(Pointwise(m2, rhs), lhs)); +} + +} // namespace gmock_matchers_test +} // namespace testing diff --git a/libwvdrmengine/test/gmock/test/gmock-more-actions_test.cc b/libwvdrmengine/test/gmock/test/gmock-more-actions_test.cc new file mode 100644 index 00000000..43ff55d8 --- /dev/null +++ b/libwvdrmengine/test/gmock/test/gmock-more-actions_test.cc @@ -0,0 +1,704 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file tests the built-in actions in gmock-more-actions.h. + +#include "gmock/gmock-more-actions.h" + +#include +#include +#include +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "gtest/internal/gtest-linked_ptr.h" + +namespace testing { +namespace gmock_more_actions_test { + +using ::std::plus; +using ::std::string; +using ::std::tr1::get; +using ::std::tr1::make_tuple; +using ::std::tr1::tuple; +using ::std::tr1::tuple_element; +using testing::_; +using testing::Action; +using testing::ActionInterface; +using testing::DeleteArg; +using testing::Invoke; +using testing::Return; +using testing::ReturnArg; +using testing::ReturnPointee; +using testing::SaveArg; +using testing::SaveArgPointee; +using testing::SetArgReferee; +using testing::StaticAssertTypeEq; +using testing::Unused; +using testing::WithArg; +using testing::WithoutArgs; +using testing::internal::linked_ptr; + +// For suppressing compiler warnings on conversion possibly losing precision. +inline short Short(short n) { return n; } // NOLINT +inline char Char(char ch) { return ch; } + +// Sample functions and functors for testing Invoke() and etc. +int Nullary() { return 1; } + +class NullaryFunctor { + public: + int operator()() { return 2; } +}; + +bool g_done = false; +void VoidNullary() { g_done = true; } + +class VoidNullaryFunctor { + public: + void operator()() { g_done = true; } +}; + +bool Unary(int x) { return x < 0; } + +const char* Plus1(const char* s) { return s + 1; } + +void VoidUnary(int /* n */) { g_done = true; } + +bool ByConstRef(const string& s) { return s == "Hi"; } + +const double g_double = 0; +bool ReferencesGlobalDouble(const double& x) { return &x == &g_double; } + +string ByNonConstRef(string& s) { return s += "+"; } // NOLINT + +struct UnaryFunctor { + int operator()(bool x) { return x ? 1 : -1; } +}; + +const char* Binary(const char* input, short n) { return input + n; } // NOLINT + +void VoidBinary(int, char) { g_done = true; } + +int Ternary(int x, char y, short z) { return x + y + z; } // NOLINT + +void VoidTernary(int, char, bool) { g_done = true; } + +int SumOf4(int a, int b, int c, int d) { return a + b + c + d; } + +int SumOfFirst2(int a, int b, Unused, Unused) { return a + b; } + +void VoidFunctionWithFourArguments(char, int, float, double) { g_done = true; } + +string Concat4(const char* s1, const char* s2, const char* s3, + const char* s4) { + return string(s1) + s2 + s3 + s4; +} + +int SumOf5(int a, int b, int c, int d, int e) { return a + b + c + d + e; } + +struct SumOf5Functor { + int operator()(int a, int b, int c, int d, int e) { + return a + b + c + d + e; + } +}; + +string Concat5(const char* s1, const char* s2, const char* s3, + const char* s4, const char* s5) { + return string(s1) + s2 + s3 + s4 + s5; +} + +int SumOf6(int a, int b, int c, int d, int e, int f) { + return a + b + c + d + e + f; +} + +struct SumOf6Functor { + int operator()(int a, int b, int c, int d, int e, int f) { + return a + b + c + d + e + f; + } +}; + +string Concat6(const char* s1, const char* s2, const char* s3, + const char* s4, const char* s5, const char* s6) { + return string(s1) + s2 + s3 + s4 + s5 + s6; +} + +string Concat7(const char* s1, const char* s2, const char* s3, + const char* s4, const char* s5, const char* s6, + const char* s7) { + return string(s1) + s2 + s3 + s4 + s5 + s6 + s7; +} + +string Concat8(const char* s1, const char* s2, const char* s3, + const char* s4, const char* s5, const char* s6, + const char* s7, const char* s8) { + return string(s1) + s2 + s3 + s4 + s5 + s6 + s7 + s8; +} + +string Concat9(const char* s1, const char* s2, const char* s3, + const char* s4, const char* s5, const char* s6, + const char* s7, const char* s8, const char* s9) { + return string(s1) + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9; +} + +string Concat10(const char* s1, const char* s2, const char* s3, + const char* s4, const char* s5, const char* s6, + const char* s7, const char* s8, const char* s9, + const char* s10) { + return string(s1) + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9 + s10; +} + +class Foo { + public: + Foo() : value_(123) {} + + int Nullary() const { return value_; } + + short Unary(long x) { return static_cast(value_ + x); } // NOLINT + + string Binary(const string& str, char c) const { return str + c; } + + int Ternary(int x, bool y, char z) { return value_ + x + y*z; } + + int SumOf4(int a, int b, int c, int d) const { + return a + b + c + d + value_; + } + + int SumOfLast2(Unused, Unused, int a, int b) const { return a + b; } + + int SumOf5(int a, int b, int c, int d, int e) { return a + b + c + d + e; } + + int SumOf6(int a, int b, int c, int d, int e, int f) { + return a + b + c + d + e + f; + } + + string Concat7(const char* s1, const char* s2, const char* s3, + const char* s4, const char* s5, const char* s6, + const char* s7) { + return string(s1) + s2 + s3 + s4 + s5 + s6 + s7; + } + + string Concat8(const char* s1, const char* s2, const char* s3, + const char* s4, const char* s5, const char* s6, + const char* s7, const char* s8) { + return string(s1) + s2 + s3 + s4 + s5 + s6 + s7 + s8; + } + + string Concat9(const char* s1, const char* s2, const char* s3, + const char* s4, const char* s5, const char* s6, + const char* s7, const char* s8, const char* s9) { + return string(s1) + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9; + } + + string Concat10(const char* s1, const char* s2, const char* s3, + const char* s4, const char* s5, const char* s6, + const char* s7, const char* s8, const char* s9, + const char* s10) { + return string(s1) + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9 + s10; + } + private: + int value_; +}; + +// Tests using Invoke() with a nullary function. +TEST(InvokeTest, Nullary) { + Action a = Invoke(Nullary); // NOLINT + EXPECT_EQ(1, a.Perform(make_tuple())); +} + +// Tests using Invoke() with a unary function. +TEST(InvokeTest, Unary) { + Action a = Invoke(Unary); // NOLINT + EXPECT_FALSE(a.Perform(make_tuple(1))); + EXPECT_TRUE(a.Perform(make_tuple(-1))); +} + +// Tests using Invoke() with a binary function. +TEST(InvokeTest, Binary) { + Action a = Invoke(Binary); // NOLINT + const char* p = "Hello"; + EXPECT_EQ(p + 2, a.Perform(make_tuple(p, Short(2)))); +} + +// Tests using Invoke() with a ternary function. +TEST(InvokeTest, Ternary) { + Action a = Invoke(Ternary); // NOLINT + EXPECT_EQ(6, a.Perform(make_tuple(1, '\2', Short(3)))); +} + +// Tests using Invoke() with a 4-argument function. +TEST(InvokeTest, FunctionThatTakes4Arguments) { + Action a = Invoke(SumOf4); // NOLINT + EXPECT_EQ(1234, a.Perform(make_tuple(1000, 200, 30, 4))); +} + +// Tests using Invoke() with a 5-argument function. +TEST(InvokeTest, FunctionThatTakes5Arguments) { + Action a = Invoke(SumOf5); // NOLINT + EXPECT_EQ(12345, a.Perform(make_tuple(10000, 2000, 300, 40, 5))); +} + +// Tests using Invoke() with a 6-argument function. +TEST(InvokeTest, FunctionThatTakes6Arguments) { + Action a = Invoke(SumOf6); // NOLINT + EXPECT_EQ(123456, a.Perform(make_tuple(100000, 20000, 3000, 400, 50, 6))); +} + +// A helper that turns the type of a C-string literal from const +// char[N] to const char*. +inline const char* CharPtr(const char* s) { return s; } + +// Tests using Invoke() with a 7-argument function. +TEST(InvokeTest, FunctionThatTakes7Arguments) { + Action a = + Invoke(Concat7); + EXPECT_EQ("1234567", + a.Perform(make_tuple(CharPtr("1"), CharPtr("2"), CharPtr("3"), + CharPtr("4"), CharPtr("5"), CharPtr("6"), + CharPtr("7")))); +} + +// Tests using Invoke() with a 8-argument function. +TEST(InvokeTest, FunctionThatTakes8Arguments) { + Action a = + Invoke(Concat8); + EXPECT_EQ("12345678", + a.Perform(make_tuple(CharPtr("1"), CharPtr("2"), CharPtr("3"), + CharPtr("4"), CharPtr("5"), CharPtr("6"), + CharPtr("7"), CharPtr("8")))); +} + +// Tests using Invoke() with a 9-argument function. +TEST(InvokeTest, FunctionThatTakes9Arguments) { + Action a = Invoke(Concat9); + EXPECT_EQ("123456789", + a.Perform(make_tuple(CharPtr("1"), CharPtr("2"), CharPtr("3"), + CharPtr("4"), CharPtr("5"), CharPtr("6"), + CharPtr("7"), CharPtr("8"), CharPtr("9")))); +} + +// Tests using Invoke() with a 10-argument function. +TEST(InvokeTest, FunctionThatTakes10Arguments) { + Action a = Invoke(Concat10); + EXPECT_EQ("1234567890", + a.Perform(make_tuple(CharPtr("1"), CharPtr("2"), CharPtr("3"), + CharPtr("4"), CharPtr("5"), CharPtr("6"), + CharPtr("7"), CharPtr("8"), CharPtr("9"), + CharPtr("0")))); +} + +// Tests using Invoke() with functions with parameters declared as Unused. +TEST(InvokeTest, FunctionWithUnusedParameters) { + Action a1 = + Invoke(SumOfFirst2); + EXPECT_EQ(12, a1.Perform(make_tuple(10, 2, 5.6, CharPtr("hi")))); + + Action a2 = + Invoke(SumOfFirst2); + EXPECT_EQ(23, a2.Perform(make_tuple(20, 3, true, static_cast(NULL)))); +} + +// Tests using Invoke() with methods with parameters declared as Unused. +TEST(InvokeTest, MethodWithUnusedParameters) { + Foo foo; + Action a1 = + Invoke(&foo, &Foo::SumOfLast2); + EXPECT_EQ(12, a1.Perform(make_tuple(CharPtr("hi"), true, 10, 2))); + + Action a2 = + Invoke(&foo, &Foo::SumOfLast2); + EXPECT_EQ(23, a2.Perform(make_tuple('a', 2.5, 20, 3))); +} + +// Tests using Invoke() with a functor. +TEST(InvokeTest, Functor) { + Action a = Invoke(plus()); // NOLINT + EXPECT_EQ(3L, a.Perform(make_tuple(1, 2))); +} + +// Tests using Invoke(f) as an action of a compatible type. +TEST(InvokeTest, FunctionWithCompatibleType) { + Action a = Invoke(SumOf4); // NOLINT + EXPECT_EQ(4321, a.Perform(make_tuple(4000, Short(300), Char(20), true))); +} + +// Tests using Invoke() with an object pointer and a method pointer. + +// Tests using Invoke() with a nullary method. +TEST(InvokeMethodTest, Nullary) { + Foo foo; + Action a = Invoke(&foo, &Foo::Nullary); // NOLINT + EXPECT_EQ(123, a.Perform(make_tuple())); +} + +// Tests using Invoke() with a unary method. +TEST(InvokeMethodTest, Unary) { + Foo foo; + Action a = Invoke(&foo, &Foo::Unary); // NOLINT + EXPECT_EQ(4123, a.Perform(make_tuple(4000))); +} + +// Tests using Invoke() with a binary method. +TEST(InvokeMethodTest, Binary) { + Foo foo; + Action a = Invoke(&foo, &Foo::Binary); + string s("Hell"); + EXPECT_EQ("Hello", a.Perform(make_tuple(s, 'o'))); +} + +// Tests using Invoke() with a ternary method. +TEST(InvokeMethodTest, Ternary) { + Foo foo; + Action a = Invoke(&foo, &Foo::Ternary); // NOLINT + EXPECT_EQ(1124, a.Perform(make_tuple(1000, true, Char(1)))); +} + +// Tests using Invoke() with a 4-argument method. +TEST(InvokeMethodTest, MethodThatTakes4Arguments) { + Foo foo; + Action a = Invoke(&foo, &Foo::SumOf4); // NOLINT + EXPECT_EQ(1357, a.Perform(make_tuple(1000, 200, 30, 4))); +} + +// Tests using Invoke() with a 5-argument method. +TEST(InvokeMethodTest, MethodThatTakes5Arguments) { + Foo foo; + Action a = Invoke(&foo, &Foo::SumOf5); // NOLINT + EXPECT_EQ(12345, a.Perform(make_tuple(10000, 2000, 300, 40, 5))); +} + +// Tests using Invoke() with a 6-argument method. +TEST(InvokeMethodTest, MethodThatTakes6Arguments) { + Foo foo; + Action a = // NOLINT + Invoke(&foo, &Foo::SumOf6); + EXPECT_EQ(123456, a.Perform(make_tuple(100000, 20000, 3000, 400, 50, 6))); +} + +// Tests using Invoke() with a 7-argument method. +TEST(InvokeMethodTest, MethodThatTakes7Arguments) { + Foo foo; + Action a = + Invoke(&foo, &Foo::Concat7); + EXPECT_EQ("1234567", + a.Perform(make_tuple(CharPtr("1"), CharPtr("2"), CharPtr("3"), + CharPtr("4"), CharPtr("5"), CharPtr("6"), + CharPtr("7")))); +} + +// Tests using Invoke() with a 8-argument method. +TEST(InvokeMethodTest, MethodThatTakes8Arguments) { + Foo foo; + Action a = + Invoke(&foo, &Foo::Concat8); + EXPECT_EQ("12345678", + a.Perform(make_tuple(CharPtr("1"), CharPtr("2"), CharPtr("3"), + CharPtr("4"), CharPtr("5"), CharPtr("6"), + CharPtr("7"), CharPtr("8")))); +} + +// Tests using Invoke() with a 9-argument method. +TEST(InvokeMethodTest, MethodThatTakes9Arguments) { + Foo foo; + Action a = Invoke(&foo, &Foo::Concat9); + EXPECT_EQ("123456789", + a.Perform(make_tuple(CharPtr("1"), CharPtr("2"), CharPtr("3"), + CharPtr("4"), CharPtr("5"), CharPtr("6"), + CharPtr("7"), CharPtr("8"), CharPtr("9")))); +} + +// Tests using Invoke() with a 10-argument method. +TEST(InvokeMethodTest, MethodThatTakes10Arguments) { + Foo foo; + Action a = Invoke(&foo, &Foo::Concat10); + EXPECT_EQ("1234567890", + a.Perform(make_tuple(CharPtr("1"), CharPtr("2"), CharPtr("3"), + CharPtr("4"), CharPtr("5"), CharPtr("6"), + CharPtr("7"), CharPtr("8"), CharPtr("9"), + CharPtr("0")))); +} + +// Tests using Invoke(f) as an action of a compatible type. +TEST(InvokeMethodTest, MethodWithCompatibleType) { + Foo foo; + Action a = // NOLINT + Invoke(&foo, &Foo::SumOf4); + EXPECT_EQ(4444, a.Perform(make_tuple(4000, Short(300), Char(20), true))); +} + +// Tests using WithoutArgs with an action that takes no argument. +TEST(WithoutArgsTest, NoArg) { + Action a = WithoutArgs(Invoke(Nullary)); // NOLINT + EXPECT_EQ(1, a.Perform(make_tuple(2))); +} + +// Tests using WithArg with an action that takes 1 argument. +TEST(WithArgTest, OneArg) { + Action b = WithArg<1>(Invoke(Unary)); // NOLINT + EXPECT_TRUE(b.Perform(make_tuple(1.5, -1))); + EXPECT_FALSE(b.Perform(make_tuple(1.5, 1))); +} + +TEST(ReturnArgActionTest, WorksForOneArgIntArg0) { + const Action a = ReturnArg<0>(); + EXPECT_EQ(5, a.Perform(make_tuple(5))); +} + +TEST(ReturnArgActionTest, WorksForMultiArgBoolArg0) { + const Action a = ReturnArg<0>(); + EXPECT_TRUE(a.Perform(make_tuple(true, false, false))); +} + +TEST(ReturnArgActionTest, WorksForMultiArgStringArg2) { + const Action a = ReturnArg<2>(); + EXPECT_EQ("seven", a.Perform(make_tuple(5, 6, string("seven"), 8))); +} + +TEST(SaveArgActionTest, WorksForSameType) { + int result = 0; + const Action a1 = SaveArg<0>(&result); + a1.Perform(make_tuple(5)); + EXPECT_EQ(5, result); +} + +TEST(SaveArgActionTest, WorksForCompatibleType) { + int result = 0; + const Action a1 = SaveArg<1>(&result); + a1.Perform(make_tuple(true, 'a')); + EXPECT_EQ('a', result); +} + +TEST(SaveArgPointeeActionTest, WorksForSameType) { + int result = 0; + const int value = 5; + const Action a1 = SaveArgPointee<0>(&result); + a1.Perform(make_tuple(&value)); + EXPECT_EQ(5, result); +} + +TEST(SaveArgPointeeActionTest, WorksForCompatibleType) { + int result = 0; + char value = 'a'; + const Action a1 = SaveArgPointee<1>(&result); + a1.Perform(make_tuple(true, &value)); + EXPECT_EQ('a', result); +} + +TEST(SaveArgPointeeActionTest, WorksForLinkedPtr) { + int result = 0; + linked_ptr value(new int(5)); + const Action)> a1 = SaveArgPointee<0>(&result); + a1.Perform(make_tuple(value)); + EXPECT_EQ(5, result); +} + +TEST(SetArgRefereeActionTest, WorksForSameType) { + int value = 0; + const Action a1 = SetArgReferee<0>(1); + a1.Perform(tuple(value)); + EXPECT_EQ(1, value); +} + +TEST(SetArgRefereeActionTest, WorksForCompatibleType) { + int value = 0; + const Action a1 = SetArgReferee<1>('a'); + a1.Perform(tuple(0, value)); + EXPECT_EQ('a', value); +} + +TEST(SetArgRefereeActionTest, WorksWithExtraArguments) { + int value = 0; + const Action a1 = SetArgReferee<2>('a'); + a1.Perform(tuple(true, 0, value, "hi")); + EXPECT_EQ('a', value); +} + +// A class that can be used to verify that its destructor is called: it will set +// the bool provided to the constructor to true when destroyed. +class DeletionTester { + public: + explicit DeletionTester(bool* is_deleted) + : is_deleted_(is_deleted) { + // Make sure the bit is set to false. + *is_deleted_ = false; + } + + ~DeletionTester() { + *is_deleted_ = true; + } + + private: + bool* is_deleted_; +}; + +TEST(DeleteArgActionTest, OneArg) { + bool is_deleted = false; + DeletionTester* t = new DeletionTester(&is_deleted); + const Action a1 = DeleteArg<0>(); // NOLINT + EXPECT_FALSE(is_deleted); + a1.Perform(make_tuple(t)); + EXPECT_TRUE(is_deleted); +} + +TEST(DeleteArgActionTest, TenArgs) { + bool is_deleted = false; + DeletionTester* t = new DeletionTester(&is_deleted); + const Action a1 = DeleteArg<9>(); + EXPECT_FALSE(is_deleted); + a1.Perform(make_tuple(true, 5, 6, CharPtr("hi"), false, 7, 8, 9, 10, t)); + EXPECT_TRUE(is_deleted); +} + +#if GTEST_HAS_EXCEPTIONS + +TEST(ThrowActionTest, ThrowsGivenExceptionInVoidFunction) { + const Action a = Throw('a'); + EXPECT_THROW(a.Perform(make_tuple(0)), char); +} + +class MyException {}; + +TEST(ThrowActionTest, ThrowsGivenExceptionInNonVoidFunction) { + const Action a = Throw(MyException()); + EXPECT_THROW(a.Perform(make_tuple('0')), MyException); +} + +TEST(ThrowActionTest, ThrowsGivenExceptionInNullaryFunction) { + const Action a = Throw(MyException()); + EXPECT_THROW(a.Perform(make_tuple()), MyException); +} + +#endif // GTEST_HAS_EXCEPTIONS + +// Tests that SetArrayArgument(first, last) sets the elements of the array +// pointed to by the N-th (0-based) argument to values in range [first, last). +TEST(SetArrayArgumentTest, SetsTheNthArray) { + typedef void MyFunction(bool, int*, char*); + int numbers[] = { 1, 2, 3 }; + Action a = SetArrayArgument<1>(numbers, numbers + 3); + + int n[4] = {}; + int* pn = n; + char ch[4] = {}; + char* pch = ch; + a.Perform(make_tuple(true, pn, pch)); + EXPECT_EQ(1, n[0]); + EXPECT_EQ(2, n[1]); + EXPECT_EQ(3, n[2]); + EXPECT_EQ(0, n[3]); + EXPECT_EQ('\0', ch[0]); + EXPECT_EQ('\0', ch[1]); + EXPECT_EQ('\0', ch[2]); + EXPECT_EQ('\0', ch[3]); + + // Tests first and last are iterators. + std::string letters = "abc"; + a = SetArrayArgument<2>(letters.begin(), letters.end()); + std::fill_n(n, 4, 0); + std::fill_n(ch, 4, '\0'); + a.Perform(make_tuple(true, pn, pch)); + EXPECT_EQ(0, n[0]); + EXPECT_EQ(0, n[1]); + EXPECT_EQ(0, n[2]); + EXPECT_EQ(0, n[3]); + EXPECT_EQ('a', ch[0]); + EXPECT_EQ('b', ch[1]); + EXPECT_EQ('c', ch[2]); + EXPECT_EQ('\0', ch[3]); +} + +// Tests SetArrayArgument(first, last) where first == last. +TEST(SetArrayArgumentTest, SetsTheNthArrayWithEmptyRange) { + typedef void MyFunction(bool, int*); + int numbers[] = { 1, 2, 3 }; + Action a = SetArrayArgument<1>(numbers, numbers); + + int n[4] = {}; + int* pn = n; + a.Perform(make_tuple(true, pn)); + EXPECT_EQ(0, n[0]); + EXPECT_EQ(0, n[1]); + EXPECT_EQ(0, n[2]); + EXPECT_EQ(0, n[3]); +} + +// Tests SetArrayArgument(first, last) where *first is convertible +// (but not equal) to the argument type. +TEST(SetArrayArgumentTest, SetsTheNthArrayWithConvertibleType) { + typedef void MyFunction(bool, char*); + int codes[] = { 97, 98, 99 }; + Action a = SetArrayArgument<1>(codes, codes + 3); + + char ch[4] = {}; + char* pch = ch; + a.Perform(make_tuple(true, pch)); + EXPECT_EQ('a', ch[0]); + EXPECT_EQ('b', ch[1]); + EXPECT_EQ('c', ch[2]); + EXPECT_EQ('\0', ch[3]); +} + +// Test SetArrayArgument(first, last) with iterator as argument. +TEST(SetArrayArgumentTest, SetsTheNthArrayWithIteratorArgument) { + typedef void MyFunction(bool, std::back_insert_iterator); + std::string letters = "abc"; + Action a = SetArrayArgument<1>(letters.begin(), letters.end()); + + std::string s; + a.Perform(make_tuple(true, back_inserter(s))); + EXPECT_EQ(letters, s); +} + +TEST(ReturnPointeeTest, Works) { + int n = 42; + const Action a = ReturnPointee(&n); + EXPECT_EQ(42, a.Perform(make_tuple())); + + n = 43; + EXPECT_EQ(43, a.Perform(make_tuple())); +} + +} // namespace gmock_generated_actions_test +} // namespace testing diff --git a/libwvdrmengine/test/gmock/test/gmock-nice-strict_test.cc b/libwvdrmengine/test/gmock/test/gmock-nice-strict_test.cc new file mode 100644 index 00000000..e3344180 --- /dev/null +++ b/libwvdrmengine/test/gmock/test/gmock-nice-strict_test.cc @@ -0,0 +1,284 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +#include "gmock/gmock-generated-nice-strict.h" + +#include +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "gtest/gtest-spi.h" + +// This must not be defined inside the ::testing namespace, or it will +// clash with ::testing::Mock. +class Mock { + public: + Mock() {} + + MOCK_METHOD0(DoThis, void()); + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(Mock); +}; + +namespace testing { +namespace gmock_nice_strict_test { + +using testing::internal::string; +using testing::GMOCK_FLAG(verbose); +using testing::HasSubstr; +using testing::NiceMock; +using testing::StrictMock; + +#if GTEST_HAS_STREAM_REDIRECTION +using testing::internal::CaptureStdout; +using testing::internal::GetCapturedStdout; +#endif + +// Defines some mock classes needed by the tests. + +class Foo { + public: + virtual ~Foo() {} + + virtual void DoThis() = 0; + virtual int DoThat(bool flag) = 0; +}; + +class MockFoo : public Foo { + public: + MockFoo() {} + void Delete() { delete this; } + + MOCK_METHOD0(DoThis, void()); + MOCK_METHOD1(DoThat, int(bool flag)); + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFoo); +}; + +class MockBar { + public: + explicit MockBar(const string& s) : str_(s) {} + + MockBar(char a1, char a2, string a3, string a4, int a5, int a6, + const string& a7, const string& a8, bool a9, bool a10) { + str_ = string() + a1 + a2 + a3 + a4 + static_cast(a5) + + static_cast(a6) + a7 + a8 + (a9 ? 'T' : 'F') + (a10 ? 'T' : 'F'); + } + + virtual ~MockBar() {} + + const string& str() const { return str_; } + + MOCK_METHOD0(This, int()); + MOCK_METHOD2(That, string(int, bool)); + + private: + string str_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(MockBar); +}; + +#if GTEST_HAS_STREAM_REDIRECTION + +// Tests that a nice mock generates no warning for uninteresting calls. +TEST(NiceMockTest, NoWarningForUninterestingCall) { + NiceMock nice_foo; + + CaptureStdout(); + nice_foo.DoThis(); + nice_foo.DoThat(true); + EXPECT_STREQ("", GetCapturedStdout().c_str()); +} + +// Tests that a nice mock generates no warning for uninteresting calls +// that delete the mock object. +TEST(NiceMockTest, NoWarningForUninterestingCallAfterDeath) { + NiceMock* const nice_foo = new NiceMock; + + ON_CALL(*nice_foo, DoThis()) + .WillByDefault(Invoke(nice_foo, &MockFoo::Delete)); + + CaptureStdout(); + nice_foo->DoThis(); + EXPECT_STREQ("", GetCapturedStdout().c_str()); +} + +// Tests that a nice mock generates informational logs for +// uninteresting calls. +TEST(NiceMockTest, InfoForUninterestingCall) { + NiceMock nice_foo; + + const string saved_flag = GMOCK_FLAG(verbose); + GMOCK_FLAG(verbose) = "info"; + CaptureStdout(); + nice_foo.DoThis(); + EXPECT_THAT(GetCapturedStdout(), + HasSubstr("Uninteresting mock function call")); + + CaptureStdout(); + nice_foo.DoThat(true); + EXPECT_THAT(GetCapturedStdout(), + HasSubstr("Uninteresting mock function call")); + GMOCK_FLAG(verbose) = saved_flag; +} + +#endif // GTEST_HAS_STREAM_REDIRECTION + +// Tests that a nice mock allows expected calls. +TEST(NiceMockTest, AllowsExpectedCall) { + NiceMock nice_foo; + + EXPECT_CALL(nice_foo, DoThis()); + nice_foo.DoThis(); +} + +// Tests that an unexpected call on a nice mock fails. +TEST(NiceMockTest, UnexpectedCallFails) { + NiceMock nice_foo; + + EXPECT_CALL(nice_foo, DoThis()).Times(0); + EXPECT_NONFATAL_FAILURE(nice_foo.DoThis(), "called more times than expected"); +} + +// Tests that NiceMock works with a mock class that has a non-default +// constructor. +TEST(NiceMockTest, NonDefaultConstructor) { + NiceMock nice_bar("hi"); + EXPECT_EQ("hi", nice_bar.str()); + + nice_bar.This(); + nice_bar.That(5, true); +} + +// Tests that NiceMock works with a mock class that has a 10-ary +// non-default constructor. +TEST(NiceMockTest, NonDefaultConstructor10) { + NiceMock nice_bar('a', 'b', "c", "d", 'e', 'f', + "g", "h", true, false); + EXPECT_EQ("abcdefghTF", nice_bar.str()); + + nice_bar.This(); + nice_bar.That(5, true); +} + +#if !GTEST_OS_SYMBIAN && !GTEST_OS_WINDOWS_MOBILE +// Tests that NiceMock compiles where Mock is a user-defined +// class (as opposed to ::testing::Mock). We had to workaround an +// MSVC 8.0 bug that caused the symbol Mock used in the definition of +// NiceMock to be looked up in the wrong context, and this test +// ensures that our fix works. +// +// We have to skip this test on Symbian and Windows Mobile, as it +// causes the program to crash there, for reasons unclear to us yet. +TEST(NiceMockTest, AcceptsClassNamedMock) { + NiceMock< ::Mock> nice; + EXPECT_CALL(nice, DoThis()); + nice.DoThis(); +} +#endif // !GTEST_OS_SYMBIAN && !GTEST_OS_WINDOWS_MOBILE + +// Tests that a strict mock allows expected calls. +TEST(StrictMockTest, AllowsExpectedCall) { + StrictMock strict_foo; + + EXPECT_CALL(strict_foo, DoThis()); + strict_foo.DoThis(); +} + +// Tests that an unexpected call on a strict mock fails. +TEST(StrictMockTest, UnexpectedCallFails) { + StrictMock strict_foo; + + EXPECT_CALL(strict_foo, DoThis()).Times(0); + EXPECT_NONFATAL_FAILURE(strict_foo.DoThis(), + "called more times than expected"); +} + +// Tests that an uninteresting call on a strict mock fails. +TEST(StrictMockTest, UninterestingCallFails) { + StrictMock strict_foo; + + EXPECT_NONFATAL_FAILURE(strict_foo.DoThis(), + "Uninteresting mock function call"); +} + +// Tests that an uninteresting call on a strict mock fails, even if +// the call deletes the mock object. +TEST(StrictMockTest, UninterestingCallFailsAfterDeath) { + StrictMock* const strict_foo = new StrictMock; + + ON_CALL(*strict_foo, DoThis()) + .WillByDefault(Invoke(strict_foo, &MockFoo::Delete)); + + EXPECT_NONFATAL_FAILURE(strict_foo->DoThis(), + "Uninteresting mock function call"); +} + +// Tests that StrictMock works with a mock class that has a +// non-default constructor. +TEST(StrictMockTest, NonDefaultConstructor) { + StrictMock strict_bar("hi"); + EXPECT_EQ("hi", strict_bar.str()); + + EXPECT_NONFATAL_FAILURE(strict_bar.That(5, true), + "Uninteresting mock function call"); +} + +// Tests that StrictMock works with a mock class that has a 10-ary +// non-default constructor. +TEST(StrictMockTest, NonDefaultConstructor10) { + StrictMock strict_bar('a', 'b', "c", "d", 'e', 'f', + "g", "h", true, false); + EXPECT_EQ("abcdefghTF", strict_bar.str()); + + EXPECT_NONFATAL_FAILURE(strict_bar.That(5, true), + "Uninteresting mock function call"); +} + +#if !GTEST_OS_SYMBIAN && !GTEST_OS_WINDOWS_MOBILE +// Tests that StrictMock compiles where Mock is a user-defined +// class (as opposed to ::testing::Mock). We had to workaround an +// MSVC 8.0 bug that caused the symbol Mock used in the definition of +// StrictMock to be looked up in the wrong context, and this test +// ensures that our fix works. +// +// We have to skip this test on Symbian and Windows Mobile, as it +// causes the program to crash there, for reasons unclear to us yet. +TEST(StrictMockTest, AcceptsClassNamedMock) { + StrictMock< ::Mock> strict; + EXPECT_CALL(strict, DoThis()); + strict.DoThis(); +} +#endif // !GTEST_OS_SYMBIAN && !GTEST_OS_WINDOWS_MOBILE + +} // namespace gmock_nice_strict_test +} // namespace testing diff --git a/libwvdrmengine/test/gmock/test/gmock-port_test.cc b/libwvdrmengine/test/gmock/test/gmock-port_test.cc new file mode 100644 index 00000000..d6a8d444 --- /dev/null +++ b/libwvdrmengine/test/gmock/test/gmock-port_test.cc @@ -0,0 +1,43 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: vladl@google.com (Vlad Losev) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file tests the internal cross-platform support utilities. + +#include "gmock/internal/gmock-port.h" +#include "gtest/gtest.h" + +// NOTE: if this file is left without tests for some reason, put a dummy +// test here to make references to symbols in the gtest library and avoid +// 'undefined symbol' linker errors in gmock_main: + +TEST(DummyTest, Dummy) {} diff --git a/libwvdrmengine/test/gmock/test/gmock-spec-builders_test.cc b/libwvdrmengine/test/gmock/test/gmock-spec-builders_test.cc new file mode 100644 index 00000000..29d47d12 --- /dev/null +++ b/libwvdrmengine/test/gmock/test/gmock-spec-builders_test.cc @@ -0,0 +1,2484 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file tests the spec builder syntax. + +#include "gmock/gmock-spec-builders.h" + +#include // NOLINT +#include +#include + +#include "gmock/gmock.h" +#include "gmock/internal/gmock-port.h" +#include "gtest/gtest.h" +#include "gtest/gtest-spi.h" +#include "gtest/internal/gtest-port.h" + +namespace testing { +namespace internal { + +// Helper class for testing the Expectation class template. +class ExpectationTester { + public: + // Sets the call count of the given expectation to the given number. + void SetCallCount(int n, ExpectationBase* exp) { + exp->call_count_ = n; + } +}; + +} // namespace internal +} // namespace testing + +namespace { + +using testing::_; +using testing::AnyNumber; +using testing::AtLeast; +using testing::AtMost; +using testing::Between; +using testing::Cardinality; +using testing::CardinalityInterface; +using testing::ContainsRegex; +using testing::Const; +using testing::DoAll; +using testing::DoDefault; +using testing::Eq; +using testing::Expectation; +using testing::ExpectationSet; +using testing::GMOCK_FLAG(verbose); +using testing::Gt; +using testing::InSequence; +using testing::Invoke; +using testing::InvokeWithoutArgs; +using testing::IsSubstring; +using testing::Lt; +using testing::Message; +using testing::Mock; +using testing::Ne; +using testing::Return; +using testing::Sequence; +using testing::internal::ExpectationTester; +using testing::internal::FormatFileLocation; +using testing::internal::g_gmock_mutex; +using testing::internal::kErrorVerbosity; +using testing::internal::kInfoVerbosity; +using testing::internal::kWarningVerbosity; +using testing::internal::String; +using testing::internal::string; + +#if GTEST_HAS_STREAM_REDIRECTION +using testing::HasSubstr; +using testing::internal::CaptureStdout; +using testing::internal::GetCapturedStdout; +#endif + +class Incomplete; + +class MockIncomplete { + public: + // This line verifies that a mock method can take a by-reference + // argument of an incomplete type. + MOCK_METHOD1(ByRefFunc, void(const Incomplete& x)); +}; + +// Tells Google Mock how to print a value of type Incomplete. +void PrintTo(const Incomplete& x, ::std::ostream* os); + +TEST(MockMethodTest, CanInstantiateWithIncompleteArgType) { + // Even though this mock class contains a mock method that takes + // by-reference an argument whose type is incomplete, we can still + // use the mock, as long as Google Mock knows how to print the + // argument. + MockIncomplete incomplete; + EXPECT_CALL(incomplete, ByRefFunc(_)) + .Times(AnyNumber()); +} + +// The definition of the printer for the argument type doesn't have to +// be visible where the mock is used. +void PrintTo(const Incomplete& /* x */, ::std::ostream* os) { + *os << "incomplete"; +} + +class Result {}; + +class MockA { + public: + MockA() {} + + MOCK_METHOD1(DoA, void(int n)); // NOLINT + MOCK_METHOD1(ReturnResult, Result(int n)); // NOLINT + MOCK_METHOD2(Binary, bool(int x, int y)); // NOLINT + MOCK_METHOD2(ReturnInt, int(int x, int y)); // NOLINT + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(MockA); +}; + +class MockB { + public: + MockB() {} + + MOCK_CONST_METHOD0(DoB, int()); // NOLINT + MOCK_METHOD1(DoB, int(int n)); // NOLINT + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(MockB); +}; + +// Tests that EXPECT_CALL and ON_CALL compile in a presence of macro +// redefining a mock method name. This could happen, for example, when +// the tested code #includes Win32 API headers which define many APIs +// as macros, e.g. #define TextOut TextOutW. + +#define Method MethodW + +class CC { + public: + virtual ~CC() {} + virtual int Method() = 0; +}; +class MockCC : public CC { + public: + MockCC() {} + + MOCK_METHOD0(Method, int()); + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(MockCC); +}; + +// Tests that a method with expanded name compiles. +TEST(OnCallSyntaxTest, CompilesWithMethodNameExpandedFromMacro) { + MockCC cc; + ON_CALL(cc, Method()); +} + +// Tests that the method with expanded name not only compiles but runs +// and returns a correct value, too. +TEST(OnCallSyntaxTest, WorksWithMethodNameExpandedFromMacro) { + MockCC cc; + ON_CALL(cc, Method()).WillByDefault(Return(42)); + EXPECT_EQ(42, cc.Method()); +} + +// Tests that a method with expanded name compiles. +TEST(ExpectCallSyntaxTest, CompilesWithMethodNameExpandedFromMacro) { + MockCC cc; + EXPECT_CALL(cc, Method()); + cc.Method(); +} + +// Tests that it works, too. +TEST(ExpectCallSyntaxTest, WorksWithMethodNameExpandedFromMacro) { + MockCC cc; + EXPECT_CALL(cc, Method()).WillOnce(Return(42)); + EXPECT_EQ(42, cc.Method()); +} + +#undef Method // Done with macro redefinition tests. + +// Tests that ON_CALL evaluates its arguments exactly once as promised +// by Google Mock. +TEST(OnCallSyntaxTest, EvaluatesFirstArgumentOnce) { + MockA a; + MockA* pa = &a; + + ON_CALL(*pa++, DoA(_)); + EXPECT_EQ(&a + 1, pa); +} + +TEST(OnCallSyntaxTest, EvaluatesSecondArgumentOnce) { + MockA a; + int n = 0; + + ON_CALL(a, DoA(n++)); + EXPECT_EQ(1, n); +} + +// Tests that the syntax of ON_CALL() is enforced at run time. + +TEST(OnCallSyntaxTest, WithIsOptional) { + MockA a; + + ON_CALL(a, DoA(5)) + .WillByDefault(Return()); + ON_CALL(a, DoA(_)) + .With(_) + .WillByDefault(Return()); +} + +TEST(OnCallSyntaxTest, WithCanAppearAtMostOnce) { + MockA a; + + EXPECT_NONFATAL_FAILURE({ // NOLINT + ON_CALL(a, ReturnResult(_)) + .With(_) + .With(_) + .WillByDefault(Return(Result())); + }, ".With() cannot appear more than once in an ON_CALL()"); +} + +TEST(OnCallSyntaxTest, WillByDefaultIsMandatory) { + MockA a; + + EXPECT_DEATH_IF_SUPPORTED({ + ON_CALL(a, DoA(5)); + a.DoA(5); + }, ""); +} + +TEST(OnCallSyntaxTest, WillByDefaultCanAppearAtMostOnce) { + MockA a; + + EXPECT_NONFATAL_FAILURE({ // NOLINT + ON_CALL(a, DoA(5)) + .WillByDefault(Return()) + .WillByDefault(Return()); + }, ".WillByDefault() must appear exactly once in an ON_CALL()"); +} + +// Tests that EXPECT_CALL evaluates its arguments exactly once as +// promised by Google Mock. +TEST(ExpectCallSyntaxTest, EvaluatesFirstArgumentOnce) { + MockA a; + MockA* pa = &a; + + EXPECT_CALL(*pa++, DoA(_)); + a.DoA(0); + EXPECT_EQ(&a + 1, pa); +} + +TEST(ExpectCallSyntaxTest, EvaluatesSecondArgumentOnce) { + MockA a; + int n = 0; + + EXPECT_CALL(a, DoA(n++)); + a.DoA(0); + EXPECT_EQ(1, n); +} + +// Tests that the syntax of EXPECT_CALL() is enforced at run time. + +TEST(ExpectCallSyntaxTest, WithIsOptional) { + MockA a; + + EXPECT_CALL(a, DoA(5)) + .Times(0); + EXPECT_CALL(a, DoA(6)) + .With(_) + .Times(0); +} + +TEST(ExpectCallSyntaxTest, WithCanAppearAtMostOnce) { + MockA a; + + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_CALL(a, DoA(6)) + .With(_) + .With(_); + }, ".With() cannot appear more than once in an EXPECT_CALL()"); + + a.DoA(6); +} + +TEST(ExpectCallSyntaxTest, WithMustBeFirstClause) { + MockA a; + + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_CALL(a, DoA(1)) + .Times(1) + .With(_); + }, ".With() must be the first clause in an EXPECT_CALL()"); + + a.DoA(1); + + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_CALL(a, DoA(2)) + .WillOnce(Return()) + .With(_); + }, ".With() must be the first clause in an EXPECT_CALL()"); + + a.DoA(2); +} + +TEST(ExpectCallSyntaxTest, TimesCanBeInferred) { + MockA a; + + EXPECT_CALL(a, DoA(1)) + .WillOnce(Return()); + + EXPECT_CALL(a, DoA(2)) + .WillOnce(Return()) + .WillRepeatedly(Return()); + + a.DoA(1); + a.DoA(2); + a.DoA(2); +} + +TEST(ExpectCallSyntaxTest, TimesCanAppearAtMostOnce) { + MockA a; + + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_CALL(a, DoA(1)) + .Times(1) + .Times(2); + }, ".Times() cannot appear more than once in an EXPECT_CALL()"); + + a.DoA(1); + a.DoA(1); +} + +TEST(ExpectCallSyntaxTest, TimesMustBeBeforeInSequence) { + MockA a; + Sequence s; + + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_CALL(a, DoA(1)) + .InSequence(s) + .Times(1); + }, ".Times() cannot appear after "); + + a.DoA(1); +} + +TEST(ExpectCallSyntaxTest, InSequenceIsOptional) { + MockA a; + Sequence s; + + EXPECT_CALL(a, DoA(1)); + EXPECT_CALL(a, DoA(2)) + .InSequence(s); + + a.DoA(1); + a.DoA(2); +} + +TEST(ExpectCallSyntaxTest, InSequenceCanAppearMultipleTimes) { + MockA a; + Sequence s1, s2; + + EXPECT_CALL(a, DoA(1)) + .InSequence(s1, s2) + .InSequence(s1); + + a.DoA(1); +} + +TEST(ExpectCallSyntaxTest, InSequenceMustBeBeforeAfter) { + MockA a; + Sequence s; + + Expectation e = EXPECT_CALL(a, DoA(1)) + .Times(AnyNumber()); + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_CALL(a, DoA(2)) + .After(e) + .InSequence(s); + }, ".InSequence() cannot appear after "); + + a.DoA(2); +} + +TEST(ExpectCallSyntaxTest, InSequenceMustBeBeforeWillOnce) { + MockA a; + Sequence s; + + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_CALL(a, DoA(1)) + .WillOnce(Return()) + .InSequence(s); + }, ".InSequence() cannot appear after "); + + a.DoA(1); +} + +TEST(ExpectCallSyntaxTest, AfterMustBeBeforeWillOnce) { + MockA a; + + Expectation e = EXPECT_CALL(a, DoA(1)); + EXPECT_NONFATAL_FAILURE({ + EXPECT_CALL(a, DoA(2)) + .WillOnce(Return()) + .After(e); + }, ".After() cannot appear after "); + + a.DoA(1); + a.DoA(2); +} + +TEST(ExpectCallSyntaxTest, WillIsOptional) { + MockA a; + + EXPECT_CALL(a, DoA(1)); + EXPECT_CALL(a, DoA(2)) + .WillOnce(Return()); + + a.DoA(1); + a.DoA(2); +} + +TEST(ExpectCallSyntaxTest, WillCanAppearMultipleTimes) { + MockA a; + + EXPECT_CALL(a, DoA(1)) + .Times(AnyNumber()) + .WillOnce(Return()) + .WillOnce(Return()) + .WillOnce(Return()); +} + +TEST(ExpectCallSyntaxTest, WillMustBeBeforeWillRepeatedly) { + MockA a; + + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_CALL(a, DoA(1)) + .WillRepeatedly(Return()) + .WillOnce(Return()); + }, ".WillOnce() cannot appear after "); + + a.DoA(1); +} + +TEST(ExpectCallSyntaxTest, WillRepeatedlyIsOptional) { + MockA a; + + EXPECT_CALL(a, DoA(1)) + .WillOnce(Return()); + EXPECT_CALL(a, DoA(2)) + .WillOnce(Return()) + .WillRepeatedly(Return()); + + a.DoA(1); + a.DoA(2); + a.DoA(2); +} + +TEST(ExpectCallSyntaxTest, WillRepeatedlyCannotAppearMultipleTimes) { + MockA a; + + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_CALL(a, DoA(1)) + .WillRepeatedly(Return()) + .WillRepeatedly(Return()); + }, ".WillRepeatedly() cannot appear more than once in an " + "EXPECT_CALL()"); +} + +TEST(ExpectCallSyntaxTest, WillRepeatedlyMustBeBeforeRetiresOnSaturation) { + MockA a; + + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_CALL(a, DoA(1)) + .RetiresOnSaturation() + .WillRepeatedly(Return()); + }, ".WillRepeatedly() cannot appear after "); +} + +TEST(ExpectCallSyntaxTest, RetiresOnSaturationIsOptional) { + MockA a; + + EXPECT_CALL(a, DoA(1)); + EXPECT_CALL(a, DoA(1)) + .RetiresOnSaturation(); + + a.DoA(1); + a.DoA(1); +} + +TEST(ExpectCallSyntaxTest, RetiresOnSaturationCannotAppearMultipleTimes) { + MockA a; + + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_CALL(a, DoA(1)) + .RetiresOnSaturation() + .RetiresOnSaturation(); + }, ".RetiresOnSaturation() cannot appear more than once"); + + a.DoA(1); +} + +TEST(ExpectCallSyntaxTest, DefaultCardinalityIsOnce) { + { + MockA a; + EXPECT_CALL(a, DoA(1)); + a.DoA(1); + } + EXPECT_NONFATAL_FAILURE({ // NOLINT + MockA a; + EXPECT_CALL(a, DoA(1)); + }, "to be called once"); + EXPECT_NONFATAL_FAILURE({ // NOLINT + MockA a; + EXPECT_CALL(a, DoA(1)); + a.DoA(1); + a.DoA(1); + }, "to be called once"); +} + +#if GTEST_HAS_STREAM_REDIRECTION + +// Tests that Google Mock doesn't print a warning when the number of +// WillOnce() is adequate. +TEST(ExpectCallSyntaxTest, DoesNotWarnOnAdequateActionCount) { + CaptureStdout(); + { + MockB b; + + // It's always fine to omit WillOnce() entirely. + EXPECT_CALL(b, DoB()) + .Times(0); + EXPECT_CALL(b, DoB(1)) + .Times(AtMost(1)); + EXPECT_CALL(b, DoB(2)) + .Times(1) + .WillRepeatedly(Return(1)); + + // It's fine for the number of WillOnce()s to equal the upper bound. + EXPECT_CALL(b, DoB(3)) + .Times(Between(1, 2)) + .WillOnce(Return(1)) + .WillOnce(Return(2)); + + // It's fine for the number of WillOnce()s to be smaller than the + // upper bound when there is a WillRepeatedly(). + EXPECT_CALL(b, DoB(4)) + .Times(AtMost(3)) + .WillOnce(Return(1)) + .WillRepeatedly(Return(2)); + + // Satisfies the above expectations. + b.DoB(2); + b.DoB(3); + } + EXPECT_STREQ("", GetCapturedStdout().c_str()); +} + +// Tests that Google Mock warns on having too many actions in an +// expectation compared to its cardinality. +TEST(ExpectCallSyntaxTest, WarnsOnTooManyActions) { + CaptureStdout(); + { + MockB b; + + // Warns when the number of WillOnce()s is larger than the upper bound. + EXPECT_CALL(b, DoB()) + .Times(0) + .WillOnce(Return(1)); // #1 + EXPECT_CALL(b, DoB()) + .Times(AtMost(1)) + .WillOnce(Return(1)) + .WillOnce(Return(2)); // #2 + EXPECT_CALL(b, DoB(1)) + .Times(1) + .WillOnce(Return(1)) + .WillOnce(Return(2)) + .RetiresOnSaturation(); // #3 + + // Warns when the number of WillOnce()s equals the upper bound and + // there is a WillRepeatedly(). + EXPECT_CALL(b, DoB()) + .Times(0) + .WillRepeatedly(Return(1)); // #4 + EXPECT_CALL(b, DoB(2)) + .Times(1) + .WillOnce(Return(1)) + .WillRepeatedly(Return(2)); // #5 + + // Satisfies the above expectations. + b.DoB(1); + b.DoB(2); + } + const String output = GetCapturedStdout(); + EXPECT_PRED_FORMAT2( + IsSubstring, + "Too many actions specified in EXPECT_CALL(b, DoB())...\n" + "Expected to be never called, but has 1 WillOnce().", + output); // #1 + EXPECT_PRED_FORMAT2( + IsSubstring, + "Too many actions specified in EXPECT_CALL(b, DoB())...\n" + "Expected to be called at most once, " + "but has 2 WillOnce()s.", + output); // #2 + EXPECT_PRED_FORMAT2( + IsSubstring, + "Too many actions specified in EXPECT_CALL(b, DoB(1))...\n" + "Expected to be called once, but has 2 WillOnce()s.", + output); // #3 + EXPECT_PRED_FORMAT2( + IsSubstring, + "Too many actions specified in EXPECT_CALL(b, DoB())...\n" + "Expected to be never called, but has 0 WillOnce()s " + "and a WillRepeatedly().", + output); // #4 + EXPECT_PRED_FORMAT2( + IsSubstring, + "Too many actions specified in EXPECT_CALL(b, DoB(2))...\n" + "Expected to be called once, but has 1 WillOnce() " + "and a WillRepeatedly().", + output); // #5 +} + +// Tests that Google Mock warns on having too few actions in an +// expectation compared to its cardinality. +TEST(ExpectCallSyntaxTest, WarnsOnTooFewActions) { + MockB b; + + EXPECT_CALL(b, DoB()) + .Times(Between(2, 3)) + .WillOnce(Return(1)); + + CaptureStdout(); + b.DoB(); + const String output = GetCapturedStdout(); + EXPECT_PRED_FORMAT2( + IsSubstring, + "Too few actions specified in EXPECT_CALL(b, DoB())...\n" + "Expected to be called between 2 and 3 times, " + "but has only 1 WillOnce().", + output); + b.DoB(); +} + +#endif // GTEST_HAS_STREAM_REDIRECTION + +// Tests the semantics of ON_CALL(). + +// Tests that the built-in default action is taken when no ON_CALL() +// is specified. +TEST(OnCallTest, TakesBuiltInDefaultActionWhenNoOnCall) { + MockB b; + EXPECT_CALL(b, DoB()); + + EXPECT_EQ(0, b.DoB()); +} + +// Tests that the built-in default action is taken when no ON_CALL() +// matches the invocation. +TEST(OnCallTest, TakesBuiltInDefaultActionWhenNoOnCallMatches) { + MockB b; + ON_CALL(b, DoB(1)) + .WillByDefault(Return(1)); + EXPECT_CALL(b, DoB(_)); + + EXPECT_EQ(0, b.DoB(2)); +} + +// Tests that the last matching ON_CALL() action is taken. +TEST(OnCallTest, PicksLastMatchingOnCall) { + MockB b; + ON_CALL(b, DoB(_)) + .WillByDefault(Return(3)); + ON_CALL(b, DoB(2)) + .WillByDefault(Return(2)); + ON_CALL(b, DoB(1)) + .WillByDefault(Return(1)); + EXPECT_CALL(b, DoB(_)); + + EXPECT_EQ(2, b.DoB(2)); +} + +// Tests the semantics of EXPECT_CALL(). + +// Tests that any call is allowed when no EXPECT_CALL() is specified. +TEST(ExpectCallTest, AllowsAnyCallWhenNoSpec) { + MockB b; + EXPECT_CALL(b, DoB()); + // There is no expectation on DoB(int). + + b.DoB(); + + // DoB(int) can be called any number of times. + b.DoB(1); + b.DoB(2); +} + +// Tests that the last matching EXPECT_CALL() fires. +TEST(ExpectCallTest, PicksLastMatchingExpectCall) { + MockB b; + EXPECT_CALL(b, DoB(_)) + .WillRepeatedly(Return(2)); + EXPECT_CALL(b, DoB(1)) + .WillRepeatedly(Return(1)); + + EXPECT_EQ(1, b.DoB(1)); +} + +// Tests lower-bound violation. +TEST(ExpectCallTest, CatchesTooFewCalls) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + MockB b; + EXPECT_CALL(b, DoB(5)) + .Times(AtLeast(2)); + + b.DoB(5); + }, "Actual function call count doesn't match EXPECT_CALL(b, DoB(5))...\n" + " Expected: to be called at least twice\n" + " Actual: called once - unsatisfied and active"); +} + +// Tests that the cardinality can be inferred when no Times(...) is +// specified. +TEST(ExpectCallTest, InfersCardinalityWhenThereIsNoWillRepeatedly) { + { + MockB b; + EXPECT_CALL(b, DoB()) + .WillOnce(Return(1)) + .WillOnce(Return(2)); + + EXPECT_EQ(1, b.DoB()); + EXPECT_EQ(2, b.DoB()); + } + + EXPECT_NONFATAL_FAILURE({ // NOLINT + MockB b; + EXPECT_CALL(b, DoB()) + .WillOnce(Return(1)) + .WillOnce(Return(2)); + + EXPECT_EQ(1, b.DoB()); + }, "to be called twice"); + + { // NOLINT + MockB b; + EXPECT_CALL(b, DoB()) + .WillOnce(Return(1)) + .WillOnce(Return(2)); + + EXPECT_EQ(1, b.DoB()); + EXPECT_EQ(2, b.DoB()); + EXPECT_NONFATAL_FAILURE(b.DoB(), "to be called twice"); + } +} + +TEST(ExpectCallTest, InfersCardinality1WhenThereIsWillRepeatedly) { + { + MockB b; + EXPECT_CALL(b, DoB()) + .WillOnce(Return(1)) + .WillRepeatedly(Return(2)); + + EXPECT_EQ(1, b.DoB()); + } + + { // NOLINT + MockB b; + EXPECT_CALL(b, DoB()) + .WillOnce(Return(1)) + .WillRepeatedly(Return(2)); + + EXPECT_EQ(1, b.DoB()); + EXPECT_EQ(2, b.DoB()); + EXPECT_EQ(2, b.DoB()); + } + + EXPECT_NONFATAL_FAILURE({ // NOLINT + MockB b; + EXPECT_CALL(b, DoB()) + .WillOnce(Return(1)) + .WillRepeatedly(Return(2)); + }, "to be called at least once"); +} + +// Tests that the n-th action is taken for the n-th matching +// invocation. +TEST(ExpectCallTest, NthMatchTakesNthAction) { + MockB b; + EXPECT_CALL(b, DoB()) + .WillOnce(Return(1)) + .WillOnce(Return(2)) + .WillOnce(Return(3)); + + EXPECT_EQ(1, b.DoB()); + EXPECT_EQ(2, b.DoB()); + EXPECT_EQ(3, b.DoB()); +} + +// Tests that the WillRepeatedly() action is taken when the WillOnce(...) +// list is exhausted. +TEST(ExpectCallTest, TakesRepeatedActionWhenWillListIsExhausted) { + MockB b; + EXPECT_CALL(b, DoB()) + .WillOnce(Return(1)) + .WillRepeatedly(Return(2)); + + EXPECT_EQ(1, b.DoB()); + EXPECT_EQ(2, b.DoB()); + EXPECT_EQ(2, b.DoB()); +} + +#if GTEST_HAS_STREAM_REDIRECTION + +// Tests that the default action is taken when the WillOnce(...) list is +// exhausted and there is no WillRepeatedly(). +TEST(ExpectCallTest, TakesDefaultActionWhenWillListIsExhausted) { + MockB b; + EXPECT_CALL(b, DoB(_)) + .Times(1); + EXPECT_CALL(b, DoB()) + .Times(AnyNumber()) + .WillOnce(Return(1)) + .WillOnce(Return(2)); + + CaptureStdout(); + EXPECT_EQ(0, b.DoB(1)); // Shouldn't generate a warning as the + // expectation has no action clause at all. + EXPECT_EQ(1, b.DoB()); + EXPECT_EQ(2, b.DoB()); + const String output1 = GetCapturedStdout(); + EXPECT_STREQ("", output1.c_str()); + + CaptureStdout(); + EXPECT_EQ(0, b.DoB()); + EXPECT_EQ(0, b.DoB()); + const String output2 = GetCapturedStdout(); + EXPECT_THAT(output2.c_str(), + HasSubstr("Actions ran out in EXPECT_CALL(b, DoB())...\n" + "Called 3 times, but only 2 WillOnce()s are specified" + " - returning default value.")); + EXPECT_THAT(output2.c_str(), + HasSubstr("Actions ran out in EXPECT_CALL(b, DoB())...\n" + "Called 4 times, but only 2 WillOnce()s are specified" + " - returning default value.")); +} + +TEST(FunctionMockerTest, ReportsExpectCallLocationForExhausedActions) { + MockB b; + std::string expect_call_location = FormatFileLocation(__FILE__, __LINE__ + 1); + EXPECT_CALL(b, DoB()).Times(AnyNumber()).WillOnce(Return(1)); + + EXPECT_EQ(1, b.DoB()); + + CaptureStdout(); + EXPECT_EQ(0, b.DoB()); + const String output = GetCapturedStdout(); + // The warning message should contain the call location. + EXPECT_PRED_FORMAT2(IsSubstring, expect_call_location, output); +} + +TEST(FunctionMockerTest, ReportsDefaultActionLocationOfUninterestingCalls) { + std::string on_call_location; + CaptureStdout(); + { + MockB b; + on_call_location = FormatFileLocation(__FILE__, __LINE__ + 1); + ON_CALL(b, DoB(_)).WillByDefault(Return(0)); + b.DoB(0); + } + EXPECT_PRED_FORMAT2(IsSubstring, on_call_location, GetCapturedStdout()); +} + +#endif // GTEST_HAS_STREAM_REDIRECTION + +// Tests that an uninteresting call performs the default action. +TEST(UninterestingCallTest, DoesDefaultAction) { + // When there is an ON_CALL() statement, the action specified by it + // should be taken. + MockA a; + ON_CALL(a, Binary(_, _)) + .WillByDefault(Return(true)); + EXPECT_TRUE(a.Binary(1, 2)); + + // When there is no ON_CALL(), the default value for the return type + // should be returned. + MockB b; + EXPECT_EQ(0, b.DoB()); +} + +// Tests that an unexpected call performs the default action. +TEST(UnexpectedCallTest, DoesDefaultAction) { + // When there is an ON_CALL() statement, the action specified by it + // should be taken. + MockA a; + ON_CALL(a, Binary(_, _)) + .WillByDefault(Return(true)); + EXPECT_CALL(a, Binary(0, 0)); + a.Binary(0, 0); + bool result = false; + EXPECT_NONFATAL_FAILURE(result = a.Binary(1, 2), + "Unexpected mock function call"); + EXPECT_TRUE(result); + + // When there is no ON_CALL(), the default value for the return type + // should be returned. + MockB b; + EXPECT_CALL(b, DoB(0)) + .Times(0); + int n = -1; + EXPECT_NONFATAL_FAILURE(n = b.DoB(1), + "Unexpected mock function call"); + EXPECT_EQ(0, n); +} + +// Tests that when an unexpected void function generates the right +// failure message. +TEST(UnexpectedCallTest, GeneratesFailureForVoidFunction) { + // First, tests the message when there is only one EXPECT_CALL(). + MockA a1; + EXPECT_CALL(a1, DoA(1)); + a1.DoA(1); + // Ideally we should match the failure message against a regex, but + // EXPECT_NONFATAL_FAILURE doesn't support that, so we test for + // multiple sub-strings instead. + EXPECT_NONFATAL_FAILURE( + a1.DoA(9), + "Unexpected mock function call - returning directly.\n" + " Function call: DoA(9)\n" + "Google Mock tried the following 1 expectation, but it didn't match:"); + EXPECT_NONFATAL_FAILURE( + a1.DoA(9), + " Expected arg #0: is equal to 1\n" + " Actual: 9\n" + " Expected: to be called once\n" + " Actual: called once - saturated and active"); + + // Next, tests the message when there are more than one EXPECT_CALL(). + MockA a2; + EXPECT_CALL(a2, DoA(1)); + EXPECT_CALL(a2, DoA(3)); + a2.DoA(1); + EXPECT_NONFATAL_FAILURE( + a2.DoA(2), + "Unexpected mock function call - returning directly.\n" + " Function call: DoA(2)\n" + "Google Mock tried the following 2 expectations, but none matched:"); + EXPECT_NONFATAL_FAILURE( + a2.DoA(2), + "tried expectation #0: EXPECT_CALL(a2, DoA(1))...\n" + " Expected arg #0: is equal to 1\n" + " Actual: 2\n" + " Expected: to be called once\n" + " Actual: called once - saturated and active"); + EXPECT_NONFATAL_FAILURE( + a2.DoA(2), + "tried expectation #1: EXPECT_CALL(a2, DoA(3))...\n" + " Expected arg #0: is equal to 3\n" + " Actual: 2\n" + " Expected: to be called once\n" + " Actual: never called - unsatisfied and active"); + a2.DoA(3); +} + +// Tests that an unexpected non-void function generates the right +// failure message. +TEST(UnexpectedCallTest, GeneartesFailureForNonVoidFunction) { + MockB b1; + EXPECT_CALL(b1, DoB(1)); + b1.DoB(1); + EXPECT_NONFATAL_FAILURE( + b1.DoB(2), + "Unexpected mock function call - returning default value.\n" + " Function call: DoB(2)\n" + " Returns: 0\n" + "Google Mock tried the following 1 expectation, but it didn't match:"); + EXPECT_NONFATAL_FAILURE( + b1.DoB(2), + " Expected arg #0: is equal to 1\n" + " Actual: 2\n" + " Expected: to be called once\n" + " Actual: called once - saturated and active"); +} + +// Tests that Google Mock explains that an retired expectation doesn't +// match the call. +TEST(UnexpectedCallTest, RetiredExpectation) { + MockB b; + EXPECT_CALL(b, DoB(1)) + .RetiresOnSaturation(); + + b.DoB(1); + EXPECT_NONFATAL_FAILURE( + b.DoB(1), + " Expected: the expectation is active\n" + " Actual: it is retired"); +} + +// Tests that Google Mock explains that an expectation that doesn't +// match the arguments doesn't match the call. +TEST(UnexpectedCallTest, UnmatchedArguments) { + MockB b; + EXPECT_CALL(b, DoB(1)); + + EXPECT_NONFATAL_FAILURE( + b.DoB(2), + " Expected arg #0: is equal to 1\n" + " Actual: 2\n"); + b.DoB(1); +} + +// Tests that Google Mock explains that an expectation with +// unsatisfied pre-requisites doesn't match the call. +TEST(UnexpectedCallTest, UnsatisifiedPrerequisites) { + Sequence s1, s2; + MockB b; + EXPECT_CALL(b, DoB(1)) + .InSequence(s1); + EXPECT_CALL(b, DoB(2)) + .Times(AnyNumber()) + .InSequence(s1); + EXPECT_CALL(b, DoB(3)) + .InSequence(s2); + EXPECT_CALL(b, DoB(4)) + .InSequence(s1, s2); + + ::testing::TestPartResultArray failures; + { + ::testing::ScopedFakeTestPartResultReporter reporter(&failures); + b.DoB(4); + // Now 'failures' contains the Google Test failures generated by + // the above statement. + } + + // There should be one non-fatal failure. + ASSERT_EQ(1, failures.size()); + const ::testing::TestPartResult& r = failures.GetTestPartResult(0); + EXPECT_EQ(::testing::TestPartResult::kNonFatalFailure, r.type()); + + // Verifies that the failure message contains the two unsatisfied + // pre-requisites but not the satisfied one. +#if GTEST_USES_PCRE + EXPECT_THAT(r.message(), ContainsRegex( + // PCRE has trouble using (.|\n) to match any character, but + // supports the (?s) prefix for using . to match any character. + "(?s)the following immediate pre-requisites are not satisfied:\n" + ".*: pre-requisite #0\n" + ".*: pre-requisite #1")); +#elif GTEST_USES_POSIX_RE + EXPECT_THAT(r.message(), ContainsRegex( + // POSIX RE doesn't understand the (?s) prefix, but has no trouble + // with (.|\n). + "the following immediate pre-requisites are not satisfied:\n" + "(.|\n)*: pre-requisite #0\n" + "(.|\n)*: pre-requisite #1")); +#else + // We can only use Google Test's own simple regex. + EXPECT_THAT(r.message(), ContainsRegex( + "the following immediate pre-requisites are not satisfied:")); + EXPECT_THAT(r.message(), ContainsRegex(": pre-requisite #0")); + EXPECT_THAT(r.message(), ContainsRegex(": pre-requisite #1")); +#endif // GTEST_USES_PCRE + + b.DoB(1); + b.DoB(3); + b.DoB(4); +} + +TEST(UndefinedReturnValueTest, ReturnValueIsMandatory) { + MockA a; + // TODO(wan@google.com): We should really verify the output message, + // but we cannot yet due to that EXPECT_DEATH only captures stderr + // while Google Mock logs to stdout. + EXPECT_DEATH_IF_SUPPORTED(a.ReturnResult(1), ""); +} + +// Tests that an excessive call (one whose arguments match the +// matchers but is called too many times) performs the default action. +TEST(ExcessiveCallTest, DoesDefaultAction) { + // When there is an ON_CALL() statement, the action specified by it + // should be taken. + MockA a; + ON_CALL(a, Binary(_, _)) + .WillByDefault(Return(true)); + EXPECT_CALL(a, Binary(0, 0)); + a.Binary(0, 0); + bool result = false; + EXPECT_NONFATAL_FAILURE(result = a.Binary(0, 0), + "Mock function called more times than expected"); + EXPECT_TRUE(result); + + // When there is no ON_CALL(), the default value for the return type + // should be returned. + MockB b; + EXPECT_CALL(b, DoB(0)) + .Times(0); + int n = -1; + EXPECT_NONFATAL_FAILURE(n = b.DoB(0), + "Mock function called more times than expected"); + EXPECT_EQ(0, n); +} + +// Tests that when a void function is called too many times, +// the failure message contains the argument values. +TEST(ExcessiveCallTest, GeneratesFailureForVoidFunction) { + MockA a; + EXPECT_CALL(a, DoA(_)) + .Times(0); + EXPECT_NONFATAL_FAILURE( + a.DoA(9), + "Mock function called more times than expected - returning directly.\n" + " Function call: DoA(9)\n" + " Expected: to be never called\n" + " Actual: called once - over-saturated and active"); +} + +// Tests that when a non-void function is called too many times, the +// failure message contains the argument values and the return value. +TEST(ExcessiveCallTest, GeneratesFailureForNonVoidFunction) { + MockB b; + EXPECT_CALL(b, DoB(_)); + b.DoB(1); + EXPECT_NONFATAL_FAILURE( + b.DoB(2), + "Mock function called more times than expected - " + "returning default value.\n" + " Function call: DoB(2)\n" + " Returns: 0\n" + " Expected: to be called once\n" + " Actual: called twice - over-saturated and active"); +} + +// Tests using sequences. + +TEST(InSequenceTest, AllExpectationInScopeAreInSequence) { + MockA a; + { + InSequence dummy; + + EXPECT_CALL(a, DoA(1)); + EXPECT_CALL(a, DoA(2)); + } + + EXPECT_NONFATAL_FAILURE({ // NOLINT + a.DoA(2); + }, "Unexpected mock function call"); + + a.DoA(1); + a.DoA(2); +} + +TEST(InSequenceTest, NestedInSequence) { + MockA a; + { + InSequence dummy; + + EXPECT_CALL(a, DoA(1)); + { + InSequence dummy2; + + EXPECT_CALL(a, DoA(2)); + EXPECT_CALL(a, DoA(3)); + } + } + + EXPECT_NONFATAL_FAILURE({ // NOLINT + a.DoA(1); + a.DoA(3); + }, "Unexpected mock function call"); + + a.DoA(2); + a.DoA(3); +} + +TEST(InSequenceTest, ExpectationsOutOfScopeAreNotAffected) { + MockA a; + { + InSequence dummy; + + EXPECT_CALL(a, DoA(1)); + EXPECT_CALL(a, DoA(2)); + } + EXPECT_CALL(a, DoA(3)); + + EXPECT_NONFATAL_FAILURE({ // NOLINT + a.DoA(2); + }, "Unexpected mock function call"); + + a.DoA(3); + a.DoA(1); + a.DoA(2); +} + +// Tests that any order is allowed when no sequence is used. +TEST(SequenceTest, AnyOrderIsOkByDefault) { + { + MockA a; + MockB b; + + EXPECT_CALL(a, DoA(1)); + EXPECT_CALL(b, DoB()) + .Times(AnyNumber()); + + a.DoA(1); + b.DoB(); + } + + { // NOLINT + MockA a; + MockB b; + + EXPECT_CALL(a, DoA(1)); + EXPECT_CALL(b, DoB()) + .Times(AnyNumber()); + + b.DoB(); + a.DoA(1); + } +} + +// Tests that the calls must be in strict order when a complete order +// is specified. +TEST(SequenceTest, CallsMustBeInStrictOrderWhenSaidSo) { + MockA a; + Sequence s; + + EXPECT_CALL(a, ReturnResult(1)) + .InSequence(s) + .WillOnce(Return(Result())); + + EXPECT_CALL(a, ReturnResult(2)) + .InSequence(s) + .WillOnce(Return(Result())); + + EXPECT_CALL(a, ReturnResult(3)) + .InSequence(s) + .WillOnce(Return(Result())); + + EXPECT_DEATH_IF_SUPPORTED({ + a.ReturnResult(1); + a.ReturnResult(3); + a.ReturnResult(2); + }, ""); + + EXPECT_DEATH_IF_SUPPORTED({ + a.ReturnResult(2); + a.ReturnResult(1); + a.ReturnResult(3); + }, ""); + + a.ReturnResult(1); + a.ReturnResult(2); + a.ReturnResult(3); +} + +// Tests specifying a DAG using multiple sequences. +TEST(SequenceTest, CallsMustConformToSpecifiedDag) { + MockA a; + MockB b; + Sequence x, y; + + EXPECT_CALL(a, ReturnResult(1)) + .InSequence(x) + .WillOnce(Return(Result())); + + EXPECT_CALL(b, DoB()) + .Times(2) + .InSequence(y); + + EXPECT_CALL(a, ReturnResult(2)) + .InSequence(x, y) + .WillRepeatedly(Return(Result())); + + EXPECT_CALL(a, ReturnResult(3)) + .InSequence(x) + .WillOnce(Return(Result())); + + EXPECT_DEATH_IF_SUPPORTED({ + a.ReturnResult(1); + b.DoB(); + a.ReturnResult(2); + }, ""); + + EXPECT_DEATH_IF_SUPPORTED({ + a.ReturnResult(2); + }, ""); + + EXPECT_DEATH_IF_SUPPORTED({ + a.ReturnResult(3); + }, ""); + + EXPECT_DEATH_IF_SUPPORTED({ + a.ReturnResult(1); + b.DoB(); + b.DoB(); + a.ReturnResult(3); + a.ReturnResult(2); + }, ""); + + b.DoB(); + a.ReturnResult(1); + b.DoB(); + a.ReturnResult(3); +} + +TEST(SequenceTest, Retirement) { + MockA a; + Sequence s; + + EXPECT_CALL(a, DoA(1)) + .InSequence(s); + EXPECT_CALL(a, DoA(_)) + .InSequence(s) + .RetiresOnSaturation(); + EXPECT_CALL(a, DoA(1)) + .InSequence(s); + + a.DoA(1); + a.DoA(2); + a.DoA(1); +} + +// Tests Expectation. + +TEST(ExpectationTest, ConstrutorsWork) { + MockA a; + Expectation e1; // Default ctor. + + // Ctor from various forms of EXPECT_CALL. + Expectation e2 = EXPECT_CALL(a, DoA(2)); + Expectation e3 = EXPECT_CALL(a, DoA(3)).With(_); + { + Sequence s; + Expectation e4 = EXPECT_CALL(a, DoA(4)).Times(1); + Expectation e5 = EXPECT_CALL(a, DoA(5)).InSequence(s); + } + Expectation e6 = EXPECT_CALL(a, DoA(6)).After(e2); + Expectation e7 = EXPECT_CALL(a, DoA(7)).WillOnce(Return()); + Expectation e8 = EXPECT_CALL(a, DoA(8)).WillRepeatedly(Return()); + Expectation e9 = EXPECT_CALL(a, DoA(9)).RetiresOnSaturation(); + + Expectation e10 = e2; // Copy ctor. + + EXPECT_THAT(e1, Ne(e2)); + EXPECT_THAT(e2, Eq(e10)); + + a.DoA(2); + a.DoA(3); + a.DoA(4); + a.DoA(5); + a.DoA(6); + a.DoA(7); + a.DoA(8); + a.DoA(9); +} + +TEST(ExpectationTest, AssignmentWorks) { + MockA a; + Expectation e1; + Expectation e2 = EXPECT_CALL(a, DoA(1)); + + EXPECT_THAT(e1, Ne(e2)); + + e1 = e2; + EXPECT_THAT(e1, Eq(e2)); + + a.DoA(1); +} + +// Tests ExpectationSet. + +TEST(ExpectationSetTest, MemberTypesAreCorrect) { + ::testing::StaticAssertTypeEq(); +} + +TEST(ExpectationSetTest, ConstructorsWork) { + MockA a; + + Expectation e1; + const Expectation e2; + ExpectationSet es1; // Default ctor. + ExpectationSet es2 = EXPECT_CALL(a, DoA(1)); // Ctor from EXPECT_CALL. + ExpectationSet es3 = e1; // Ctor from Expectation. + ExpectationSet es4(e1); // Ctor from Expectation; alternative syntax. + ExpectationSet es5 = e2; // Ctor from const Expectation. + ExpectationSet es6(e2); // Ctor from const Expectation; alternative syntax. + ExpectationSet es7 = es2; // Copy ctor. + + EXPECT_EQ(0, es1.size()); + EXPECT_EQ(1, es2.size()); + EXPECT_EQ(1, es3.size()); + EXPECT_EQ(1, es4.size()); + EXPECT_EQ(1, es5.size()); + EXPECT_EQ(1, es6.size()); + EXPECT_EQ(1, es7.size()); + + EXPECT_THAT(es3, Ne(es2)); + EXPECT_THAT(es4, Eq(es3)); + EXPECT_THAT(es5, Eq(es4)); + EXPECT_THAT(es6, Eq(es5)); + EXPECT_THAT(es7, Eq(es2)); + a.DoA(1); +} + +TEST(ExpectationSetTest, AssignmentWorks) { + ExpectationSet es1; + ExpectationSet es2 = Expectation(); + + es1 = es2; + EXPECT_EQ(1, es1.size()); + EXPECT_THAT(*(es1.begin()), Eq(Expectation())); + EXPECT_THAT(es1, Eq(es2)); +} + +TEST(ExpectationSetTest, InsertionWorks) { + ExpectationSet es1; + Expectation e1; + es1 += e1; + EXPECT_EQ(1, es1.size()); + EXPECT_THAT(*(es1.begin()), Eq(e1)); + + MockA a; + Expectation e2 = EXPECT_CALL(a, DoA(1)); + es1 += e2; + EXPECT_EQ(2, es1.size()); + + ExpectationSet::const_iterator it1 = es1.begin(); + ExpectationSet::const_iterator it2 = it1; + ++it2; + EXPECT_TRUE(*it1 == e1 || *it2 == e1); // e1 must be in the set. + EXPECT_TRUE(*it1 == e2 || *it2 == e2); // e2 must be in the set too. + a.DoA(1); +} + +TEST(ExpectationSetTest, SizeWorks) { + ExpectationSet es; + EXPECT_EQ(0, es.size()); + + es += Expectation(); + EXPECT_EQ(1, es.size()); + + MockA a; + es += EXPECT_CALL(a, DoA(1)); + EXPECT_EQ(2, es.size()); + + a.DoA(1); +} + +TEST(ExpectationSetTest, IsEnumerable) { + ExpectationSet es; + EXPECT_THAT(es.begin(), Eq(es.end())); + + es += Expectation(); + ExpectationSet::const_iterator it = es.begin(); + EXPECT_THAT(it, Ne(es.end())); + EXPECT_THAT(*it, Eq(Expectation())); + ++it; + EXPECT_THAT(it, Eq(es.end())); +} + +// Tests the .After() clause. + +TEST(AfterTest, SucceedsWhenPartialOrderIsSatisfied) { + MockA a; + ExpectationSet es; + es += EXPECT_CALL(a, DoA(1)); + es += EXPECT_CALL(a, DoA(2)); + EXPECT_CALL(a, DoA(3)) + .After(es); + + a.DoA(1); + a.DoA(2); + a.DoA(3); +} + +TEST(AfterTest, SucceedsWhenTotalOrderIsSatisfied) { + MockA a; + MockB b; + // The following also verifies that const Expectation objects work + // too. Do not remove the const modifiers. + const Expectation e1 = EXPECT_CALL(a, DoA(1)); + const Expectation e2 = EXPECT_CALL(b, DoB()) + .Times(2) + .After(e1); + EXPECT_CALL(a, DoA(2)).After(e2); + + a.DoA(1); + b.DoB(); + b.DoB(); + a.DoA(2); +} + +// Calls must be in strict order when specified so. +TEST(AfterDeathTest, CallsMustBeInStrictOrderWhenSpecifiedSo) { + MockA a; + MockB b; + Expectation e1 = EXPECT_CALL(a, DoA(1)); + Expectation e2 = EXPECT_CALL(b, DoB()) + .Times(2) + .After(e1); + EXPECT_CALL(a, ReturnResult(2)) + .After(e2) + .WillOnce(Return(Result())); + + a.DoA(1); + // If a call to ReturnResult() violates the specified order, no + // matching expectation will be found, and thus the default action + // will be done. Since the return type of ReturnResult() is not a + // built-in type, gmock won't know what to return and will thus + // abort the program. Therefore a death test can tell us whether + // gmock catches the order violation correctly. + // + // gtest and gmock print messages to stdout, which isn't captured by + // death tests. Therefore we have to match with an empty regular + // expression in all the EXPECT_DEATH()s. + EXPECT_DEATH_IF_SUPPORTED(a.ReturnResult(2), ""); + + b.DoB(); + EXPECT_DEATH_IF_SUPPORTED(a.ReturnResult(2), ""); + + b.DoB(); + a.ReturnResult(2); +} + +// Calls must satisfy the partial order when specified so. +TEST(AfterDeathTest, CallsMustSatisfyPartialOrderWhenSpecifiedSo) { + MockA a; + Expectation e = EXPECT_CALL(a, DoA(1)); + const ExpectationSet es = EXPECT_CALL(a, DoA(2)); + EXPECT_CALL(a, ReturnResult(3)) + .After(e, es) + .WillOnce(Return(Result())); + + EXPECT_DEATH_IF_SUPPORTED(a.ReturnResult(3), ""); + + a.DoA(2); + EXPECT_DEATH_IF_SUPPORTED(a.ReturnResult(3), ""); + + a.DoA(1); + a.ReturnResult(3); +} + +// .After() can be combined with .InSequence(). +TEST(AfterDeathTest, CanBeUsedWithInSequence) { + MockA a; + Sequence s; + Expectation e = EXPECT_CALL(a, DoA(1)); + EXPECT_CALL(a, DoA(2)).InSequence(s); + EXPECT_CALL(a, ReturnResult(3)) + .InSequence(s).After(e) + .WillOnce(Return(Result())); + + a.DoA(1); + EXPECT_DEATH_IF_SUPPORTED(a.ReturnResult(3), ""); + + a.DoA(2); + a.ReturnResult(3); +} + +// .After() can be called multiple times. +TEST(AfterTest, CanBeCalledManyTimes) { + MockA a; + Expectation e1 = EXPECT_CALL(a, DoA(1)); + Expectation e2 = EXPECT_CALL(a, DoA(2)); + Expectation e3 = EXPECT_CALL(a, DoA(3)); + EXPECT_CALL(a, DoA(4)) + .After(e1) + .After(e2) + .After(e3); + + a.DoA(3); + a.DoA(1); + a.DoA(2); + a.DoA(4); +} + +// .After() accepts up to 5 arguments. +TEST(AfterTest, AcceptsUpToFiveArguments) { + MockA a; + Expectation e1 = EXPECT_CALL(a, DoA(1)); + Expectation e2 = EXPECT_CALL(a, DoA(2)); + Expectation e3 = EXPECT_CALL(a, DoA(3)); + ExpectationSet es1 = EXPECT_CALL(a, DoA(4)); + ExpectationSet es2 = EXPECT_CALL(a, DoA(5)); + EXPECT_CALL(a, DoA(6)) + .After(e1, e2, e3, es1, es2); + + a.DoA(5); + a.DoA(2); + a.DoA(4); + a.DoA(1); + a.DoA(3); + a.DoA(6); +} + +// .After() allows input to contain duplicated Expectations. +TEST(AfterTest, AcceptsDuplicatedInput) { + MockA a; + Expectation e1 = EXPECT_CALL(a, DoA(1)); + Expectation e2 = EXPECT_CALL(a, DoA(2)); + ExpectationSet es; + es += e1; + es += e2; + EXPECT_CALL(a, ReturnResult(3)) + .After(e1, e2, es, e1) + .WillOnce(Return(Result())); + + a.DoA(1); + EXPECT_DEATH_IF_SUPPORTED(a.ReturnResult(3), ""); + + a.DoA(2); + a.ReturnResult(3); +} + +// An Expectation added to an ExpectationSet after it has been used in +// an .After() has no effect. +TEST(AfterTest, ChangesToExpectationSetHaveNoEffectAfterwards) { + MockA a; + ExpectationSet es1 = EXPECT_CALL(a, DoA(1)); + Expectation e2 = EXPECT_CALL(a, DoA(2)); + EXPECT_CALL(a, DoA(3)) + .After(es1); + es1 += e2; + + a.DoA(1); + a.DoA(3); + a.DoA(2); +} + +// Tests that Google Mock correctly handles calls to mock functions +// after a mock object owning one of their pre-requisites has died. + +// Tests that calls that satisfy the original spec are successful. +TEST(DeletingMockEarlyTest, Success1) { + MockB* const b1 = new MockB; + MockA* const a = new MockA; + MockB* const b2 = new MockB; + + { + InSequence dummy; + EXPECT_CALL(*b1, DoB(_)) + .WillOnce(Return(1)); + EXPECT_CALL(*a, Binary(_, _)) + .Times(AnyNumber()) + .WillRepeatedly(Return(true)); + EXPECT_CALL(*b2, DoB(_)) + .Times(AnyNumber()) + .WillRepeatedly(Return(2)); + } + + EXPECT_EQ(1, b1->DoB(1)); + delete b1; + // a's pre-requisite has died. + EXPECT_TRUE(a->Binary(0, 1)); + delete b2; + // a's successor has died. + EXPECT_TRUE(a->Binary(1, 2)); + delete a; +} + +// Tests that calls that satisfy the original spec are successful. +TEST(DeletingMockEarlyTest, Success2) { + MockB* const b1 = new MockB; + MockA* const a = new MockA; + MockB* const b2 = new MockB; + + { + InSequence dummy; + EXPECT_CALL(*b1, DoB(_)) + .WillOnce(Return(1)); + EXPECT_CALL(*a, Binary(_, _)) + .Times(AnyNumber()); + EXPECT_CALL(*b2, DoB(_)) + .Times(AnyNumber()) + .WillRepeatedly(Return(2)); + } + + delete a; // a is trivially satisfied. + EXPECT_EQ(1, b1->DoB(1)); + EXPECT_EQ(2, b2->DoB(2)); + delete b1; + delete b2; +} + +// Tests that it's OK to delete a mock object itself in its action. + +// Suppresses warning on unreferenced formal parameter in MSVC with +// -W4. +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable:4100) +#endif + +ACTION_P(Delete, ptr) { delete ptr; } + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + +TEST(DeletingMockEarlyTest, CanDeleteSelfInActionReturningVoid) { + MockA* const a = new MockA; + EXPECT_CALL(*a, DoA(_)).WillOnce(Delete(a)); + a->DoA(42); // This will cause a to be deleted. +} + +TEST(DeletingMockEarlyTest, CanDeleteSelfInActionReturningValue) { + MockA* const a = new MockA; + EXPECT_CALL(*a, ReturnResult(_)) + .WillOnce(DoAll(Delete(a), Return(Result()))); + a->ReturnResult(42); // This will cause a to be deleted. +} + +// Tests that calls that violate the original spec yield failures. +TEST(DeletingMockEarlyTest, Failure1) { + MockB* const b1 = new MockB; + MockA* const a = new MockA; + MockB* const b2 = new MockB; + + { + InSequence dummy; + EXPECT_CALL(*b1, DoB(_)) + .WillOnce(Return(1)); + EXPECT_CALL(*a, Binary(_, _)) + .Times(AnyNumber()); + EXPECT_CALL(*b2, DoB(_)) + .Times(AnyNumber()) + .WillRepeatedly(Return(2)); + } + + delete a; // a is trivially satisfied. + EXPECT_NONFATAL_FAILURE({ + b2->DoB(2); + }, "Unexpected mock function call"); + EXPECT_EQ(1, b1->DoB(1)); + delete b1; + delete b2; +} + +// Tests that calls that violate the original spec yield failures. +TEST(DeletingMockEarlyTest, Failure2) { + MockB* const b1 = new MockB; + MockA* const a = new MockA; + MockB* const b2 = new MockB; + + { + InSequence dummy; + EXPECT_CALL(*b1, DoB(_)); + EXPECT_CALL(*a, Binary(_, _)) + .Times(AnyNumber()); + EXPECT_CALL(*b2, DoB(_)) + .Times(AnyNumber()); + } + + EXPECT_NONFATAL_FAILURE(delete b1, + "Actual: never called"); + EXPECT_NONFATAL_FAILURE(a->Binary(0, 1), + "Unexpected mock function call"); + EXPECT_NONFATAL_FAILURE(b2->DoB(1), + "Unexpected mock function call"); + delete a; + delete b2; +} + +class EvenNumberCardinality : public CardinalityInterface { + public: + // Returns true iff call_count calls will satisfy this cardinality. + virtual bool IsSatisfiedByCallCount(int call_count) const { + return call_count % 2 == 0; + } + + // Returns true iff call_count calls will saturate this cardinality. + virtual bool IsSaturatedByCallCount(int /* call_count */) const { + return false; + } + + // Describes self to an ostream. + virtual void DescribeTo(::std::ostream* os) const { + *os << "called even number of times"; + } +}; + +Cardinality EvenNumber() { + return Cardinality(new EvenNumberCardinality); +} + +TEST(ExpectationBaseTest, + AllPrerequisitesAreSatisfiedWorksForNonMonotonicCardinality) { + MockA* a = new MockA; + Sequence s; + + EXPECT_CALL(*a, DoA(1)) + .Times(EvenNumber()) + .InSequence(s); + EXPECT_CALL(*a, DoA(2)) + .Times(AnyNumber()) + .InSequence(s); + EXPECT_CALL(*a, DoA(3)) + .Times(AnyNumber()); + + a->DoA(3); + a->DoA(1); + EXPECT_NONFATAL_FAILURE(a->DoA(2), "Unexpected mock function call"); + EXPECT_NONFATAL_FAILURE(delete a, "to be called even number of times"); +} + +// The following tests verify the message generated when a mock +// function is called. + +struct Printable { +}; + +inline void operator<<(::std::ostream& os, const Printable&) { + os << "Printable"; +} + +struct Unprintable { + Unprintable() : value(0) {} + int value; +}; + +class MockC { + public: + MockC() {} + + MOCK_METHOD6(VoidMethod, void(bool cond, int n, string s, void* p, + const Printable& x, Unprintable y)); + MOCK_METHOD0(NonVoidMethod, int()); // NOLINT + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(MockC); +}; + +class VerboseFlagPreservingFixture : public testing::Test { + protected: + // The code needs to work when both ::string and ::std::string are defined + // and the flag is implemented as a testing::internal::String. In this + // case, without the call to c_str(), the compiler will complain that it + // cannot figure out what overload of string constructor to use. + // TODO(vladl@google.com): Use internal::string instead of String for + // string flags in Google Test. + VerboseFlagPreservingFixture() + : saved_verbose_flag_(GMOCK_FLAG(verbose).c_str()) {} + + ~VerboseFlagPreservingFixture() { GMOCK_FLAG(verbose) = saved_verbose_flag_; } + + private: + const string saved_verbose_flag_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(VerboseFlagPreservingFixture); +}; + +#if GTEST_HAS_STREAM_REDIRECTION + +// Tests that an uninteresting mock function call generates a warning +// containing the stack trace. +TEST(FunctionCallMessageTest, UninterestingCallGeneratesFyiWithStackTrace) { + MockC c; + CaptureStdout(); + c.VoidMethod(false, 5, "Hi", NULL, Printable(), Unprintable()); + const String output = GetCapturedStdout(); + EXPECT_PRED_FORMAT2(IsSubstring, "GMOCK WARNING", output); + EXPECT_PRED_FORMAT2(IsSubstring, "Stack trace:", output); + +# ifndef NDEBUG + + // We check the stack trace content in dbg-mode only, as opt-mode + // may inline the call we are interested in seeing. + + // Verifies that a void mock function's name appears in the stack + // trace. + EXPECT_PRED_FORMAT2(IsSubstring, "VoidMethod(", output); + + // Verifies that a non-void mock function's name appears in the + // stack trace. + CaptureStdout(); + c.NonVoidMethod(); + const String output2 = GetCapturedStdout(); + EXPECT_PRED_FORMAT2(IsSubstring, "NonVoidMethod(", output2); + +# endif // NDEBUG +} + +// Tests that an uninteresting mock function call causes the function +// arguments and return value to be printed. +TEST(FunctionCallMessageTest, UninterestingCallPrintsArgumentsAndReturnValue) { + // A non-void mock function. + MockB b; + CaptureStdout(); + b.DoB(); + const String output1 = GetCapturedStdout(); + EXPECT_PRED_FORMAT2( + IsSubstring, + "Uninteresting mock function call - returning default value.\n" + " Function call: DoB()\n" + " Returns: 0\n", output1.c_str()); + // Makes sure the return value is printed. + + // A void mock function. + MockC c; + CaptureStdout(); + c.VoidMethod(false, 5, "Hi", NULL, Printable(), Unprintable()); + const String output2 = GetCapturedStdout(); + EXPECT_THAT(output2.c_str(), + ContainsRegex( + "Uninteresting mock function call - returning directly\\.\n" + " Function call: VoidMethod" + "\\(false, 5, \"Hi\", NULL, @.+ " + "Printable, 4-byte object <00-00 00-00>\\)")); + // A void function has no return value to print. +} + +// Tests how the --gmock_verbose flag affects Google Mock's output. + +class GMockVerboseFlagTest : public VerboseFlagPreservingFixture { + public: + // Verifies that the given Google Mock output is correct. (When + // should_print is true, the output should match the given regex and + // contain the given function name in the stack trace. When it's + // false, the output should be empty.) + void VerifyOutput(const String& output, bool should_print, + const string& expected_substring, + const string& function_name) { + if (should_print) { + EXPECT_THAT(output.c_str(), HasSubstr(expected_substring)); +# ifndef NDEBUG + // We check the stack trace content in dbg-mode only, as opt-mode + // may inline the call we are interested in seeing. + EXPECT_THAT(output.c_str(), HasSubstr(function_name)); +# else + // Suppresses 'unused function parameter' warnings. + static_cast(function_name); +# endif // NDEBUG + } else { + EXPECT_STREQ("", output.c_str()); + } + } + + // Tests how the flag affects expected calls. + void TestExpectedCall(bool should_print) { + MockA a; + EXPECT_CALL(a, DoA(5)); + EXPECT_CALL(a, Binary(_, 1)) + .WillOnce(Return(true)); + + // A void-returning function. + CaptureStdout(); + a.DoA(5); + VerifyOutput( + GetCapturedStdout(), + should_print, + "Mock function call matches EXPECT_CALL(a, DoA(5))...\n" + " Function call: DoA(5)\n" + "Stack trace:\n", + "DoA"); + + // A non-void-returning function. + CaptureStdout(); + a.Binary(2, 1); + VerifyOutput( + GetCapturedStdout(), + should_print, + "Mock function call matches EXPECT_CALL(a, Binary(_, 1))...\n" + " Function call: Binary(2, 1)\n" + " Returns: true\n" + "Stack trace:\n", + "Binary"); + } + + // Tests how the flag affects uninteresting calls. + void TestUninterestingCall(bool should_print) { + MockA a; + + // A void-returning function. + CaptureStdout(); + a.DoA(5); + VerifyOutput( + GetCapturedStdout(), + should_print, + "\nGMOCK WARNING:\n" + "Uninteresting mock function call - returning directly.\n" + " Function call: DoA(5)\n" + "Stack trace:\n", + "DoA"); + + // A non-void-returning function. + CaptureStdout(); + a.Binary(2, 1); + VerifyOutput( + GetCapturedStdout(), + should_print, + "\nGMOCK WARNING:\n" + "Uninteresting mock function call - returning default value.\n" + " Function call: Binary(2, 1)\n" + " Returns: false\n" + "Stack trace:\n", + "Binary"); + } +}; + +// Tests that --gmock_verbose=info causes both expected and +// uninteresting calls to be reported. +TEST_F(GMockVerboseFlagTest, Info) { + GMOCK_FLAG(verbose) = kInfoVerbosity; + TestExpectedCall(true); + TestUninterestingCall(true); +} + +// Tests that --gmock_verbose=warning causes uninteresting calls to be +// reported. +TEST_F(GMockVerboseFlagTest, Warning) { + GMOCK_FLAG(verbose) = kWarningVerbosity; + TestExpectedCall(false); + TestUninterestingCall(true); +} + +// Tests that --gmock_verbose=warning causes neither expected nor +// uninteresting calls to be reported. +TEST_F(GMockVerboseFlagTest, Error) { + GMOCK_FLAG(verbose) = kErrorVerbosity; + TestExpectedCall(false); + TestUninterestingCall(false); +} + +// Tests that --gmock_verbose=SOME_INVALID_VALUE has the same effect +// as --gmock_verbose=warning. +TEST_F(GMockVerboseFlagTest, InvalidFlagIsTreatedAsWarning) { + GMOCK_FLAG(verbose) = "invalid"; // Treated as "warning". + TestExpectedCall(false); + TestUninterestingCall(true); +} + +#endif // GTEST_HAS_STREAM_REDIRECTION + +// A helper class that generates a failure when printed. We use it to +// ensure that Google Mock doesn't print a value (even to an internal +// buffer) when it is not supposed to do so. +class PrintMeNot {}; + +void PrintTo(PrintMeNot /* dummy */, ::std::ostream* /* os */) { + ADD_FAILURE() << "Google Mock is printing a value that shouldn't be " + << "printed even to an internal buffer."; +} + +class LogTestHelper { + public: + LogTestHelper() {} + + MOCK_METHOD1(Foo, PrintMeNot(PrintMeNot)); + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(LogTestHelper); +}; + +class GMockLogTest : public VerboseFlagPreservingFixture { + protected: + LogTestHelper helper_; +}; + +TEST_F(GMockLogTest, DoesNotPrintGoodCallInternallyIfVerbosityIsWarning) { + GMOCK_FLAG(verbose) = kWarningVerbosity; + EXPECT_CALL(helper_, Foo(_)) + .WillOnce(Return(PrintMeNot())); + helper_.Foo(PrintMeNot()); // This is an expected call. +} + +TEST_F(GMockLogTest, DoesNotPrintGoodCallInternallyIfVerbosityIsError) { + GMOCK_FLAG(verbose) = kErrorVerbosity; + EXPECT_CALL(helper_, Foo(_)) + .WillOnce(Return(PrintMeNot())); + helper_.Foo(PrintMeNot()); // This is an expected call. +} + +TEST_F(GMockLogTest, DoesNotPrintWarningInternallyIfVerbosityIsError) { + GMOCK_FLAG(verbose) = kErrorVerbosity; + ON_CALL(helper_, Foo(_)) + .WillByDefault(Return(PrintMeNot())); + helper_.Foo(PrintMeNot()); // This should generate a warning. +} + +// Tests Mock::AllowLeak(). + +TEST(AllowLeakTest, AllowsLeakingUnusedMockObject) { + MockA* a = new MockA; + Mock::AllowLeak(a); +} + +TEST(AllowLeakTest, CanBeCalledBeforeOnCall) { + MockA* a = new MockA; + Mock::AllowLeak(a); + ON_CALL(*a, DoA(_)).WillByDefault(Return()); + a->DoA(0); +} + +TEST(AllowLeakTest, CanBeCalledAfterOnCall) { + MockA* a = new MockA; + ON_CALL(*a, DoA(_)).WillByDefault(Return()); + Mock::AllowLeak(a); +} + +TEST(AllowLeakTest, CanBeCalledBeforeExpectCall) { + MockA* a = new MockA; + Mock::AllowLeak(a); + EXPECT_CALL(*a, DoA(_)); + a->DoA(0); +} + +TEST(AllowLeakTest, CanBeCalledAfterExpectCall) { + MockA* a = new MockA; + EXPECT_CALL(*a, DoA(_)).Times(AnyNumber()); + Mock::AllowLeak(a); +} + +TEST(AllowLeakTest, WorksWhenBothOnCallAndExpectCallArePresent) { + MockA* a = new MockA; + ON_CALL(*a, DoA(_)).WillByDefault(Return()); + EXPECT_CALL(*a, DoA(_)).Times(AnyNumber()); + Mock::AllowLeak(a); +} + +// Tests that we can verify and clear a mock object's expectations +// when none of its methods has expectations. +TEST(VerifyAndClearExpectationsTest, NoMethodHasExpectations) { + MockB b; + ASSERT_TRUE(Mock::VerifyAndClearExpectations(&b)); + + // There should be no expectations on the methods now, so we can + // freely call them. + EXPECT_EQ(0, b.DoB()); + EXPECT_EQ(0, b.DoB(1)); +} + +// Tests that we can verify and clear a mock object's expectations +// when some, but not all, of its methods have expectations *and* the +// verification succeeds. +TEST(VerifyAndClearExpectationsTest, SomeMethodsHaveExpectationsAndSucceed) { + MockB b; + EXPECT_CALL(b, DoB()) + .WillOnce(Return(1)); + b.DoB(); + ASSERT_TRUE(Mock::VerifyAndClearExpectations(&b)); + + // There should be no expectations on the methods now, so we can + // freely call them. + EXPECT_EQ(0, b.DoB()); + EXPECT_EQ(0, b.DoB(1)); +} + +// Tests that we can verify and clear a mock object's expectations +// when some, but not all, of its methods have expectations *and* the +// verification fails. +TEST(VerifyAndClearExpectationsTest, SomeMethodsHaveExpectationsAndFail) { + MockB b; + EXPECT_CALL(b, DoB()) + .WillOnce(Return(1)); + bool result = true; + EXPECT_NONFATAL_FAILURE(result = Mock::VerifyAndClearExpectations(&b), + "Actual: never called"); + ASSERT_FALSE(result); + + // There should be no expectations on the methods now, so we can + // freely call them. + EXPECT_EQ(0, b.DoB()); + EXPECT_EQ(0, b.DoB(1)); +} + +// Tests that we can verify and clear a mock object's expectations +// when all of its methods have expectations. +TEST(VerifyAndClearExpectationsTest, AllMethodsHaveExpectations) { + MockB b; + EXPECT_CALL(b, DoB()) + .WillOnce(Return(1)); + EXPECT_CALL(b, DoB(_)) + .WillOnce(Return(2)); + b.DoB(); + b.DoB(1); + ASSERT_TRUE(Mock::VerifyAndClearExpectations(&b)); + + // There should be no expectations on the methods now, so we can + // freely call them. + EXPECT_EQ(0, b.DoB()); + EXPECT_EQ(0, b.DoB(1)); +} + +// Tests that we can verify and clear a mock object's expectations +// when a method has more than one expectation. +TEST(VerifyAndClearExpectationsTest, AMethodHasManyExpectations) { + MockB b; + EXPECT_CALL(b, DoB(0)) + .WillOnce(Return(1)); + EXPECT_CALL(b, DoB(_)) + .WillOnce(Return(2)); + b.DoB(1); + bool result = true; + EXPECT_NONFATAL_FAILURE(result = Mock::VerifyAndClearExpectations(&b), + "Actual: never called"); + ASSERT_FALSE(result); + + // There should be no expectations on the methods now, so we can + // freely call them. + EXPECT_EQ(0, b.DoB()); + EXPECT_EQ(0, b.DoB(1)); +} + +// Tests that we can call VerifyAndClearExpectations() on the same +// mock object multiple times. +TEST(VerifyAndClearExpectationsTest, CanCallManyTimes) { + MockB b; + EXPECT_CALL(b, DoB()); + b.DoB(); + Mock::VerifyAndClearExpectations(&b); + + EXPECT_CALL(b, DoB(_)) + .WillOnce(Return(1)); + b.DoB(1); + Mock::VerifyAndClearExpectations(&b); + Mock::VerifyAndClearExpectations(&b); + + // There should be no expectations on the methods now, so we can + // freely call them. + EXPECT_EQ(0, b.DoB()); + EXPECT_EQ(0, b.DoB(1)); +} + +// Tests that we can clear a mock object's default actions when none +// of its methods has default actions. +TEST(VerifyAndClearTest, NoMethodHasDefaultActions) { + MockB b; + // If this crashes or generates a failure, the test will catch it. + Mock::VerifyAndClear(&b); + EXPECT_EQ(0, b.DoB()); +} + +// Tests that we can clear a mock object's default actions when some, +// but not all of its methods have default actions. +TEST(VerifyAndClearTest, SomeMethodsHaveDefaultActions) { + MockB b; + ON_CALL(b, DoB()) + .WillByDefault(Return(1)); + + Mock::VerifyAndClear(&b); + + // Verifies that the default action of int DoB() was removed. + EXPECT_EQ(0, b.DoB()); +} + +// Tests that we can clear a mock object's default actions when all of +// its methods have default actions. +TEST(VerifyAndClearTest, AllMethodsHaveDefaultActions) { + MockB b; + ON_CALL(b, DoB()) + .WillByDefault(Return(1)); + ON_CALL(b, DoB(_)) + .WillByDefault(Return(2)); + + Mock::VerifyAndClear(&b); + + // Verifies that the default action of int DoB() was removed. + EXPECT_EQ(0, b.DoB()); + + // Verifies that the default action of int DoB(int) was removed. + EXPECT_EQ(0, b.DoB(0)); +} + +// Tests that we can clear a mock object's default actions when a +// method has more than one ON_CALL() set on it. +TEST(VerifyAndClearTest, AMethodHasManyDefaultActions) { + MockB b; + ON_CALL(b, DoB(0)) + .WillByDefault(Return(1)); + ON_CALL(b, DoB(_)) + .WillByDefault(Return(2)); + + Mock::VerifyAndClear(&b); + + // Verifies that the default actions (there are two) of int DoB(int) + // were removed. + EXPECT_EQ(0, b.DoB(0)); + EXPECT_EQ(0, b.DoB(1)); +} + +// Tests that we can call VerifyAndClear() on a mock object multiple +// times. +TEST(VerifyAndClearTest, CanCallManyTimes) { + MockB b; + ON_CALL(b, DoB()) + .WillByDefault(Return(1)); + Mock::VerifyAndClear(&b); + Mock::VerifyAndClear(&b); + + ON_CALL(b, DoB(_)) + .WillByDefault(Return(1)); + Mock::VerifyAndClear(&b); + + EXPECT_EQ(0, b.DoB()); + EXPECT_EQ(0, b.DoB(1)); +} + +// Tests that VerifyAndClear() works when the verification succeeds. +TEST(VerifyAndClearTest, Success) { + MockB b; + ON_CALL(b, DoB()) + .WillByDefault(Return(1)); + EXPECT_CALL(b, DoB(1)) + .WillOnce(Return(2)); + + b.DoB(); + b.DoB(1); + ASSERT_TRUE(Mock::VerifyAndClear(&b)); + + // There should be no expectations on the methods now, so we can + // freely call them. + EXPECT_EQ(0, b.DoB()); + EXPECT_EQ(0, b.DoB(1)); +} + +// Tests that VerifyAndClear() works when the verification fails. +TEST(VerifyAndClearTest, Failure) { + MockB b; + ON_CALL(b, DoB(_)) + .WillByDefault(Return(1)); + EXPECT_CALL(b, DoB()) + .WillOnce(Return(2)); + + b.DoB(1); + bool result = true; + EXPECT_NONFATAL_FAILURE(result = Mock::VerifyAndClear(&b), + "Actual: never called"); + ASSERT_FALSE(result); + + // There should be no expectations on the methods now, so we can + // freely call them. + EXPECT_EQ(0, b.DoB()); + EXPECT_EQ(0, b.DoB(1)); +} + +// Tests that VerifyAndClear() works when the default actions and +// expectations are set on a const mock object. +TEST(VerifyAndClearTest, Const) { + MockB b; + ON_CALL(Const(b), DoB()) + .WillByDefault(Return(1)); + + EXPECT_CALL(Const(b), DoB()) + .WillOnce(DoDefault()) + .WillOnce(Return(2)); + + b.DoB(); + b.DoB(); + ASSERT_TRUE(Mock::VerifyAndClear(&b)); + + // There should be no expectations on the methods now, so we can + // freely call them. + EXPECT_EQ(0, b.DoB()); + EXPECT_EQ(0, b.DoB(1)); +} + +// Tests that we can set default actions and expectations on a mock +// object after VerifyAndClear() has been called on it. +TEST(VerifyAndClearTest, CanSetDefaultActionsAndExpectationsAfterwards) { + MockB b; + ON_CALL(b, DoB()) + .WillByDefault(Return(1)); + EXPECT_CALL(b, DoB(_)) + .WillOnce(Return(2)); + b.DoB(1); + + Mock::VerifyAndClear(&b); + + EXPECT_CALL(b, DoB()) + .WillOnce(Return(3)); + ON_CALL(b, DoB(_)) + .WillByDefault(Return(4)); + + EXPECT_EQ(3, b.DoB()); + EXPECT_EQ(4, b.DoB(1)); +} + +// Tests that calling VerifyAndClear() on one mock object does not +// affect other mock objects (either of the same type or not). +TEST(VerifyAndClearTest, DoesNotAffectOtherMockObjects) { + MockA a; + MockB b1; + MockB b2; + + ON_CALL(a, Binary(_, _)) + .WillByDefault(Return(true)); + EXPECT_CALL(a, Binary(_, _)) + .WillOnce(DoDefault()) + .WillOnce(Return(false)); + + ON_CALL(b1, DoB()) + .WillByDefault(Return(1)); + EXPECT_CALL(b1, DoB(_)) + .WillOnce(Return(2)); + + ON_CALL(b2, DoB()) + .WillByDefault(Return(3)); + EXPECT_CALL(b2, DoB(_)); + + b2.DoB(0); + Mock::VerifyAndClear(&b2); + + // Verifies that the default actions and expectations of a and b1 + // are still in effect. + EXPECT_TRUE(a.Binary(0, 0)); + EXPECT_FALSE(a.Binary(0, 0)); + + EXPECT_EQ(1, b1.DoB()); + EXPECT_EQ(2, b1.DoB(0)); +} + +// Tests that a mock function's action can call a mock function +// (either the same function or a different one) either as an explicit +// action or as a default action without causing a dead lock. It +// verifies that the action is not performed inside the critical +// section. +TEST(SynchronizationTest, CanCallMockMethodInAction) { + MockA a; + MockC c; + ON_CALL(a, DoA(_)) + .WillByDefault(IgnoreResult(InvokeWithoutArgs(&c, + &MockC::NonVoidMethod))); + EXPECT_CALL(a, DoA(1)); + EXPECT_CALL(a, DoA(1)) + .WillOnce(Invoke(&a, &MockA::DoA)) + .RetiresOnSaturation(); + EXPECT_CALL(c, NonVoidMethod()); + + a.DoA(1); + // This will match the second EXPECT_CALL() and trigger another a.DoA(1), + // which will in turn match the first EXPECT_CALL() and trigger a call to + // c.NonVoidMethod() that was specified by the ON_CALL() since the first + // EXPECT_CALL() did not specify an action. +} + +} // namespace + +// Allows the user to define his own main and then invoke gmock_main +// from it. This might be necessary on some platforms which require +// specific setup and teardown. +#if GMOCK_RENAME_MAIN +int gmock_main(int argc, char **argv) { +#else +int main(int argc, char **argv) { +#endif // GMOCK_RENAME_MAIN + testing::InitGoogleMock(&argc, argv); + + // Ensures that the tests pass no matter what value of + // --gmock_catch_leaked_mocks and --gmock_verbose the user specifies. + testing::GMOCK_FLAG(catch_leaked_mocks) = true; + testing::GMOCK_FLAG(verbose) = testing::internal::kWarningVerbosity; + + return RUN_ALL_TESTS(); +} diff --git a/libwvdrmengine/test/gmock/test/gmock_all_test.cc b/libwvdrmengine/test/gmock/test/gmock_all_test.cc new file mode 100644 index 00000000..691aac84 --- /dev/null +++ b/libwvdrmengine/test/gmock/test/gmock_all_test.cc @@ -0,0 +1,48 @@ +// Copyright 2009, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) +// +// Tests for Google C++ Mocking Framework (Google Mock) +// +// Sometimes it's desirable to build most of Google Mock's own tests +// by compiling a single file. This file serves this purpose. +#include "test/gmock-actions_test.cc" +#include "test/gmock-cardinalities_test.cc" +#include "test/gmock-generated-actions_test.cc" +#include "test/gmock-generated-function-mockers_test.cc" +#include "test/gmock-generated-internal-utils_test.cc" +#include "test/gmock-generated-matchers_test.cc" +#include "test/gmock-internal-utils_test.cc" +#include "test/gmock-matchers_test.cc" +#include "test/gmock-more-actions_test.cc" +#include "test/gmock-nice-strict_test.cc" +#include "test/gmock-port_test.cc" +#include "test/gmock-spec-builders_test.cc" +#include "test/gmock_test.cc" diff --git a/libwvdrmengine/test/gmock/test/gmock_leak_test.py b/libwvdrmengine/test/gmock/test/gmock_leak_test.py new file mode 100755 index 00000000..38ff9d01 --- /dev/null +++ b/libwvdrmengine/test/gmock/test/gmock_leak_test.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python +# +# Copyright 2009, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Tests that leaked mock objects can be caught be Google Mock.""" + +__author__ = 'wan@google.com (Zhanyong Wan)' + + +import gmock_test_utils + + +PROGRAM_PATH = gmock_test_utils.GetTestExecutablePath('gmock_leak_test_') +TEST_WITH_EXPECT_CALL = [PROGRAM_PATH, '--gtest_filter=*ExpectCall*'] +TEST_WITH_ON_CALL = [PROGRAM_PATH, '--gtest_filter=*OnCall*'] +TEST_MULTIPLE_LEAKS = [PROGRAM_PATH, '--gtest_filter=*MultipleLeaked*'] + + +class GMockLeakTest(gmock_test_utils.TestCase): + + def testCatchesLeakedMockByDefault(self): + self.assertNotEqual( + 0, + gmock_test_utils.Subprocess(TEST_WITH_EXPECT_CALL).exit_code) + self.assertNotEqual( + 0, + gmock_test_utils.Subprocess(TEST_WITH_ON_CALL).exit_code) + + def testDoesNotCatchLeakedMockWhenDisabled(self): + self.assertEquals( + 0, + gmock_test_utils.Subprocess(TEST_WITH_EXPECT_CALL + + ['--gmock_catch_leaked_mocks=0']).exit_code) + self.assertEquals( + 0, + gmock_test_utils.Subprocess(TEST_WITH_ON_CALL + + ['--gmock_catch_leaked_mocks=0']).exit_code) + + def testCatchesLeakedMockWhenEnabled(self): + self.assertNotEqual( + 0, + gmock_test_utils.Subprocess(TEST_WITH_EXPECT_CALL + + ['--gmock_catch_leaked_mocks']).exit_code) + self.assertNotEqual( + 0, + gmock_test_utils.Subprocess(TEST_WITH_ON_CALL + + ['--gmock_catch_leaked_mocks']).exit_code) + + def testCatchesLeakedMockWhenEnabledWithExplictFlagValue(self): + self.assertNotEqual( + 0, + gmock_test_utils.Subprocess(TEST_WITH_EXPECT_CALL + + ['--gmock_catch_leaked_mocks=1']).exit_code) + + def testCatchesMultipleLeakedMocks(self): + self.assertNotEqual( + 0, + gmock_test_utils.Subprocess(TEST_MULTIPLE_LEAKS + + ['--gmock_catch_leaked_mocks']).exit_code) + + +if __name__ == '__main__': + gmock_test_utils.Main() diff --git a/libwvdrmengine/test/gmock/test/gmock_leak_test_.cc b/libwvdrmengine/test/gmock/test/gmock_leak_test_.cc new file mode 100644 index 00000000..1d27d22f --- /dev/null +++ b/libwvdrmengine/test/gmock/test/gmock_leak_test_.cc @@ -0,0 +1,100 @@ +// Copyright 2009, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This program is for verifying that a leaked mock object can be +// caught by Google Mock's leak detector. + +#include "gmock/gmock.h" + +namespace { + +using ::testing::Return; + +class FooInterface { + public: + virtual ~FooInterface() {} + virtual void DoThis() = 0; +}; + +class MockFoo : public FooInterface { + public: + MockFoo() {} + + MOCK_METHOD0(DoThis, void()); + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFoo); +}; + +TEST(LeakTest, LeakedMockWithExpectCallCausesFailureWhenLeakCheckingIsEnabled) { + MockFoo* foo = new MockFoo; + + EXPECT_CALL(*foo, DoThis()); + foo->DoThis(); + + // In order to test the leak detector, we deliberately leak foo. + + // Makes sure Google Mock's leak detector can change the exit code + // to 1 even when the code is already exiting with 0. + exit(0); +} + +TEST(LeakTest, LeakedMockWithOnCallCausesFailureWhenLeakCheckingIsEnabled) { + MockFoo* foo = new MockFoo; + + ON_CALL(*foo, DoThis()).WillByDefault(Return()); + + // In order to test the leak detector, we deliberately leak foo. + + // Makes sure Google Mock's leak detector can change the exit code + // to 1 even when the code is already exiting with 0. + exit(0); +} + +TEST(LeakTest, CatchesMultipleLeakedMockObjects) { + MockFoo* foo1 = new MockFoo; + MockFoo* foo2 = new MockFoo; + + ON_CALL(*foo1, DoThis()).WillByDefault(Return()); + EXPECT_CALL(*foo2, DoThis()); + foo2->DoThis(); + + // In order to test the leak detector, we deliberately leak foo1 and + // foo2. + + // Makes sure Google Mock's leak detector can change the exit code + // to 1 even when the code is already exiting with 0. + exit(0); +} + +} // namespace diff --git a/libwvdrmengine/test/gmock/test/gmock_link2_test.cc b/libwvdrmengine/test/gmock/test/gmock_link2_test.cc new file mode 100644 index 00000000..4c310c3d --- /dev/null +++ b/libwvdrmengine/test/gmock/test/gmock_link2_test.cc @@ -0,0 +1,40 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan), vladl@google.com (Vlad Losev) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file is for verifying that various Google Mock constructs do not +// produce linker errors when instantiated in different translation units. +// Please see gmock_link_test.h for details. + +#define LinkTest LinkTest2 + +#include "test/gmock_link_test.h" diff --git a/libwvdrmengine/test/gmock/test/gmock_link_test.cc b/libwvdrmengine/test/gmock/test/gmock_link_test.cc new file mode 100644 index 00000000..61e97d10 --- /dev/null +++ b/libwvdrmengine/test/gmock/test/gmock_link_test.cc @@ -0,0 +1,40 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan), vladl@google.com (Vlad Losev) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file is for verifying that various Google Mock constructs do not +// produce linker errors when instantiated in different translation units. +// Please see gmock_link_test.h for details. + +#define LinkTest LinkTest1 + +#include "test/gmock_link_test.h" diff --git a/libwvdrmengine/test/gmock/test/gmock_link_test.h b/libwvdrmengine/test/gmock/test/gmock_link_test.h new file mode 100644 index 00000000..ab5af4b4 --- /dev/null +++ b/libwvdrmengine/test/gmock/test/gmock_link_test.h @@ -0,0 +1,669 @@ +// Copyright 2009, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: vladl@google.com (Vlad Losev) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file tests that: +// a. A header file defining a mock class can be included in multiple +// translation units without causing a link error. +// b. Actions and matchers can be instantiated with identical template +// arguments in different translation units without causing link +// errors. +// The following constructs are currently tested: +// Actions: +// Return() +// Return(value) +// ReturnNull +// ReturnRef +// Assign +// SetArgPointee +// SetArrayArgument +// SetErrnoAndReturn +// Invoke(function) +// Invoke(object, method) +// InvokeWithoutArgs(function) +// InvokeWithoutArgs(object, method) +// InvokeArgument +// WithArg +// WithArgs +// WithoutArgs +// DoAll +// DoDefault +// IgnoreResult +// Throw +// ACTION()-generated +// ACTION_P()-generated +// ACTION_P2()-generated +// Matchers: +// _ +// A +// An +// Eq +// Gt, Lt, Ge, Le, Ne +// NotNull +// Ref +// TypedEq +// DoubleEq +// FloatEq +// NanSensitiveDoubleEq +// NanSensitiveFloatEq +// ContainsRegex +// MatchesRegex +// EndsWith +// HasSubstr +// StartsWith +// StrCaseEq +// StrCaseNe +// StrEq +// StrNe +// ElementsAre +// ElementsAreArray +// ContainerEq +// Field +// Property +// ResultOf(function) +// Pointee +// Truly(predicate) +// AllOf +// AnyOf +// Not +// MatcherCast +// +// Please note: this test does not verify the functioning of these +// constructs, only that the programs using them will link successfully. +// +// Implementation note: +// This test requires identical definitions of Interface and Mock to be +// included in different translation units. We achieve this by writing +// them in this header and #including it in gmock_link_test.cc and +// gmock_link2_test.cc. Because the symbols generated by the compiler for +// those constructs must be identical in both translation units, +// definitions of Interface and Mock tests MUST be kept in the SAME +// NON-ANONYMOUS namespace in this file. The test fixture class LinkTest +// is defined as LinkTest1 in gmock_link_test.cc and as LinkTest2 in +// gmock_link2_test.cc to avoid producing linker errors. + +#ifndef GMOCK_TEST_GMOCK_LINK_TEST_H_ +#define GMOCK_TEST_GMOCK_LINK_TEST_H_ + +#include "gmock/gmock.h" + +#if !GTEST_OS_WINDOWS_MOBILE +# include +#endif + +#include "gmock/internal/gmock-port.h" +#include "gtest/gtest.h" +#include +#include + +using testing::_; +using testing::A; +using testing::AllOf; +using testing::AnyOf; +using testing::Assign; +using testing::ContainerEq; +using testing::DoAll; +using testing::DoDefault; +using testing::DoubleEq; +using testing::ElementsAre; +using testing::ElementsAreArray; +using testing::EndsWith; +using testing::Eq; +using testing::Field; +using testing::FloatEq; +using testing::Ge; +using testing::Gt; +using testing::HasSubstr; +using testing::IgnoreResult; +using testing::Invoke; +using testing::InvokeArgument; +using testing::InvokeWithoutArgs; +using testing::IsNull; +using testing::Le; +using testing::Lt; +using testing::Matcher; +using testing::MatcherCast; +using testing::NanSensitiveDoubleEq; +using testing::NanSensitiveFloatEq; +using testing::Ne; +using testing::Not; +using testing::NotNull; +using testing::Pointee; +using testing::Property; +using testing::Ref; +using testing::ResultOf; +using testing::Return; +using testing::ReturnNull; +using testing::ReturnRef; +using testing::SetArgPointee; +using testing::SetArrayArgument; +using testing::StartsWith; +using testing::StrCaseEq; +using testing::StrCaseNe; +using testing::StrEq; +using testing::StrNe; +using testing::Truly; +using testing::TypedEq; +using testing::WithArg; +using testing::WithArgs; +using testing::WithoutArgs; + +#if !GTEST_OS_WINDOWS_MOBILE +using testing::SetErrnoAndReturn; +#endif + +#if GTEST_HAS_EXCEPTIONS +using testing::Throw; +#endif + +using testing::ContainsRegex; +using testing::MatchesRegex; + +class Interface { + public: + virtual ~Interface() {} + virtual void VoidFromString(char* str) = 0; + virtual char* StringFromString(char* str) = 0; + virtual int IntFromString(char* str) = 0; + virtual int& IntRefFromString(char* str) = 0; + virtual void VoidFromFunc(void(*)(char*)) = 0; + virtual void VoidFromIntRef(int& n) = 0; + virtual void VoidFromFloat(float n) = 0; + virtual void VoidFromDouble(double n) = 0; + virtual void VoidFromVector(const std::vector& v) = 0; +}; + +class Mock: public Interface { + public: + Mock() {} + + MOCK_METHOD1(VoidFromString, void(char* str)); + MOCK_METHOD1(StringFromString, char*(char* str)); + MOCK_METHOD1(IntFromString, int(char* str)); + MOCK_METHOD1(IntRefFromString, int&(char* str)); + MOCK_METHOD1(VoidFromFunc, void(void(*func)(char* str))); + MOCK_METHOD1(VoidFromIntRef, void(int& n)); + MOCK_METHOD1(VoidFromFloat, void(float n)); + MOCK_METHOD1(VoidFromDouble, void(double n)); + MOCK_METHOD1(VoidFromVector, void(const std::vector& v)); + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(Mock); +}; + +class InvokeHelper { + public: + static void StaticVoidFromVoid() {} + void VoidFromVoid() {} + static void StaticVoidFromString(char*) {} + void VoidFromString(char*) {} + static int StaticIntFromString(char*) { return 1; } + static bool StaticBoolFromString(const char*) { return true; } +}; + +class FieldHelper { + public: + FieldHelper(int a_field) : field_(a_field) {} + int field() const { return field_; } + int field_; // NOLINT -- need external access to field_ to test + // the Field matcher. +}; + +// Tests the linkage of the ReturnVoid action. +TEST(LinkTest, TestReturnVoid) { + Mock mock; + + EXPECT_CALL(mock, VoidFromString(_)).WillOnce(Return()); + mock.VoidFromString(NULL); +} + +// Tests the linkage of the Return action. +TEST(LinkTest, TestReturn) { + Mock mock; + char ch = 'x'; + + EXPECT_CALL(mock, StringFromString(_)).WillOnce(Return(&ch)); + mock.StringFromString(NULL); +} + +// Tests the linkage of the ReturnNull action. +TEST(LinkTest, TestReturnNull) { + Mock mock; + + EXPECT_CALL(mock, VoidFromString(_)).WillOnce(Return()); + mock.VoidFromString(NULL); +} + +// Tests the linkage of the ReturnRef action. +TEST(LinkTest, TestReturnRef) { + Mock mock; + int n = 42; + + EXPECT_CALL(mock, IntRefFromString(_)).WillOnce(ReturnRef(n)); + mock.IntRefFromString(NULL); +} + +// Tests the linkage of the Assign action. +TEST(LinkTest, TestAssign) { + Mock mock; + char ch = 'x'; + + EXPECT_CALL(mock, VoidFromString(_)).WillOnce(Assign(&ch, 'y')); + mock.VoidFromString(NULL); +} + +// Tests the linkage of the SetArgPointee action. +TEST(LinkTest, TestSetArgPointee) { + Mock mock; + char ch = 'x'; + + EXPECT_CALL(mock, VoidFromString(_)).WillOnce(SetArgPointee<0>('y')); + mock.VoidFromString(&ch); +} + +// Tests the linkage of the SetArrayArgument action. +TEST(LinkTest, TestSetArrayArgument) { + Mock mock; + char ch = 'x'; + char ch2 = 'y'; + + EXPECT_CALL(mock, VoidFromString(_)).WillOnce(SetArrayArgument<0>(&ch2, + &ch2 + 1)); + mock.VoidFromString(&ch); +} + +#if !GTEST_OS_WINDOWS_MOBILE + +// Tests the linkage of the SetErrnoAndReturn action. +TEST(LinkTest, TestSetErrnoAndReturn) { + Mock mock; + + int saved_errno = errno; + EXPECT_CALL(mock, IntFromString(_)).WillOnce(SetErrnoAndReturn(1, -1)); + mock.IntFromString(NULL); + errno = saved_errno; +} + +#endif // !GTEST_OS_WINDOWS_MOBILE + +// Tests the linkage of the Invoke(function) and Invoke(object, method) actions. +TEST(LinkTest, TestInvoke) { + Mock mock; + InvokeHelper test_invoke_helper; + + EXPECT_CALL(mock, VoidFromString(_)) + .WillOnce(Invoke(&InvokeHelper::StaticVoidFromString)) + .WillOnce(Invoke(&test_invoke_helper, &InvokeHelper::VoidFromString)); + mock.VoidFromString(NULL); + mock.VoidFromString(NULL); +} + +// Tests the linkage of the InvokeWithoutArgs action. +TEST(LinkTest, TestInvokeWithoutArgs) { + Mock mock; + InvokeHelper test_invoke_helper; + + EXPECT_CALL(mock, VoidFromString(_)) + .WillOnce(InvokeWithoutArgs(&InvokeHelper::StaticVoidFromVoid)) + .WillOnce(InvokeWithoutArgs(&test_invoke_helper, + &InvokeHelper::VoidFromVoid)); + mock.VoidFromString(NULL); + mock.VoidFromString(NULL); +} + +// Tests the linkage of the InvokeArgument action. +TEST(LinkTest, TestInvokeArgument) { + Mock mock; + char ch = 'x'; + + EXPECT_CALL(mock, VoidFromFunc(_)).WillOnce(InvokeArgument<0>(&ch)); + mock.VoidFromFunc(InvokeHelper::StaticVoidFromString); +} + +// Tests the linkage of the WithArg action. +TEST(LinkTest, TestWithArg) { + Mock mock; + + EXPECT_CALL(mock, VoidFromString(_)) + .WillOnce(WithArg<0>(Invoke(&InvokeHelper::StaticVoidFromString))); + mock.VoidFromString(NULL); +} + +// Tests the linkage of the WithArgs action. +TEST(LinkTest, TestWithArgs) { + Mock mock; + + EXPECT_CALL(mock, VoidFromString(_)) + .WillOnce(WithArgs<0>(Invoke(&InvokeHelper::StaticVoidFromString))); + mock.VoidFromString(NULL); +} + +// Tests the linkage of the WithoutArgs action. +TEST(LinkTest, TestWithoutArgs) { + Mock mock; + + EXPECT_CALL(mock, VoidFromString(_)).WillOnce(WithoutArgs(Return())); + mock.VoidFromString(NULL); +} + +// Tests the linkage of the DoAll action. +TEST(LinkTest, TestDoAll) { + Mock mock; + char ch = 'x'; + + EXPECT_CALL(mock, VoidFromString(_)) + .WillOnce(DoAll(SetArgPointee<0>('y'), Return())); + mock.VoidFromString(&ch); +} + +// Tests the linkage of the DoDefault action. +TEST(LinkTest, TestDoDefault) { + Mock mock; + char ch = 'x'; + + ON_CALL(mock, VoidFromString(_)).WillByDefault(Return()); + EXPECT_CALL(mock, VoidFromString(_)).WillOnce(DoDefault()); + mock.VoidFromString(&ch); +} + +// Tests the linkage of the IgnoreResult action. +TEST(LinkTest, TestIgnoreResult) { + Mock mock; + + EXPECT_CALL(mock, VoidFromString(_)).WillOnce(IgnoreResult(Return(42))); + mock.VoidFromString(NULL); +} + +#if GTEST_HAS_EXCEPTIONS +// Tests the linkage of the Throw action. +TEST(LinkTest, TestThrow) { + Mock mock; + + EXPECT_CALL(mock, VoidFromString(_)).WillOnce(Throw(42)); + EXPECT_THROW(mock.VoidFromString(NULL), int); +} +#endif // GTEST_HAS_EXCEPTIONS + +// The ACTION*() macros trigger warning C4100 (unreferenced formal +// parameter) in MSVC with -W4. Unfortunately they cannot be fixed in +// the macro definition, as the warnings are generated when the macro +// is expanded and macro expansion cannot contain #pragma. Therefore +// we suppress them here. +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable:4100) +#endif + +// Tests the linkage of actions created using ACTION macro. +namespace { +ACTION(Return1) { return 1; } +} + +TEST(LinkTest, TestActionMacro) { + Mock mock; + + EXPECT_CALL(mock, IntFromString(_)).WillOnce(Return1()); + mock.IntFromString(NULL); +} + +// Tests the linkage of actions created using ACTION_P macro. +namespace { +ACTION_P(ReturnArgument, ret_value) { return ret_value; } +} + +TEST(LinkTest, TestActionPMacro) { + Mock mock; + + EXPECT_CALL(mock, IntFromString(_)).WillOnce(ReturnArgument(42)); + mock.IntFromString(NULL); +} + +// Tests the linkage of actions created using ACTION_P2 macro. +namespace { +ACTION_P2(ReturnEqualsEitherOf, first, second) { + return arg0 == first || arg0 == second; +} +} + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + +TEST(LinkTest, TestActionP2Macro) { + Mock mock; + char ch = 'x'; + + EXPECT_CALL(mock, IntFromString(_)) + .WillOnce(ReturnEqualsEitherOf("one", "two")); + mock.IntFromString(&ch); +} + +// Tests the linkage of the "_" matcher. +TEST(LinkTest, TestMatcherAnything) { + Mock mock; + + ON_CALL(mock, VoidFromString(_)).WillByDefault(Return()); +} + +// Tests the linkage of the A matcher. +TEST(LinkTest, TestMatcherA) { + Mock mock; + + ON_CALL(mock, VoidFromString(A())).WillByDefault(Return()); +} + +// Tests the linkage of the Eq and the "bare value" matcher. +TEST(LinkTest, TestMatchersEq) { + Mock mock; + const char* p = "x"; + + ON_CALL(mock, VoidFromString(Eq(p))).WillByDefault(Return()); + ON_CALL(mock, VoidFromString(const_cast("y"))) + .WillByDefault(Return()); +} + +// Tests the linkage of the Lt, Gt, Le, Ge, and Ne matchers. +TEST(LinkTest, TestMatchersRelations) { + Mock mock; + + ON_CALL(mock, VoidFromFloat(Lt(1.0f))).WillByDefault(Return()); + ON_CALL(mock, VoidFromFloat(Gt(1.0f))).WillByDefault(Return()); + ON_CALL(mock, VoidFromFloat(Le(1.0f))).WillByDefault(Return()); + ON_CALL(mock, VoidFromFloat(Ge(1.0f))).WillByDefault(Return()); + ON_CALL(mock, VoidFromFloat(Ne(1.0f))).WillByDefault(Return()); +} + +// Tests the linkage of the NotNull matcher. +TEST(LinkTest, TestMatcherNotNull) { + Mock mock; + + ON_CALL(mock, VoidFromString(NotNull())).WillByDefault(Return()); +} + +// Tests the linkage of the IsNull matcher. +TEST(LinkTest, TestMatcherIsNull) { + Mock mock; + + ON_CALL(mock, VoidFromString(IsNull())).WillByDefault(Return()); +} + +// Tests the linkage of the Ref matcher. +TEST(LinkTest, TestMatcherRef) { + Mock mock; + int a = 0; + + ON_CALL(mock, VoidFromIntRef(Ref(a))).WillByDefault(Return()); +} + +// Tests the linkage of the TypedEq matcher. +TEST(LinkTest, TestMatcherTypedEq) { + Mock mock; + long a = 0; + + ON_CALL(mock, VoidFromIntRef(TypedEq(a))).WillByDefault(Return()); +} + +// Tests the linkage of the FloatEq, DoubleEq, NanSensitiveFloatEq and +// NanSensitiveDoubleEq matchers. +TEST(LinkTest, TestMatchersFloatingPoint) { + Mock mock; + float a = 0; + + ON_CALL(mock, VoidFromFloat(FloatEq(a))).WillByDefault(Return()); + ON_CALL(mock, VoidFromDouble(DoubleEq(a))).WillByDefault(Return()); + ON_CALL(mock, VoidFromFloat(NanSensitiveFloatEq(a))).WillByDefault(Return()); + ON_CALL(mock, VoidFromDouble(NanSensitiveDoubleEq(a))) + .WillByDefault(Return()); +} + +// Tests the linkage of the ContainsRegex matcher. +TEST(LinkTest, TestMatcherContainsRegex) { + Mock mock; + + ON_CALL(mock, VoidFromString(ContainsRegex(".*"))).WillByDefault(Return()); +} + +// Tests the linkage of the MatchesRegex matcher. +TEST(LinkTest, TestMatcherMatchesRegex) { + Mock mock; + + ON_CALL(mock, VoidFromString(MatchesRegex(".*"))).WillByDefault(Return()); +} + +// Tests the linkage of the StartsWith, EndsWith, and HasSubstr matchers. +TEST(LinkTest, TestMatchersSubstrings) { + Mock mock; + + ON_CALL(mock, VoidFromString(StartsWith("a"))).WillByDefault(Return()); + ON_CALL(mock, VoidFromString(EndsWith("c"))).WillByDefault(Return()); + ON_CALL(mock, VoidFromString(HasSubstr("b"))).WillByDefault(Return()); +} + +// Tests the linkage of the StrEq, StrNe, StrCaseEq, and StrCaseNe matchers. +TEST(LinkTest, TestMatchersStringEquality) { + Mock mock; + ON_CALL(mock, VoidFromString(StrEq("a"))).WillByDefault(Return()); + ON_CALL(mock, VoidFromString(StrNe("a"))).WillByDefault(Return()); + ON_CALL(mock, VoidFromString(StrCaseEq("a"))).WillByDefault(Return()); + ON_CALL(mock, VoidFromString(StrCaseNe("a"))).WillByDefault(Return()); +} + +// Tests the linkage of the ElementsAre matcher. +TEST(LinkTest, TestMatcherElementsAre) { + Mock mock; + + ON_CALL(mock, VoidFromVector(ElementsAre('a', _))).WillByDefault(Return()); +} + +// Tests the linkage of the ElementsAreArray matcher. +TEST(LinkTest, TestMatcherElementsAreArray) { + Mock mock; + char arr[] = { 'a', 'b' }; + + ON_CALL(mock, VoidFromVector(ElementsAreArray(arr))).WillByDefault(Return()); +} + +// Tests the linkage of the ContainerEq matcher. +TEST(LinkTest, TestMatcherContainerEq) { + Mock mock; + std::vector v; + + ON_CALL(mock, VoidFromVector(ContainerEq(v))).WillByDefault(Return()); +} + +// Tests the linkage of the Field matcher. +TEST(LinkTest, TestMatcherField) { + FieldHelper helper(0); + + Matcher m = Field(&FieldHelper::field_, Eq(0)); + EXPECT_TRUE(m.Matches(helper)); + + Matcher m2 = Field(&FieldHelper::field_, Eq(0)); + EXPECT_TRUE(m2.Matches(&helper)); +} + +// Tests the linkage of the Property matcher. +TEST(LinkTest, TestMatcherProperty) { + FieldHelper helper(0); + + Matcher m = Property(&FieldHelper::field, Eq(0)); + EXPECT_TRUE(m.Matches(helper)); + + Matcher m2 = Property(&FieldHelper::field, Eq(0)); + EXPECT_TRUE(m2.Matches(&helper)); +} + +// Tests the linkage of the ResultOf matcher. +TEST(LinkTest, TestMatcherResultOf) { + Matcher m = ResultOf(&InvokeHelper::StaticIntFromString, Eq(1)); + EXPECT_TRUE(m.Matches(NULL)); +} + +// Tests the linkage of the ResultOf matcher. +TEST(LinkTest, TestMatcherPointee) { + int n = 1; + + Matcher m = Pointee(Eq(1)); + EXPECT_TRUE(m.Matches(&n)); +} + +// Tests the linkage of the Truly matcher. +TEST(LinkTest, TestMatcherTruly) { + Matcher m = Truly(&InvokeHelper::StaticBoolFromString); + EXPECT_TRUE(m.Matches(NULL)); +} + +// Tests the linkage of the AllOf matcher. +TEST(LinkTest, TestMatcherAllOf) { + Matcher m = AllOf(_, Eq(1)); + EXPECT_TRUE(m.Matches(1)); +} + +// Tests the linkage of the AnyOf matcher. +TEST(LinkTest, TestMatcherAnyOf) { + Matcher m = AnyOf(_, Eq(1)); + EXPECT_TRUE(m.Matches(1)); +} + +// Tests the linkage of the Not matcher. +TEST(LinkTest, TestMatcherNot) { + Matcher m = Not(_); + EXPECT_FALSE(m.Matches(1)); +} + +// Tests the linkage of the MatcherCast() function. +TEST(LinkTest, TestMatcherCast) { + Matcher m = MatcherCast(_); + EXPECT_TRUE(m.Matches(NULL)); +} + +#endif // GMOCK_TEST_GMOCK_LINK_TEST_H_ diff --git a/libwvdrmengine/test/gmock/test/gmock_output_test.py b/libwvdrmengine/test/gmock/test/gmock_output_test.py new file mode 100755 index 00000000..eced8a81 --- /dev/null +++ b/libwvdrmengine/test/gmock/test/gmock_output_test.py @@ -0,0 +1,180 @@ +#!/usr/bin/env python +# +# Copyright 2008, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Tests the text output of Google C++ Mocking Framework. + +SYNOPSIS + gmock_output_test.py --build_dir=BUILD/DIR --gengolden + # where BUILD/DIR contains the built gmock_output_test_ file. + gmock_output_test.py --gengolden + gmock_output_test.py +""" + +__author__ = 'wan@google.com (Zhanyong Wan)' + +import os +import re +import sys + +import gmock_test_utils + + +# The flag for generating the golden file +GENGOLDEN_FLAG = '--gengolden' + +PROGRAM_PATH = gmock_test_utils.GetTestExecutablePath('gmock_output_test_') +COMMAND = [PROGRAM_PATH, '--gtest_stack_trace_depth=0', '--gtest_print_time=0'] +GOLDEN_NAME = 'gmock_output_test_golden.txt' +GOLDEN_PATH = os.path.join(gmock_test_utils.GetSourceDir(), GOLDEN_NAME) + + +def ToUnixLineEnding(s): + """Changes all Windows/Mac line endings in s to UNIX line endings.""" + + return s.replace('\r\n', '\n').replace('\r', '\n') + + +def RemoveReportHeaderAndFooter(output): + """Removes Google Test result report's header and footer from the output.""" + + output = re.sub(r'.*gtest_main.*\n', '', output) + output = re.sub(r'\[.*\d+ tests.*\n', '', output) + output = re.sub(r'\[.* test environment .*\n', '', output) + output = re.sub(r'\[=+\] \d+ tests .* ran.*', '', output) + output = re.sub(r'.* FAILED TESTS\n', '', output) + return output + + +def RemoveLocations(output): + """Removes all file location info from a Google Test program's output. + + Args: + output: the output of a Google Test program. + + Returns: + output with all file location info (in the form of + 'DIRECTORY/FILE_NAME:LINE_NUMBER: 'or + 'DIRECTORY\\FILE_NAME(LINE_NUMBER): ') replaced by + 'FILE:#: '. + """ + + return re.sub(r'.*[/\\](.+)(\:\d+|\(\d+\))\:', 'FILE:#:', output) + + +def NormalizeErrorMarker(output): + """Normalizes the error marker, which is different on Windows vs on Linux.""" + + return re.sub(r' error: ', ' Failure\n', output) + + +def RemoveMemoryAddresses(output): + """Removes memory addresses from the test output.""" + + return re.sub(r'@\w+', '@0x#', output) + + +def RemoveTestNamesOfLeakedMocks(output): + """Removes the test names of leaked mock objects from the test output.""" + + return re.sub(r'\(used in test .+\) ', '', output) + + +def GetLeakyTests(output): + """Returns a list of test names that leak mock objects.""" + + # findall() returns a list of all matches of the regex in output. + # For example, if '(used in test FooTest.Bar)' is in output, the + # list will contain 'FooTest.Bar'. + return re.findall(r'\(used in test (.+)\)', output) + + +def GetNormalizedOutputAndLeakyTests(output): + """Normalizes the output of gmock_output_test_. + + Args: + output: The test output. + + Returns: + A tuple (the normalized test output, the list of test names that have + leaked mocks). + """ + + output = ToUnixLineEnding(output) + output = RemoveReportHeaderAndFooter(output) + output = NormalizeErrorMarker(output) + output = RemoveLocations(output) + output = RemoveMemoryAddresses(output) + return (RemoveTestNamesOfLeakedMocks(output), GetLeakyTests(output)) + + +def GetShellCommandOutput(cmd): + """Runs a command in a sub-process, and returns its STDOUT in a string.""" + + return gmock_test_utils.Subprocess(cmd, capture_stderr=False).output + + +def GetNormalizedCommandOutputAndLeakyTests(cmd): + """Runs a command and returns its normalized output and a list of leaky tests. + + Args: + cmd: the shell command. + """ + + # Disables exception pop-ups on Windows. + os.environ['GTEST_CATCH_EXCEPTIONS'] = '1' + return GetNormalizedOutputAndLeakyTests(GetShellCommandOutput(cmd)) + + +class GMockOutputTest(gmock_test_utils.TestCase): + def testOutput(self): + (output, leaky_tests) = GetNormalizedCommandOutputAndLeakyTests(COMMAND) + golden_file = open(GOLDEN_PATH, 'rb') + golden = golden_file.read() + golden_file.close() + + # The normalized output should match the golden file. + self.assertEquals(golden, output) + + # The raw output should contain 2 leaked mock object errors for + # test GMockOutputTest.CatchesLeakedMocks. + self.assertEquals(['GMockOutputTest.CatchesLeakedMocks', + 'GMockOutputTest.CatchesLeakedMocks'], + leaky_tests) + + +if __name__ == '__main__': + if sys.argv[1:] == [GENGOLDEN_FLAG]: + (output, _) = GetNormalizedCommandOutputAndLeakyTests(COMMAND) + golden_file = open(GOLDEN_PATH, 'wb') + golden_file.write(output) + golden_file.close() + else: + gmock_test_utils.Main() diff --git a/libwvdrmengine/test/gmock/test/gmock_output_test_.cc b/libwvdrmengine/test/gmock/test/gmock_output_test_.cc new file mode 100644 index 00000000..c8e6b831 --- /dev/null +++ b/libwvdrmengine/test/gmock/test/gmock_output_test_.cc @@ -0,0 +1,290 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Tests Google Mock's output in various scenarios. This ensures that +// Google Mock's messages are readable and useful. + +#include "gmock/gmock.h" + +#include +#include + +#include "gtest/gtest.h" + +using testing::_; +using testing::AnyNumber; +using testing::Ge; +using testing::InSequence; +using testing::Ref; +using testing::Return; +using testing::Sequence; + +class MockFoo { + public: + MockFoo() {} + + MOCK_METHOD3(Bar, char(const std::string& s, int i, double x)); + MOCK_METHOD2(Bar2, bool(int x, int y)); + MOCK_METHOD2(Bar3, void(int x, int y)); + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(MockFoo); +}; + +class GMockOutputTest : public testing::Test { + protected: + MockFoo foo_; +}; + +TEST_F(GMockOutputTest, ExpectedCall) { + testing::GMOCK_FLAG(verbose) = "info"; + + EXPECT_CALL(foo_, Bar2(0, _)); + foo_.Bar2(0, 0); // Expected call + + testing::GMOCK_FLAG(verbose) = "warning"; +} + +TEST_F(GMockOutputTest, ExpectedCallToVoidFunction) { + testing::GMOCK_FLAG(verbose) = "info"; + + EXPECT_CALL(foo_, Bar3(0, _)); + foo_.Bar3(0, 0); // Expected call + + testing::GMOCK_FLAG(verbose) = "warning"; +} + +TEST_F(GMockOutputTest, ExplicitActionsRunOut) { + EXPECT_CALL(foo_, Bar2(_, _)) + .Times(2) + .WillOnce(Return(false)); + foo_.Bar2(2, 2); + foo_.Bar2(1, 1); // Explicit actions in EXPECT_CALL run out. +} + +TEST_F(GMockOutputTest, UnexpectedCall) { + EXPECT_CALL(foo_, Bar2(0, _)); + + foo_.Bar2(1, 0); // Unexpected call + foo_.Bar2(0, 0); // Expected call +} + +TEST_F(GMockOutputTest, UnexpectedCallToVoidFunction) { + EXPECT_CALL(foo_, Bar3(0, _)); + + foo_.Bar3(1, 0); // Unexpected call + foo_.Bar3(0, 0); // Expected call +} + +TEST_F(GMockOutputTest, ExcessiveCall) { + EXPECT_CALL(foo_, Bar2(0, _)); + + foo_.Bar2(0, 0); // Expected call + foo_.Bar2(0, 1); // Excessive call +} + +TEST_F(GMockOutputTest, ExcessiveCallToVoidFunction) { + EXPECT_CALL(foo_, Bar3(0, _)); + + foo_.Bar3(0, 0); // Expected call + foo_.Bar3(0, 1); // Excessive call +} + +TEST_F(GMockOutputTest, UninterestingCall) { + foo_.Bar2(0, 1); // Uninteresting call +} + +TEST_F(GMockOutputTest, UninterestingCallToVoidFunction) { + foo_.Bar3(0, 1); // Uninteresting call +} + +TEST_F(GMockOutputTest, RetiredExpectation) { + EXPECT_CALL(foo_, Bar2(_, _)) + .RetiresOnSaturation(); + EXPECT_CALL(foo_, Bar2(0, 0)); + + foo_.Bar2(1, 1); + foo_.Bar2(1, 1); // Matches a retired expectation + foo_.Bar2(0, 0); +} + +TEST_F(GMockOutputTest, UnsatisfiedPrerequisite) { + { + InSequence s; + EXPECT_CALL(foo_, Bar(_, 0, _)); + EXPECT_CALL(foo_, Bar2(0, 0)); + EXPECT_CALL(foo_, Bar2(1, _)); + } + + foo_.Bar2(1, 0); // Has one immediate unsatisfied pre-requisite + foo_.Bar("Hi", 0, 0); + foo_.Bar2(0, 0); + foo_.Bar2(1, 0); +} + +TEST_F(GMockOutputTest, UnsatisfiedPrerequisites) { + Sequence s1, s2; + + EXPECT_CALL(foo_, Bar(_, 0, _)) + .InSequence(s1); + EXPECT_CALL(foo_, Bar2(0, 0)) + .InSequence(s2); + EXPECT_CALL(foo_, Bar2(1, _)) + .InSequence(s1, s2); + + foo_.Bar2(1, 0); // Has two immediate unsatisfied pre-requisites + foo_.Bar("Hi", 0, 0); + foo_.Bar2(0, 0); + foo_.Bar2(1, 0); +} + +TEST_F(GMockOutputTest, UnsatisfiedWith) { + EXPECT_CALL(foo_, Bar2(_, _)).With(Ge()); +} + +TEST_F(GMockOutputTest, UnsatisfiedExpectation) { + EXPECT_CALL(foo_, Bar(_, _, _)); + EXPECT_CALL(foo_, Bar2(0, _)) + .Times(2); + + foo_.Bar2(0, 1); +} + +TEST_F(GMockOutputTest, MismatchArguments) { + const std::string s = "Hi"; + EXPECT_CALL(foo_, Bar(Ref(s), _, Ge(0))); + + foo_.Bar("Ho", 0, -0.1); // Mismatch arguments + foo_.Bar(s, 0, 0); +} + +TEST_F(GMockOutputTest, MismatchWith) { + EXPECT_CALL(foo_, Bar2(Ge(2), Ge(1))) + .With(Ge()); + + foo_.Bar2(2, 3); // Mismatch With() + foo_.Bar2(2, 1); +} + +TEST_F(GMockOutputTest, MismatchArgumentsAndWith) { + EXPECT_CALL(foo_, Bar2(Ge(2), Ge(1))) + .With(Ge()); + + foo_.Bar2(1, 3); // Mismatch arguments and mismatch With() + foo_.Bar2(2, 1); +} + +TEST_F(GMockOutputTest, UnexpectedCallWithDefaultAction) { + ON_CALL(foo_, Bar2(_, _)) + .WillByDefault(Return(true)); // Default action #1 + ON_CALL(foo_, Bar2(1, _)) + .WillByDefault(Return(false)); // Default action #2 + + EXPECT_CALL(foo_, Bar2(2, 2)); + foo_.Bar2(1, 0); // Unexpected call, takes default action #2. + foo_.Bar2(0, 0); // Unexpected call, takes default action #1. + foo_.Bar2(2, 2); // Expected call. +} + +TEST_F(GMockOutputTest, ExcessiveCallWithDefaultAction) { + ON_CALL(foo_, Bar2(_, _)) + .WillByDefault(Return(true)); // Default action #1 + ON_CALL(foo_, Bar2(1, _)) + .WillByDefault(Return(false)); // Default action #2 + + EXPECT_CALL(foo_, Bar2(2, 2)); + EXPECT_CALL(foo_, Bar2(1, 1)); + + foo_.Bar2(2, 2); // Expected call. + foo_.Bar2(2, 2); // Excessive call, takes default action #1. + foo_.Bar2(1, 1); // Expected call. + foo_.Bar2(1, 1); // Excessive call, takes default action #2. +} + +TEST_F(GMockOutputTest, UninterestingCallWithDefaultAction) { + ON_CALL(foo_, Bar2(_, _)) + .WillByDefault(Return(true)); // Default action #1 + ON_CALL(foo_, Bar2(1, _)) + .WillByDefault(Return(false)); // Default action #2 + + foo_.Bar2(2, 2); // Uninteresting call, takes default action #1. + foo_.Bar2(1, 1); // Uninteresting call, takes default action #2. +} + +TEST_F(GMockOutputTest, ExplicitActionsRunOutWithDefaultAction) { + ON_CALL(foo_, Bar2(_, _)) + .WillByDefault(Return(true)); // Default action #1 + + EXPECT_CALL(foo_, Bar2(_, _)) + .Times(2) + .WillOnce(Return(false)); + foo_.Bar2(2, 2); + foo_.Bar2(1, 1); // Explicit actions in EXPECT_CALL run out. +} + +TEST_F(GMockOutputTest, CatchesLeakedMocks) { + MockFoo* foo1 = new MockFoo; + MockFoo* foo2 = new MockFoo; + + // Invokes ON_CALL on foo1. + ON_CALL(*foo1, Bar(_, _, _)).WillByDefault(Return('a')); + + // Invokes EXPECT_CALL on foo2. + EXPECT_CALL(*foo2, Bar2(_, _)); + EXPECT_CALL(*foo2, Bar2(1, _)); + EXPECT_CALL(*foo2, Bar3(_, _)).Times(AnyNumber()); + foo2->Bar2(2, 1); + foo2->Bar2(1, 1); + + // Both foo1 and foo2 are deliberately leaked. +} + +void TestCatchesLeakedMocksInAdHocTests() { + MockFoo* foo = new MockFoo; + + // Invokes EXPECT_CALL on foo. + EXPECT_CALL(*foo, Bar2(_, _)); + foo->Bar2(2, 1); + + // foo is deliberately leaked. +} + +int main(int argc, char **argv) { + testing::InitGoogleMock(&argc, argv); + + // Ensures that the tests pass no matter what value of + // --gmock_catch_leaked_mocks and --gmock_verbose the user specifies. + testing::GMOCK_FLAG(catch_leaked_mocks) = true; + testing::GMOCK_FLAG(verbose) = "warning"; + + TestCatchesLeakedMocksInAdHocTests(); + return RUN_ALL_TESTS(); +} diff --git a/libwvdrmengine/test/gmock/test/gmock_output_test_golden.txt b/libwvdrmengine/test/gmock/test/gmock_output_test_golden.txt new file mode 100644 index 00000000..a7ff5630 --- /dev/null +++ b/libwvdrmengine/test/gmock/test/gmock_output_test_golden.txt @@ -0,0 +1,310 @@ +[ RUN ] GMockOutputTest.ExpectedCall + +FILE:#: EXPECT_CALL(foo_, Bar2(0, _)) invoked +Stack trace: + +FILE:#: Mock function call matches EXPECT_CALL(foo_, Bar2(0, _))... + Function call: Bar2(0, 0) + Returns: false +Stack trace: +[ OK ] GMockOutputTest.ExpectedCall +[ RUN ] GMockOutputTest.ExpectedCallToVoidFunction + +FILE:#: EXPECT_CALL(foo_, Bar3(0, _)) invoked +Stack trace: + +FILE:#: Mock function call matches EXPECT_CALL(foo_, Bar3(0, _))... + Function call: Bar3(0, 0) +Stack trace: +[ OK ] GMockOutputTest.ExpectedCallToVoidFunction +[ RUN ] GMockOutputTest.ExplicitActionsRunOut + +GMOCK WARNING: +FILE:#: Too few actions specified in EXPECT_CALL(foo_, Bar2(_, _))... +Expected to be called twice, but has only 1 WillOnce(). +GMOCK WARNING: +FILE:#: Actions ran out in EXPECT_CALL(foo_, Bar2(_, _))... +Called 2 times, but only 1 WillOnce() is specified - returning default value. +Stack trace: +[ OK ] GMockOutputTest.ExplicitActionsRunOut +[ RUN ] GMockOutputTest.UnexpectedCall +unknown file: Failure + +Unexpected mock function call - returning default value. + Function call: Bar2(1, 0) + Returns: false +Google Mock tried the following 1 expectation, but it didn't match: + +FILE:#: EXPECT_CALL(foo_, Bar2(0, _))... + Expected arg #0: is equal to 0 + Actual: 1 + Expected: to be called once + Actual: never called - unsatisfied and active +[ FAILED ] GMockOutputTest.UnexpectedCall +[ RUN ] GMockOutputTest.UnexpectedCallToVoidFunction +unknown file: Failure + +Unexpected mock function call - returning directly. + Function call: Bar3(1, 0) +Google Mock tried the following 1 expectation, but it didn't match: + +FILE:#: EXPECT_CALL(foo_, Bar3(0, _))... + Expected arg #0: is equal to 0 + Actual: 1 + Expected: to be called once + Actual: never called - unsatisfied and active +[ FAILED ] GMockOutputTest.UnexpectedCallToVoidFunction +[ RUN ] GMockOutputTest.ExcessiveCall +FILE:#: Failure +Mock function called more times than expected - returning default value. + Function call: Bar2(0, 1) + Returns: false + Expected: to be called once + Actual: called twice - over-saturated and active +[ FAILED ] GMockOutputTest.ExcessiveCall +[ RUN ] GMockOutputTest.ExcessiveCallToVoidFunction +FILE:#: Failure +Mock function called more times than expected - returning directly. + Function call: Bar3(0, 1) + Expected: to be called once + Actual: called twice - over-saturated and active +[ FAILED ] GMockOutputTest.ExcessiveCallToVoidFunction +[ RUN ] GMockOutputTest.UninterestingCall + +GMOCK WARNING: +Uninteresting mock function call - returning default value. + Function call: Bar2(0, 1) + Returns: false +Stack trace: +[ OK ] GMockOutputTest.UninterestingCall +[ RUN ] GMockOutputTest.UninterestingCallToVoidFunction + +GMOCK WARNING: +Uninteresting mock function call - returning directly. + Function call: Bar3(0, 1) +Stack trace: +[ OK ] GMockOutputTest.UninterestingCallToVoidFunction +[ RUN ] GMockOutputTest.RetiredExpectation +unknown file: Failure + +Unexpected mock function call - returning default value. + Function call: Bar2(1, 1) + Returns: false +Google Mock tried the following 2 expectations, but none matched: + +FILE:#: tried expectation #0: EXPECT_CALL(foo_, Bar2(_, _))... + Expected: the expectation is active + Actual: it is retired + Expected: to be called once + Actual: called once - saturated and retired +FILE:#: tried expectation #1: EXPECT_CALL(foo_, Bar2(0, 0))... + Expected arg #0: is equal to 0 + Actual: 1 + Expected arg #1: is equal to 0 + Actual: 1 + Expected: to be called once + Actual: never called - unsatisfied and active +[ FAILED ] GMockOutputTest.RetiredExpectation +[ RUN ] GMockOutputTest.UnsatisfiedPrerequisite +unknown file: Failure + +Unexpected mock function call - returning default value. + Function call: Bar2(1, 0) + Returns: false +Google Mock tried the following 2 expectations, but none matched: + +FILE:#: tried expectation #0: EXPECT_CALL(foo_, Bar2(0, 0))... + Expected arg #0: is equal to 0 + Actual: 1 + Expected: to be called once + Actual: never called - unsatisfied and active +FILE:#: tried expectation #1: EXPECT_CALL(foo_, Bar2(1, _))... + Expected: all pre-requisites are satisfied + Actual: the following immediate pre-requisites are not satisfied: +FILE:#: pre-requisite #0 + (end of pre-requisites) + Expected: to be called once + Actual: never called - unsatisfied and active +[ FAILED ] GMockOutputTest.UnsatisfiedPrerequisite +[ RUN ] GMockOutputTest.UnsatisfiedPrerequisites +unknown file: Failure + +Unexpected mock function call - returning default value. + Function call: Bar2(1, 0) + Returns: false +Google Mock tried the following 2 expectations, but none matched: + +FILE:#: tried expectation #0: EXPECT_CALL(foo_, Bar2(0, 0))... + Expected arg #0: is equal to 0 + Actual: 1 + Expected: to be called once + Actual: never called - unsatisfied and active +FILE:#: tried expectation #1: EXPECT_CALL(foo_, Bar2(1, _))... + Expected: all pre-requisites are satisfied + Actual: the following immediate pre-requisites are not satisfied: +FILE:#: pre-requisite #0 +FILE:#: pre-requisite #1 + (end of pre-requisites) + Expected: to be called once + Actual: never called - unsatisfied and active +[ FAILED ] GMockOutputTest.UnsatisfiedPrerequisites +[ RUN ] GMockOutputTest.UnsatisfiedWith +FILE:#: Failure +Actual function call count doesn't match EXPECT_CALL(foo_, Bar2(_, _))... + Expected args: are a pair where the first >= the second + Expected: to be called once + Actual: never called - unsatisfied and active +[ FAILED ] GMockOutputTest.UnsatisfiedWith +[ RUN ] GMockOutputTest.UnsatisfiedExpectation +FILE:#: Failure +Actual function call count doesn't match EXPECT_CALL(foo_, Bar2(0, _))... + Expected: to be called twice + Actual: called once - unsatisfied and active +FILE:#: Failure +Actual function call count doesn't match EXPECT_CALL(foo_, Bar(_, _, _))... + Expected: to be called once + Actual: never called - unsatisfied and active +[ FAILED ] GMockOutputTest.UnsatisfiedExpectation +[ RUN ] GMockOutputTest.MismatchArguments +unknown file: Failure + +Unexpected mock function call - returning default value. + Function call: Bar(@0x# "Ho", 0, -0.1) + Returns: '\0' +Google Mock tried the following 1 expectation, but it didn't match: + +FILE:#: EXPECT_CALL(foo_, Bar(Ref(s), _, Ge(0)))... + Expected arg #0: references the variable @0x# "Hi" + Actual: "Ho", which is located @0x# + Expected arg #2: is >= 0 + Actual: -0.1 + Expected: to be called once + Actual: never called - unsatisfied and active +[ FAILED ] GMockOutputTest.MismatchArguments +[ RUN ] GMockOutputTest.MismatchWith +unknown file: Failure + +Unexpected mock function call - returning default value. + Function call: Bar2(2, 3) + Returns: false +Google Mock tried the following 1 expectation, but it didn't match: + +FILE:#: EXPECT_CALL(foo_, Bar2(Ge(2), Ge(1)))... + Expected args: are a pair where the first >= the second + Actual: don't match + Expected: to be called once + Actual: never called - unsatisfied and active +[ FAILED ] GMockOutputTest.MismatchWith +[ RUN ] GMockOutputTest.MismatchArgumentsAndWith +unknown file: Failure + +Unexpected mock function call - returning default value. + Function call: Bar2(1, 3) + Returns: false +Google Mock tried the following 1 expectation, but it didn't match: + +FILE:#: EXPECT_CALL(foo_, Bar2(Ge(2), Ge(1)))... + Expected arg #0: is >= 2 + Actual: 1 + Expected args: are a pair where the first >= the second + Actual: don't match + Expected: to be called once + Actual: never called - unsatisfied and active +[ FAILED ] GMockOutputTest.MismatchArgumentsAndWith +[ RUN ] GMockOutputTest.UnexpectedCallWithDefaultAction +unknown file: Failure + +Unexpected mock function call - taking default action specified at: +FILE:#: + Function call: Bar2(1, 0) + Returns: false +Google Mock tried the following 1 expectation, but it didn't match: + +FILE:#: EXPECT_CALL(foo_, Bar2(2, 2))... + Expected arg #0: is equal to 2 + Actual: 1 + Expected arg #1: is equal to 2 + Actual: 0 + Expected: to be called once + Actual: never called - unsatisfied and active +unknown file: Failure + +Unexpected mock function call - taking default action specified at: +FILE:#: + Function call: Bar2(0, 0) + Returns: true +Google Mock tried the following 1 expectation, but it didn't match: + +FILE:#: EXPECT_CALL(foo_, Bar2(2, 2))... + Expected arg #0: is equal to 2 + Actual: 0 + Expected arg #1: is equal to 2 + Actual: 0 + Expected: to be called once + Actual: never called - unsatisfied and active +[ FAILED ] GMockOutputTest.UnexpectedCallWithDefaultAction +[ RUN ] GMockOutputTest.ExcessiveCallWithDefaultAction +FILE:#: Failure +Mock function called more times than expected - taking default action specified at: +FILE:#: + Function call: Bar2(2, 2) + Returns: true + Expected: to be called once + Actual: called twice - over-saturated and active +FILE:#: Failure +Mock function called more times than expected - taking default action specified at: +FILE:#: + Function call: Bar2(1, 1) + Returns: false + Expected: to be called once + Actual: called twice - over-saturated and active +[ FAILED ] GMockOutputTest.ExcessiveCallWithDefaultAction +[ RUN ] GMockOutputTest.UninterestingCallWithDefaultAction + +GMOCK WARNING: +Uninteresting mock function call - taking default action specified at: +FILE:#: + Function call: Bar2(2, 2) + Returns: true +Stack trace: + +GMOCK WARNING: +Uninteresting mock function call - taking default action specified at: +FILE:#: + Function call: Bar2(1, 1) + Returns: false +Stack trace: +[ OK ] GMockOutputTest.UninterestingCallWithDefaultAction +[ RUN ] GMockOutputTest.ExplicitActionsRunOutWithDefaultAction + +GMOCK WARNING: +FILE:#: Too few actions specified in EXPECT_CALL(foo_, Bar2(_, _))... +Expected to be called twice, but has only 1 WillOnce(). +GMOCK WARNING: +FILE:#: Actions ran out in EXPECT_CALL(foo_, Bar2(_, _))... +Called 2 times, but only 1 WillOnce() is specified - taking default action specified at: +FILE:#: +Stack trace: +[ OK ] GMockOutputTest.ExplicitActionsRunOutWithDefaultAction +[ RUN ] GMockOutputTest.CatchesLeakedMocks +[ OK ] GMockOutputTest.CatchesLeakedMocks +[ FAILED ] GMockOutputTest.UnexpectedCall +[ FAILED ] GMockOutputTest.UnexpectedCallToVoidFunction +[ FAILED ] GMockOutputTest.ExcessiveCall +[ FAILED ] GMockOutputTest.ExcessiveCallToVoidFunction +[ FAILED ] GMockOutputTest.RetiredExpectation +[ FAILED ] GMockOutputTest.UnsatisfiedPrerequisite +[ FAILED ] GMockOutputTest.UnsatisfiedPrerequisites +[ FAILED ] GMockOutputTest.UnsatisfiedWith +[ FAILED ] GMockOutputTest.UnsatisfiedExpectation +[ FAILED ] GMockOutputTest.MismatchArguments +[ FAILED ] GMockOutputTest.MismatchWith +[ FAILED ] GMockOutputTest.MismatchArgumentsAndWith +[ FAILED ] GMockOutputTest.UnexpectedCallWithDefaultAction +[ FAILED ] GMockOutputTest.ExcessiveCallWithDefaultAction + + +FILE:#: ERROR: this mock object should be deleted but never is. Its address is @0x#. +FILE:#: ERROR: this mock object should be deleted but never is. Its address is @0x#. +FILE:#: ERROR: this mock object should be deleted but never is. Its address is @0x#. +ERROR: 3 leaked mock objects found at program exit. diff --git a/libwvdrmengine/test/gmock/test/gmock_test.cc b/libwvdrmengine/test/gmock/test/gmock_test.cc new file mode 100644 index 00000000..0b891137 --- /dev/null +++ b/libwvdrmengine/test/gmock/test/gmock_test.cc @@ -0,0 +1,255 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This file tests code in gmock.cc. + +#include "gmock/gmock.h" + +#include +#include "gtest/gtest.h" + +using testing::GMOCK_FLAG(verbose); +using testing::InitGoogleMock; +using testing::internal::g_init_gtest_count; + +// Verifies that calling InitGoogleMock() on argv results in new_argv, +// and the gmock_verbose flag's value is set to expected_gmock_verbose. +template +void TestInitGoogleMock(const Char* (&argv)[M], const Char* (&new_argv)[N], + const ::std::string& expected_gmock_verbose) { + const ::std::string old_verbose = GMOCK_FLAG(verbose); + + int argc = M; + InitGoogleMock(&argc, const_cast(argv)); + ASSERT_EQ(N, argc) << "The new argv has wrong number of elements."; + + for (int i = 0; i < N; i++) { + EXPECT_STREQ(new_argv[i], argv[i]); + } + + EXPECT_EQ(expected_gmock_verbose, GMOCK_FLAG(verbose).c_str()); + GMOCK_FLAG(verbose) = old_verbose; // Restores the gmock_verbose flag. +} + +TEST(InitGoogleMockTest, ParsesInvalidCommandLine) { + const char* argv[] = { + NULL + }; + + const char* new_argv[] = { + NULL + }; + + TestInitGoogleMock(argv, new_argv, GMOCK_FLAG(verbose)); +} + +TEST(InitGoogleMockTest, ParsesEmptyCommandLine) { + const char* argv[] = { + "foo.exe", + NULL + }; + + const char* new_argv[] = { + "foo.exe", + NULL + }; + + TestInitGoogleMock(argv, new_argv, GMOCK_FLAG(verbose)); +} + +TEST(InitGoogleMockTest, ParsesSingleFlag) { + const char* argv[] = { + "foo.exe", + "--gmock_verbose=info", + NULL + }; + + const char* new_argv[] = { + "foo.exe", + NULL + }; + + TestInitGoogleMock(argv, new_argv, "info"); +} + +TEST(InitGoogleMockTest, ParsesUnrecognizedFlag) { + const char* argv[] = { + "foo.exe", + "--non_gmock_flag=blah", + NULL + }; + + const char* new_argv[] = { + "foo.exe", + "--non_gmock_flag=blah", + NULL + }; + + TestInitGoogleMock(argv, new_argv, GMOCK_FLAG(verbose)); +} + +TEST(InitGoogleMockTest, ParsesGoogleMockFlagAndUnrecognizedFlag) { + const char* argv[] = { + "foo.exe", + "--non_gmock_flag=blah", + "--gmock_verbose=error", + NULL + }; + + const char* new_argv[] = { + "foo.exe", + "--non_gmock_flag=blah", + NULL + }; + + TestInitGoogleMock(argv, new_argv, "error"); +} + +TEST(InitGoogleMockTest, CallsInitGoogleTest) { + const int old_init_gtest_count = g_init_gtest_count; + const char* argv[] = { + "foo.exe", + "--non_gmock_flag=blah", + "--gmock_verbose=error", + NULL + }; + + const char* new_argv[] = { + "foo.exe", + "--non_gmock_flag=blah", + NULL + }; + + TestInitGoogleMock(argv, new_argv, "error"); + EXPECT_EQ(old_init_gtest_count + 1, g_init_gtest_count); +} + +TEST(WideInitGoogleMockTest, ParsesInvalidCommandLine) { + const wchar_t* argv[] = { + NULL + }; + + const wchar_t* new_argv[] = { + NULL + }; + + TestInitGoogleMock(argv, new_argv, GMOCK_FLAG(verbose)); +} + +TEST(WideInitGoogleMockTest, ParsesEmptyCommandLine) { + const wchar_t* argv[] = { + L"foo.exe", + NULL + }; + + const wchar_t* new_argv[] = { + L"foo.exe", + NULL + }; + + TestInitGoogleMock(argv, new_argv, GMOCK_FLAG(verbose)); +} + +TEST(WideInitGoogleMockTest, ParsesSingleFlag) { + const wchar_t* argv[] = { + L"foo.exe", + L"--gmock_verbose=info", + NULL + }; + + const wchar_t* new_argv[] = { + L"foo.exe", + NULL + }; + + TestInitGoogleMock(argv, new_argv, "info"); +} + +TEST(WideInitGoogleMockTest, ParsesUnrecognizedFlag) { + const wchar_t* argv[] = { + L"foo.exe", + L"--non_gmock_flag=blah", + NULL + }; + + const wchar_t* new_argv[] = { + L"foo.exe", + L"--non_gmock_flag=blah", + NULL + }; + + TestInitGoogleMock(argv, new_argv, GMOCK_FLAG(verbose)); +} + +TEST(WideInitGoogleMockTest, ParsesGoogleMockFlagAndUnrecognizedFlag) { + const wchar_t* argv[] = { + L"foo.exe", + L"--non_gmock_flag=blah", + L"--gmock_verbose=error", + NULL + }; + + const wchar_t* new_argv[] = { + L"foo.exe", + L"--non_gmock_flag=blah", + NULL + }; + + TestInitGoogleMock(argv, new_argv, "error"); +} + +TEST(WideInitGoogleMockTest, CallsInitGoogleTest) { + const int old_init_gtest_count = g_init_gtest_count; + const wchar_t* argv[] = { + L"foo.exe", + L"--non_gmock_flag=blah", + L"--gmock_verbose=error", + NULL + }; + + const wchar_t* new_argv[] = { + L"foo.exe", + L"--non_gmock_flag=blah", + NULL + }; + + TestInitGoogleMock(argv, new_argv, "error"); + EXPECT_EQ(old_init_gtest_count + 1, g_init_gtest_count); +} + +// Makes sure Google Mock flags can be accessed in code. +TEST(FlagTest, IsAccessibleInCode) { + bool dummy = testing::GMOCK_FLAG(catch_leaked_mocks) && + testing::GMOCK_FLAG(verbose) == ""; + (void)dummy; // Avoids the "unused local variable" warning. +} diff --git a/libwvdrmengine/test/gmock/test/gmock_test_utils.py b/libwvdrmengine/test/gmock/test/gmock_test_utils.py new file mode 100755 index 00000000..ac3d67ae --- /dev/null +++ b/libwvdrmengine/test/gmock/test/gmock_test_utils.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python +# +# Copyright 2006, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Unit test utilities for Google C++ Mocking Framework.""" + +__author__ = 'wan@google.com (Zhanyong Wan)' + +import os +import sys + + +# Determines path to gtest_test_utils and imports it. +SCRIPT_DIR = os.path.dirname(__file__) or '.' + +# isdir resolves symbolic links. +gtest_tests_util_dir = os.path.join(SCRIPT_DIR, '../gtest/test') +if os.path.isdir(gtest_tests_util_dir): + GTEST_TESTS_UTIL_DIR = gtest_tests_util_dir +else: + GTEST_TESTS_UTIL_DIR = os.path.join(SCRIPT_DIR, '../../gtest/test') + +sys.path.append(GTEST_TESTS_UTIL_DIR) +import gtest_test_utils # pylint: disable-msg=C6204 + + +def GetSourceDir(): + """Returns the absolute path of the directory where the .py files are.""" + + return gtest_test_utils.GetSourceDir() + + +def GetTestExecutablePath(executable_name): + """Returns the absolute path of the test binary given its name. + + The function will print a message and abort the program if the resulting file + doesn't exist. + + Args: + executable_name: name of the test binary that the test script runs. + + Returns: + The absolute path of the test binary. + """ + + return gtest_test_utils.GetTestExecutablePath(executable_name) + + +def GetExitStatus(exit_code): + """Returns the argument to exit(), or -1 if exit() wasn't called. + + Args: + exit_code: the result value of os.system(command). + """ + + if os.name == 'nt': + # On Windows, os.WEXITSTATUS() doesn't work and os.system() returns + # the argument to exit() directly. + return exit_code + else: + # On Unix, os.WEXITSTATUS() must be used to extract the exit status + # from the result of os.system(). + if os.WIFEXITED(exit_code): + return os.WEXITSTATUS(exit_code) + else: + return -1 + + +# Suppresses the "Invalid const name" lint complaint +# pylint: disable-msg=C6409 + +# Exposes Subprocess from gtest_test_utils. +Subprocess = gtest_test_utils.Subprocess + +# Exposes TestCase from gtest_test_utils. +TestCase = gtest_test_utils.TestCase + +# pylint: enable-msg=C6409 + + +def Main(): + """Runs the unit test.""" + + gtest_test_utils.Main() diff --git a/libwvdrmengine/test/java/Android.mk b/libwvdrmengine/test/java/Android.mk new file mode 100644 index 00000000..ac7dddf4 --- /dev/null +++ b/libwvdrmengine/test/java/Android.mk @@ -0,0 +1,15 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := optional + +LOCAL_SRC_FILES := $(call all-java-files-under, src) + +LOCAL_PACKAGE_NAME := MediaDrmAPITest + +LOCAL_SDK_VERSION := current + +include $(BUILD_PACKAGE) + +# Use the following include to make our test apk. +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/libwvdrmengine/test/java/AndroidManifest.xml b/libwvdrmengine/test/java/AndroidManifest.xml new file mode 100644 index 00000000..9d27677e --- /dev/null +++ b/libwvdrmengine/test/java/AndroidManifest.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + diff --git a/libwvdrmengine/test/java/default.properties b/libwvdrmengine/test/java/default.properties new file mode 100644 index 00000000..8d2c89a7 --- /dev/null +++ b/libwvdrmengine/test/java/default.properties @@ -0,0 +1,2 @@ +# Project target. +target=android-IceCreamSandwich diff --git a/libwvdrmengine/test/java/res/drawable-hdpi/icon.png b/libwvdrmengine/test/java/res/drawable-hdpi/icon.png new file mode 100644 index 00000000..8074c4c5 Binary files /dev/null and b/libwvdrmengine/test/java/res/drawable-hdpi/icon.png differ diff --git a/libwvdrmengine/test/java/res/drawable-ldpi/icon.png b/libwvdrmengine/test/java/res/drawable-ldpi/icon.png new file mode 100644 index 00000000..1095584e Binary files /dev/null and b/libwvdrmengine/test/java/res/drawable-ldpi/icon.png differ diff --git a/libwvdrmengine/test/java/res/drawable-mdpi/icon.png b/libwvdrmengine/test/java/res/drawable-mdpi/icon.png new file mode 100644 index 00000000..a07c69fa Binary files /dev/null and b/libwvdrmengine/test/java/res/drawable-mdpi/icon.png differ diff --git a/libwvdrmengine/test/java/res/layout/main.xml b/libwvdrmengine/test/java/res/layout/main.xml new file mode 100644 index 00000000..1f015bcb --- /dev/null +++ b/libwvdrmengine/test/java/res/layout/main.xml @@ -0,0 +1,21 @@ + + + + + + + diff --git a/libwvdrmengine/test/java/res/values/strings.xml b/libwvdrmengine/test/java/res/values/strings.xml new file mode 100644 index 00000000..1e97aad0 --- /dev/null +++ b/libwvdrmengine/test/java/res/values/strings.xml @@ -0,0 +1,4 @@ + + + MediaDrmAPI Test + diff --git a/libwvdrmengine/test/java/src/com/widevine/test/MediaDrmAPITest.java b/libwvdrmengine/test/java/src/com/widevine/test/MediaDrmAPITest.java new file mode 100644 index 00000000..6e7bb942 --- /dev/null +++ b/libwvdrmengine/test/java/src/com/widevine/test/MediaDrmAPITest.java @@ -0,0 +1,173 @@ +/* + * (c)Copyright 2011 Widevine Technologies, Inc + */ + +package com.widevine.test; + +import android.app.TabActivity; +import android.os.Bundle; +import android.os.AsyncTask; +import android.media.MediaDrm; +import android.util.Log; +import java.util.concurrent.TimeUnit; +import java.util.UUID; +import java.util.Arrays; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.HttpClient; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.entity.ByteArrayEntity; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.HttpResponse; +import org.apache.http.util.EntityUtils; + +public class MediaDrmAPITest extends TabActivity { + private final String TAG = "MediaDrmAPITest"; + + private class PostRequestTask extends AsyncTask { + private byte[] mDrmRequest; + private byte[] mResponseBody; + + public PostRequestTask(byte[] drmRequest) { + mDrmRequest = drmRequest; + } + + protected Void doInBackground(String... urls) { + mResponseBody = postRequest(urls[0], mDrmRequest); + Log.d(TAG, "response length=" + mResponseBody.length); + return null; + } + + public byte[] getResponseBody() { + return mResponseBody; + } + } + + private byte[] postRequest(String url, byte[] drmRequest) { + Log.d(TAG, "PostRequest url=" + url); + HttpClient httpclient = new DefaultHttpClient(); + HttpPost httppost = new HttpPost(url); + + try { + // Add data + ByteArrayEntity entity = new ByteArrayEntity(drmRequest); + entity.setChunked(true); + httppost.setEntity(entity); + httppost.setHeader("User-Agent", "Widevine CDM v1.0"); + + // Execute HTTP Post Request + HttpResponse response = httpclient.execute(httppost); + + byte[] responseBody; + int responseCode = response.getStatusLine().getStatusCode(); + if (responseCode == 200) { + responseBody = EntityUtils.toByteArray(response.getEntity()); + } else { + Log.d(TAG, "Server returned HTTP error code " + responseCode); + return null; + } + return responseBody; + + } catch (ClientProtocolException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + private static byte[] hexToByteArray(String s) { + int len = s.length(); + byte[] data = new byte[len / 2]; + for (int i = 0; i < len; i += 2) { + data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + + Character.digit(s.charAt(i+1), 16)); + } + return data; + } + + // validate the response body and return the drmResponse blob + private byte[] parseResponseBody(byte[] responseBody) { + String bodyString = null; + try { + bodyString = new String(responseBody, "UTF-8"); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + + if (bodyString == null) { + return null; + } + + if (!bodyString.startsWith("GLS/")) { + Log.e(TAG, "Invalid response from server, expected GLS/"); + return null; + } + if (!bodyString.startsWith("GLS/1.")) { + Log.e(TAG, "Invalid server version, expected 1.x"); + return null; + } + int drmMessageOffset = bodyString.indexOf("\r\n\r\n"); + if (drmMessageOffset == -1) { + Log.e(TAG, "Invalid server response, could not locate drm message"); + return null; + } + return Arrays.copyOfRange(responseBody, drmMessageOffset + 4, responseBody.length); + } + + + + static final UUID kWidevineScheme = new UUID(0xEDEF8BA979D64ACEL, 0xA3C827DCD51D21EDL); + static final String kClientAuth = "?source=YOUTUBE&video_id=EGHC6OHNbOo&oauth=ya.gtsqawidevine"; + static final String kServerUrl = "https://jmt17.google.com/video-dev/license/GetCencLicense"; + static final String kPort = "80"; + + static final byte[] kKeyId = hexToByteArray(// blob size and pssh + "000000347073736800000000" + + // Widevine system id + "edef8ba979d64acea3c827dcd51d21ed00000014" + + // pssh data + "08011210e02562e04cd55351b14b3d748d36ed8e"); + + /** Called when the activity is first created. */ + @Override + public void onCreate(Bundle savedInstanceState) { + + super.onCreate(savedInstanceState); + setContentView(R.layout.main); + try { + MediaDrm drm = new MediaDrm(kWidevineScheme); + + byte[] sessionId = drm.openSession(); + + MediaDrm.LicenseRequest drmRequest; + drmRequest = drm.getLicenseRequest(sessionId, kKeyId, "video/mp4", + MediaDrm.MEDIA_DRM_LICENSE_TYPE_STREAMING, null); + + PostRequestTask postTask = new PostRequestTask(drmRequest.data); + postTask.execute(kServerUrl + ":" + kPort + kClientAuth); + + // wait for post task to complete + byte[] responseBody; + long startTime = System.currentTimeMillis(); + do { + responseBody = postTask.getResponseBody(); + } while (responseBody == null && System.currentTimeMillis() - startTime < 5000); + + if (responseBody == null) { + Log.d(TAG, "No response from license server!"); + } else { + byte[] drmResponse = parseResponseBody(responseBody); + + drm.provideLicenseResponse(sessionId, drmResponse); + drm.closeSession(sessionId); + } + + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/libwvdrmengine/test/unit/Android.mk b/libwvdrmengine/test/unit/Android.mk new file mode 100644 index 00000000..18be3315 --- /dev/null +++ b/libwvdrmengine/test/unit/Android.mk @@ -0,0 +1,33 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + WVCreateDrmPluginFactory_test.cpp \ + WVDrmPluginFactory_test.cpp \ + ../../src/WVCreateDrmPluginFactory.cpp \ + ../../src/WVDrmPluginFactory.cpp \ + +LOCAL_C_INCLUDES := \ + bionic \ + external/gtest/include \ + external/stlport/stlport \ + frameworks/av/include \ + frameworks/native/include \ + vendor/widevine/libwvdrmengine/include \ + vendor/widevine/libwvdrmengine/oemcrypto/include \ + +LOCAL_STATIC_LIBRARIES := \ + libgtest \ + libgtest_main \ + +LOCAL_SHARED_LIBRARIES := \ + libdl \ + liblog \ + libstlport \ + libutils \ + +LOCAL_MODULE := libwvdrmengine_test + +LOCAL_MODULE_TAGS := tests + +include $(BUILD_EXECUTABLE) diff --git a/libwvdrmengine/test/unit/WVCreateDrmPluginFactory_test.cpp b/libwvdrmengine/test/unit/WVCreateDrmPluginFactory_test.cpp new file mode 100644 index 00000000..66a3085f --- /dev/null +++ b/libwvdrmengine/test/unit/WVCreateDrmPluginFactory_test.cpp @@ -0,0 +1,15 @@ +/* + * Copyright 2012 Google Inc. All Rights Reserved. + */ + +#include "gtest/gtest.h" +#include "utils/UniquePtr.h" +#include "WVCreateDrmPluginFactory.h" + +using namespace android; + +TEST(CreateDrmPluginFactoryTest, CreatesObject) { + UniquePtr factory(createDrmPluginFactory()); + + EXPECT_NE((DrmPluginFactory*)NULL, factory.get()) << "createDrmPluginFactory() returned null"; +} diff --git a/libwvdrmengine/test/unit/WVDrmPluginFactory_test.cpp b/libwvdrmengine/test/unit/WVDrmPluginFactory_test.cpp new file mode 100644 index 00000000..a443fa2a --- /dev/null +++ b/libwvdrmengine/test/unit/WVDrmPluginFactory_test.cpp @@ -0,0 +1,41 @@ +/* + * Copyright 2012 Google Inc. All Rights Reserved. + */ + +#include "gtest/gtest.h" +#include "utils/UniquePtr.h" +#include "WVDrmPluginFactory.h" + +using namespace wvdrm; + +const uint8_t kWidevineUUID[16] = { + 0xED,0xEF,0x8B,0xA9,0x79,0xD6,0x4A,0xCE, + 0xA3,0xC8,0x27,0xDC,0xD5,0x1D,0x21,0xED +}; + +const uint8_t kOldNetflixWidevineUUID[16] = { + 0x29,0x70,0x1F,0xE4,0x3C,0xC7,0x4A,0x34, + 0x8C,0x5B,0xAE,0x90,0xC7,0x43,0x9A,0x47 +}; + +const uint8_t kUnknownUUID[16] = { + 0x6A,0x7F,0xAA,0xB0,0x83,0xC7,0x9E,0x20, + 0x08,0xBC,0xEF,0x32,0x34,0x1A,0x9A,0x26 +}; + +TEST(WVDrmPluginFactoryTest, SupportsSupportedCryptoSchemes) { + UniquePtr factory(new WVDrmPluginFactory()); + + EXPECT_TRUE(factory->isCryptoSchemeSupported(kWidevineUUID)) << + "WVPluginFactory does not support Widevine's UUID"; + + EXPECT_TRUE(factory->isCryptoSchemeSupported(kOldNetflixWidevineUUID)) << + "WVPluginFactory does not support the old Netflix Widevine UUID"; +} + +TEST(WVDrmPluginFactoryTest, DoesNotSupportUnsupportedCryptoSchemes) { + UniquePtr factory(new WVDrmPluginFactory()); + + EXPECT_FALSE(factory->isCryptoSchemeSupported(kUnknownUUID)) << + "WVPluginFactory incorrectly claims to support an unknown UUID"; +}