Compare commits
15
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad42ee66b1 | ||
|
|
b2e77d6847 | ||
|
|
784b1650c3 | ||
|
|
78c6456fbe | ||
|
|
84cce8cfdf | ||
|
|
49b7c7169f | ||
|
|
dbfe2dbc5e | ||
|
|
0ec60de287 | ||
|
|
7eb756ca96 | ||
|
|
3abddc4584 | ||
|
|
35661a1974 | ||
|
|
606a1259f6 | ||
|
|
6479a5bc12 | ||
|
|
c95f9ed7e0 | ||
|
|
e3b7861aa2 |
+2
-10
@@ -1,15 +1,7 @@
|
||||
;;; Directory Local Variables -*- no-byte-compile: t -*-
|
||||
;;; For more information see (info "(emacs) Directory Variables")
|
||||
|
||||
((c++-mode . ((eval . (progn
|
||||
(make-local-variable 'eglot-ignored-server-capabilities)
|
||||
(add-to-list 'eglot-ignored-server-capabilities
|
||||
:documentOnTypeFormattingProvider)))
|
||||
(c-file-style . "filament")
|
||||
((c++-mode . ((c-file-style . "filament")
|
||||
(apheleia-inhibit . t)))
|
||||
(c-mode . ((eval . (progn
|
||||
(make-local-variable 'eglot-ignored-server-capabilities)
|
||||
(add-to-list 'eglot-ignored-server-capabilities
|
||||
:documentOnTypeFormattingProvider)))
|
||||
(c-file-style . "filament")
|
||||
(c-mode . ((c-file-style . "filament")
|
||||
(apheleia-inhibit . t))))
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ end_of_line = lf
|
||||
insert_final_newline = true
|
||||
charset = utf-8
|
||||
|
||||
[*.{c,cpp,h,inc,kt,java,js,md,fs,vs}]
|
||||
[*.{c,cpp,h,inc,kt,java,js,md}]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
max_line_length = 100
|
||||
|
||||
@@ -92,14 +92,15 @@ jobs:
|
||||
|
||||
test-renderdiff:
|
||||
name: test-renderdiff
|
||||
runs-on: ubuntu-22.04-16core
|
||||
runs-on: ubuntu-22.04-4core
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
- uses: ./.github/actions/ubuntu-apt-add-src
|
||||
- name: Run script
|
||||
run: |
|
||||
source ./build/linux/ci-common.sh && bash test/renderdiff/test.sh
|
||||
echo "Disabled renderdiff due to Mesa -> Currently planned outage: 2025-03-16 -> 2025-03-22"
|
||||
# source ./build/linux/ci-common.sh && bash test/renderdiff/test.sh
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: presubmit-renderdiff-result
|
||||
@@ -107,11 +108,11 @@ jobs:
|
||||
|
||||
validate-wgsl-pipeline:
|
||||
name: validate-wgsl-pipeline
|
||||
runs-on: macos-14-xlarge
|
||||
runs-on: ubuntu-22.04-4core
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
- name: Run build script
|
||||
run: ./build.sh -W debug test_filamat
|
||||
run: source ./build/linux/ci-common.sh && ./build.sh -W debug test_filamat
|
||||
- name: Run test
|
||||
run: ./out/cmake-debug/libs/filamat/test_filamat --gtest_filter=MaterialCompiler.Wgsl*
|
||||
@@ -601,6 +601,14 @@ if (FILAMENT_SUPPORTS_METAL)
|
||||
set(MATC_API_FLAGS ${MATC_API_FLAGS} -a metal)
|
||||
endif()
|
||||
|
||||
# With WebGPU, push constants are not supported. Skinning uses them.
|
||||
# WebGPU has a proposal to add push constants at https://github.com/gpuweb/gpuweb/blob/main/proposals/push-constants.md
|
||||
# With WebGPU, Tint does not support ClipDistance which is used in Stereo. Mentioned in comment
|
||||
# https://github.com/google/dawn/blob/855d17b08abdf02f9142bf5a8f14d0ea088810a4/src/tint/lang/spirv/reader/ast_parser/function.cc#L4434
|
||||
if (FILAMENT_SUPPORTS_WEBGPU)
|
||||
set(MATC_API_FLAGS ${MATC_API_FLAGS} -a webgpu --variant-filter=skinning,stereo)
|
||||
endif()
|
||||
|
||||
# Disable ESSL 1.0 code generation.
|
||||
if (NOT FILAMENT_ENABLE_FEATURE_LEVEL_0)
|
||||
set(MATC_API_FLAGS ${MATC_API_FLAGS} -1)
|
||||
|
||||
@@ -12,12 +12,12 @@ if (FILAMENT_SUPPORTS_VULKAN)
|
||||
endif()
|
||||
|
||||
if (FILAMENT_SUPPORTS_WEBGPU)
|
||||
add_library(webgpu_dawn STATIC IMPORTED)
|
||||
set_target_properties(webgpu_dawn PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libwebgpu_dawn.a)
|
||||
# See third_party/dawn/tnt/CMakeLists.txt and libs/filamat/CMakeLists.txt
|
||||
add_library(tint STATIC IMPORTED)
|
||||
set_target_properties(tint PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libtint_combined.a)
|
||||
endif()
|
||||
|
||||
|
||||
add_library(${FILAMAT_FLAVOR} STATIC IMPORTED)
|
||||
set_target_properties(${FILAMAT_FLAVOR} PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/lib${FILAMAT_FLAVOR}.a)
|
||||
@@ -57,5 +57,5 @@ target_link_libraries(filamat-jni
|
||||
utils
|
||||
log
|
||||
smol-v
|
||||
$<$<STREQUAL:${FILAMENT_SUPPORTS_WEBGPU},ON>:webgpu_dawn> # for tint
|
||||
$<$<STREQUAL:${FILAMENT_SUPPORTS_WEBGPU},ON>:tint>
|
||||
)
|
||||
|
||||
@@ -29,10 +29,6 @@ add_library(filaflat STATIC IMPORTED)
|
||||
set_target_properties(filaflat PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libfilaflat.a)
|
||||
|
||||
add_library(filamat STATIC IMPORTED)
|
||||
set_target_properties(filamat PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libfilamat.a)
|
||||
|
||||
add_library(geometry STATIC IMPORTED)
|
||||
set_target_properties(geometry PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libgeometry.a)
|
||||
@@ -59,11 +55,11 @@ add_library(smol-v STATIC IMPORTED)
|
||||
set_target_properties(smol-v PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libsmol-v.a)
|
||||
|
||||
if (FILAMENT_ENABLE_FGVIEWER)
|
||||
add_library(fgviewer STATIC IMPORTED)
|
||||
set_target_properties(fgviewer PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libfgviewer.a)
|
||||
endif()
|
||||
if (FILAMENT_ENABLE_FGVIEWER)
|
||||
add_library(fgviewer STATIC IMPORTED)
|
||||
set_target_properties(fgviewer PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libfgviewer.a)
|
||||
endif()
|
||||
|
||||
if (FILAMENT_ENABLE_MATDBG)
|
||||
add_library(matdbg STATIC IMPORTED)
|
||||
|
||||
@@ -68,6 +68,21 @@ Java_com_google_android_filament_Texture_nIsTextureSwizzleSupported(JNIEnv*, jcl
|
||||
return (jboolean) Texture::isTextureSwizzleSupported(*engine);
|
||||
}
|
||||
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
Java_com_google_android_filament_Texture_nGetMaxTextureSize(JNIEnv *, jclass,
|
||||
jlong nativeEngine, jint sampler) {
|
||||
Engine *engine = (Engine *) nativeEngine;
|
||||
return Texture::getMaxTextureSize(*engine, (Texture::Sampler)sampler);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
Java_com_google_android_filament_Texture_nGetMaxArrayTextureLayers(JNIEnv *, jclass,
|
||||
jlong nativeEngine) {
|
||||
Engine *engine = (Engine *) nativeEngine;
|
||||
return Texture::getMaxArrayTextureLayers(*engine);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
Java_com_google_android_filament_Texture_nValidatePixelFormatAndType(JNIEnv*, jclass,
|
||||
jint internalFormat, jint pixelDataFormat, jint pixelDataType) {
|
||||
|
||||
@@ -691,6 +691,24 @@ public class Texture {
|
||||
pixelDataType.ordinal());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param engine {@link Engine}
|
||||
* @param type Texture sampler type
|
||||
* @return The maximum size in texels of a texture of type \p type. At least 2048 for
|
||||
* 2D textures, 256 for 3D textures
|
||||
*/
|
||||
public static int getMaxTextureSize(@NonNull Engine engine, Sampler type) {
|
||||
return nGetMaxTextureSize(engine.getNativeObject(), type.ordinal());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param engine {@link Engine}
|
||||
* @return The maximum number of layers supported by texture arrays. At least 256.
|
||||
*/
|
||||
public static int getMaxArrayTextureLayers(@NonNull Engine engine) {
|
||||
return nGetMaxArrayTextureLayers(engine.getNativeObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* Use <code>Builder</code> to construct a <code>Texture</code> object instance.
|
||||
*/
|
||||
@@ -1289,6 +1307,8 @@ public class Texture {
|
||||
private static native boolean nIsTextureFormatSupported(long nativeEngine, int internalFormat);
|
||||
private static native boolean nIsTextureFormatMipmappable(long nativeEngine, int internalFormat);
|
||||
private static native boolean nIsTextureSwizzleSupported(long nativeEngine);
|
||||
private static native int nGetMaxTextureSize(long nativeObject, int ordinal);
|
||||
private static native int nGetMaxArrayTextureLayers(long nativeObject);
|
||||
|
||||
private static native boolean nValidatePixelFormatAndType(int internalFormat, int pixelDataFormat,
|
||||
int pixelDataType);
|
||||
|
||||
@@ -30,6 +30,9 @@ if [[ "$GITHUB_WORKFLOW" ]]; then
|
||||
sudo apt-get install clang-$GITHUB_CLANG_VERSION libc++-$GITHUB_CLANG_VERSION-dev libc++abi-$GITHUB_CLANG_VERSION-dev
|
||||
sudo apt-get install mesa-common-dev libxi-dev libxxf86vm-dev
|
||||
|
||||
# For dawn
|
||||
sudo apt-get install libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev
|
||||
|
||||
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang-${GITHUB_CLANG_VERSION} 100
|
||||
sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++-${GITHUB_CLANG_VERSION} 100
|
||||
fi
|
||||
|
||||
@@ -242,11 +242,31 @@ endif()
|
||||
|
||||
if (FILAMENT_SUPPORTS_WEBGPU)
|
||||
list(APPEND SRCS
|
||||
include/backend/platforms/WebGPUPlatform.h
|
||||
src/webgpu/platform/WebGPUPlatform.cpp
|
||||
src/webgpu/WebGPUConstants.h
|
||||
src/webgpu/WebGPUDriver.cpp
|
||||
src/webgpu/WebGPUDriver.h
|
||||
src/webgpu/WebGPUPlatform.cpp
|
||||
src/webgpu/WebGPUPlatform.h
|
||||
)
|
||||
if (WIN32)
|
||||
list(APPEND SRCS src/webgpu/platform/WebGPUPlatformWindows.cpp)
|
||||
elseif (LINUX)
|
||||
list(APPEND SRCS src/webgpu/platform/WebGPUPlatformLinux.cpp)
|
||||
elseif (APPLE OR IOS)
|
||||
list(APPEND SRCS src/webgpu/platform/WebGPUPlatformApple.mm)
|
||||
elseif (ANDROID)
|
||||
list(APPEND SRCS src/webgpu/platform/WebGPUPlatformAndroid.cpp)
|
||||
endif()
|
||||
|
||||
if (TNT_DEV)
|
||||
set(FILAMENT_WEBGPU_IMMEDIATE_ERROR_HANDLING_DEFAULT ON)
|
||||
else()
|
||||
set(FILAMENT_WEBGPU_IMMEDIATE_ERROR_HANDLING_DEFAULT OFF)
|
||||
endif()
|
||||
|
||||
option(FILAMENT_WEBGPU_IMMEDIATE_ERROR_HANDLING
|
||||
"Enable immediate_error_handling for the WebGPU backend Dawn implementation"
|
||||
${FILAMENT_WEBGPU_IMMEDIATE_ERROR_HANDLING_DEFAULT})
|
||||
endif()
|
||||
|
||||
if (ANDROID)
|
||||
@@ -433,6 +453,10 @@ if (FILAMENT_SUPPORTS_METAL)
|
||||
target_compile_definitions(${TARGET} PRIVATE $<$<BOOL:${FILAMENT_METAL_PROFILING}>:FILAMENT_METAL_PROFILING>)
|
||||
endif()
|
||||
|
||||
if (FILAMENT_SUPPORTS_WEBGPU)
|
||||
target_compile_definitions(${TARGET} PRIVATE $<$<BOOL:${FILAMENT_WEBGPU_IMMEDIATE_ERROR_HANDLING}>:FILAMENT_WEBGPU_IMMEDIATE_ERROR_HANDLING>)
|
||||
endif()
|
||||
|
||||
target_link_libraries(${TARGET} PRIVATE
|
||||
${OSMESA_LINKER_FLAGS}
|
||||
$<$<AND:$<PLATFORM_ID:Linux>,$<CONFIG:Release>>:${LINUX_LINKER_OPTIMIZATION_FLAGS}>
|
||||
|
||||
@@ -126,6 +126,10 @@ static_assert(MAX_VERTEX_BUFFER_COUNT <= MAX_VERTEX_ATTRIBUTE_COUNT,
|
||||
static constexpr size_t CONFIG_UNIFORM_BINDING_COUNT = 9; // This is guaranteed by OpenGL ES.
|
||||
static constexpr size_t CONFIG_SAMPLER_BINDING_COUNT = 4; // This is guaranteed by OpenGL ES.
|
||||
|
||||
static constexpr uint8_t EXTERNAL_SAMPLER_DATA_INDEX_UNUSED =
|
||||
uint8_t(-1);// Case where the descriptor set binding isnt using any external sampler state
|
||||
// and therefore doesn't have a valid entry.
|
||||
|
||||
/**
|
||||
* Defines the backend's feature levels.
|
||||
*/
|
||||
@@ -252,21 +256,19 @@ struct DescriptorSetLayoutBinding {
|
||||
DescriptorFlags flags = DescriptorFlags::NONE;
|
||||
uint16_t count = 0;
|
||||
|
||||
uint8_t externalSamplerDataIndex = EXTERNAL_SAMPLER_DATA_INDEX_UNUSED;
|
||||
|
||||
friend inline bool operator==(
|
||||
DescriptorSetLayoutBinding const& lhs,
|
||||
DescriptorSetLayoutBinding const& rhs) noexcept {
|
||||
return lhs.type == rhs.type &&
|
||||
lhs.flags == rhs.flags &&
|
||||
lhs.count == rhs.count &&
|
||||
lhs.stageFlags == rhs.stageFlags;
|
||||
lhs.stageFlags == rhs.stageFlags &&
|
||||
lhs.externalSamplerDataIndex == rhs.externalSamplerDataIndex;
|
||||
}
|
||||
};
|
||||
|
||||
struct DescriptorSetLayout {
|
||||
utils::FixedCapacityVector<DescriptorSetLayoutBinding> bindings;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Bitmask for selecting render buffers
|
||||
*/
|
||||
@@ -968,8 +970,28 @@ enum class SamplerCompareFunc : uint8_t {
|
||||
N //!< Never. The depth / stencil test always fails.
|
||||
};
|
||||
|
||||
//! this API is copied from (and only applies to) the Vulkan spec.
|
||||
//! These specify YUV to RGB conversions.
|
||||
enum class SamplerYcbcrModelConversion : uint8_t {
|
||||
RGB_IDENTITY = 0,
|
||||
YCBCR_IDENTITY = 1,
|
||||
YCBCR_709 = 2,
|
||||
YCBCR_601 = 3,
|
||||
YCBCR_2020 = 4,
|
||||
};
|
||||
|
||||
enum class SamplerYcbcrRange : uint8_t {
|
||||
ITU_FULL = 0,
|
||||
ITU_NARROW = 1,
|
||||
};
|
||||
|
||||
enum class ChromaLocation : uint8_t {
|
||||
COSITED_EVEN = 0,
|
||||
MIDPOINT = 1,
|
||||
};
|
||||
|
||||
//! Sampler parameters
|
||||
struct SamplerParams { // NOLINT
|
||||
struct SamplerParams { // NOLINT
|
||||
SamplerMagFilter filterMag : 1; //!< magnification filter (NEAREST)
|
||||
SamplerMinFilter filterMin : 3; //!< minification filter (NEAREST)
|
||||
SamplerWrapMode wrapS : 2; //!< s-coordinate wrap mode (CLAMP_TO_EDGE)
|
||||
@@ -1024,6 +1046,7 @@ private:
|
||||
return SamplerParams::LessThan{}(lhs, rhs);
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(SamplerParams) == 4);
|
||||
|
||||
// The limitation to 64-bits max comes from how we store a SamplerParams in our JNI code
|
||||
@@ -1031,6 +1054,93 @@ static_assert(sizeof(SamplerParams) == 4);
|
||||
static_assert(sizeof(SamplerParams) <= sizeof(uint64_t),
|
||||
"SamplerParams must be no more than 64 bits");
|
||||
|
||||
//! Sampler parameters
|
||||
struct SamplerYcbcrConversion {// NOLINT
|
||||
SamplerYcbcrModelConversion ycbcrModel : 4;
|
||||
TextureSwizzle r : 4;
|
||||
TextureSwizzle g : 4;
|
||||
TextureSwizzle b : 4;
|
||||
TextureSwizzle a : 4;
|
||||
SamplerYcbcrRange ycbcrRange : 1;
|
||||
ChromaLocation xChromaOffset : 1;
|
||||
ChromaLocation yChromaOffset : 1;
|
||||
SamplerMagFilter chromaFilter : 1;
|
||||
uint8_t padding;
|
||||
|
||||
struct Hasher {
|
||||
size_t operator()(const SamplerYcbcrConversion p) const noexcept {
|
||||
// we don't use std::hash<> here, so we don't have to include <functional>
|
||||
return *reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&p));
|
||||
}
|
||||
};
|
||||
|
||||
struct EqualTo {
|
||||
bool operator()(SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs) const noexcept {
|
||||
assert_invariant(lhs.padding == 0);
|
||||
auto* pLhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&lhs));
|
||||
auto* pRhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&rhs));
|
||||
return *pLhs == *pRhs;
|
||||
}
|
||||
};
|
||||
|
||||
struct LessThan {
|
||||
bool operator()(SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs) const noexcept {
|
||||
assert_invariant(lhs.padding == 0);
|
||||
auto* pLhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&lhs));
|
||||
auto* pRhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&rhs));
|
||||
return *pLhs < *pRhs;
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
friend inline bool operator == (SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs)
|
||||
noexcept {
|
||||
return SamplerYcbcrConversion::EqualTo{}(lhs, rhs);
|
||||
}
|
||||
friend inline bool operator != (SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs)
|
||||
noexcept {
|
||||
return !SamplerYcbcrConversion::EqualTo{}(lhs, rhs);
|
||||
}
|
||||
friend inline bool operator < (SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs)
|
||||
noexcept {
|
||||
return SamplerYcbcrConversion::LessThan{}(lhs, rhs);
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(SamplerYcbcrConversion) == 4);
|
||||
|
||||
static_assert(sizeof(SamplerYcbcrConversion) <= sizeof(uint64_t),
|
||||
"SamplerYcbcrConversion must be no more than 64 bits");
|
||||
|
||||
struct ExternalSamplerDatum {
|
||||
ExternalSamplerDatum(SamplerYcbcrConversion ycbcr, SamplerParams spm, uint32_t extFmt)
|
||||
: YcbcrConversion(ycbcr),
|
||||
samplerParams(spm),
|
||||
externalFormat(extFmt) {}
|
||||
bool operator==(ExternalSamplerDatum const& rhs) const {
|
||||
return (YcbcrConversion == rhs.YcbcrConversion && samplerParams == rhs.samplerParams &&
|
||||
externalFormat == rhs.externalFormat);
|
||||
}
|
||||
struct EqualTo {
|
||||
bool operator()(const ExternalSamplerDatum& lhs,
|
||||
const ExternalSamplerDatum& rhs) const noexcept {
|
||||
return (lhs.YcbcrConversion == rhs.YcbcrConversion &&
|
||||
lhs.samplerParams == rhs.samplerParams &&
|
||||
lhs.externalFormat == rhs.externalFormat);
|
||||
}
|
||||
};
|
||||
SamplerYcbcrConversion YcbcrConversion;
|
||||
SamplerParams samplerParams;
|
||||
uint32_t externalFormat;
|
||||
};
|
||||
// No implicit padding allowed due to it being a hash key.
|
||||
static_assert(sizeof(ExternalSamplerDatum) == 12);
|
||||
|
||||
struct DescriptorSetLayout {
|
||||
utils::FixedCapacityVector<DescriptorSetLayoutBinding> bindings;
|
||||
utils::FixedCapacityVector<ExternalSamplerDatum> externalSamplerData;
|
||||
};
|
||||
|
||||
//! blending equation function
|
||||
enum class BlendEquation : uint8_t {
|
||||
ADD, //!< the fragment is added to the color buffer
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#define TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORM_H
|
||||
|
||||
#include <backend/Platform.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <bluevk/BlueVK.h>
|
||||
|
||||
@@ -317,6 +318,11 @@ public:
|
||||
*/
|
||||
uint32_t layers;
|
||||
|
||||
/**
|
||||
* The numbers of samples per texel
|
||||
*/
|
||||
VkSampleCountFlagBits samples;
|
||||
|
||||
/**
|
||||
* The format of the external image
|
||||
*/
|
||||
@@ -351,7 +357,16 @@ public:
|
||||
|
||||
using ImageData = std::pair<VkImage, VkDeviceMemory>;
|
||||
virtual ImageData createExternalImageData(ExternalImageHandleRef externalImage,
|
||||
const ExternalImageMetadata& metadata);
|
||||
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex,
|
||||
VkImageUsageFlags usage);
|
||||
|
||||
virtual VkSampler createExternalSampler(SamplerYcbcrConversion chroma,
|
||||
SamplerParams sampler,
|
||||
uint32_t internalFormat);
|
||||
|
||||
virtual VkImageView createExternalImageView(SamplerYcbcrConversion chroma,
|
||||
uint32_t internalFormat, VkImage image, VkImageSubresourceRange range,
|
||||
VkImageViewType viewType, VkComponentMapping swizzle);
|
||||
|
||||
private:
|
||||
static ExtensionSet getSwapchainInstanceExtensions();
|
||||
@@ -360,7 +375,15 @@ private:
|
||||
VkDevice device);
|
||||
|
||||
static ImageData createExternalImageDataImpl(ExternalImageHandleRef externalImage,
|
||||
VkDevice device, const ExternalImageMetadata& metadata);
|
||||
VkDevice device, const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex,
|
||||
VkImageUsageFlags usage);
|
||||
static VkSampler createExternalSamplerImpl(VkDevice device,
|
||||
SamplerYcbcrConversion chroma, SamplerParams sampler,
|
||||
uint32_t internalFormat);
|
||||
static VkImageView createExternalImageViewImpl(VkDevice device,
|
||||
SamplerYcbcrConversion chroma, uint32_t internalFormat, VkImage image,
|
||||
VkImageSubresourceRange range, VkImageViewType viewType,
|
||||
VkComponentMapping swizzle);
|
||||
|
||||
// Platform dependent helper methods
|
||||
using SurfaceBundle = std::tuple<VkSurfaceKHR, VkExtent2D>;
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORM_H
|
||||
#define TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORM_H
|
||||
|
||||
#include <backend/Platform.h>
|
||||
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
/**
|
||||
* A Platform interface, handling the environment-specific concerns, e.g. OS, for creating a WebGPU
|
||||
* driver (backend).
|
||||
*/
|
||||
class WebGPUPlatform final : public Platform {
|
||||
public:
|
||||
WebGPUPlatform();
|
||||
~WebGPUPlatform() override = default;
|
||||
|
||||
[[nodiscard]] int getOSVersion() const noexcept final { return 0; }
|
||||
|
||||
[[nodiscard]] wgpu::Instance& getInstance() noexcept { return mInstance; }
|
||||
|
||||
// either returns a valid surface or panics
|
||||
[[nodiscard]] wgpu::Surface createSurface(void* nativeWindow, uint64_t flags);
|
||||
// either returns a valid adapter or panics
|
||||
[[nodiscard]] wgpu::Adapter requestAdapter(wgpu::Surface const& surface);
|
||||
// either returns a valid device or panics
|
||||
[[nodiscard]] wgpu::Device requestDevice(wgpu::Adapter const& adapter);
|
||||
|
||||
protected:
|
||||
[[nodiscard]] Driver* createDriver(void* sharedContext,
|
||||
const Platform::DriverConfig& driverConfig) noexcept override;
|
||||
|
||||
private:
|
||||
// we may consider having the driver own this in the future
|
||||
wgpu::Instance mInstance;
|
||||
};
|
||||
|
||||
}// namespace filament::backend
|
||||
|
||||
#endif// TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORM_H
|
||||
@@ -362,6 +362,8 @@ DECL_DRIVER_API_SYNCHRONOUS_0(bool, isProtectedTexturesSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isDepthClampSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(uint8_t, getMaxDrawBuffers)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(size_t, getMaxUniformBufferSize)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_N(size_t, getMaxTextureSize, backend::SamplerType, target)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(size_t, getMaxArrayTextureLayers)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(math::float2, getClipSpaceParams)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_N(void, setupExternalImage2, backend::Platform::ExternalImageHandleRef, image)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_N(void, setupExternalImage, void*, image)
|
||||
|
||||
@@ -66,7 +66,7 @@ filament::backend::Platform* createDefaultMetalPlatform();
|
||||
|
||||
#include "noop/PlatformNoop.h"
|
||||
#if defined(FILAMENT_SUPPORTS_WEBGPU)
|
||||
#include "webgpu/WebGPUPlatform.h"
|
||||
#include "backend/platforms/WebGPUPlatform.h"
|
||||
#endif
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
@@ -1177,6 +1177,16 @@ size_t MetalDriver::getMaxUniformBufferSize() {
|
||||
return 256 * 1024 * 1024; // TODO: return the actual size instead of hardcoding the minspec
|
||||
}
|
||||
|
||||
size_t MetalDriver::getMaxTextureSize(SamplerType) {
|
||||
// TODO: return the actual size instead of hardcoding the minspec
|
||||
return 2048;
|
||||
}
|
||||
|
||||
size_t MetalDriver::getMaxArrayTextureLayers() {
|
||||
// TODO: return the actual size instead of hardcoding the minspec
|
||||
return 256;
|
||||
}
|
||||
|
||||
void MetalDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor&& data,
|
||||
uint32_t byteOffset) {
|
||||
FILAMENT_CHECK_PRECONDITION(data.buffer)
|
||||
|
||||
@@ -240,6 +240,14 @@ size_t NoopDriver::getMaxUniformBufferSize() {
|
||||
return 16384u;
|
||||
}
|
||||
|
||||
size_t NoopDriver::getMaxTextureSize(SamplerType target) {
|
||||
return 2048u;
|
||||
}
|
||||
|
||||
size_t NoopDriver::getMaxArrayTextureLayers() {
|
||||
return 256u;
|
||||
}
|
||||
|
||||
void NoopDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor&& p,
|
||||
uint32_t byteOffset) {
|
||||
scheduleDestroy(std::move(p));
|
||||
|
||||
@@ -94,18 +94,22 @@ OpenGLContext::OpenGLContext(OpenGLPlatform& platform,
|
||||
}
|
||||
#endif
|
||||
|
||||
OpenGLContext::initExtensions(&ext, state.major, state.minor);
|
||||
initExtensions(&ext, state.major, state.minor);
|
||||
|
||||
OpenGLContext::initProcs(&procs, ext, state.major, state.minor);
|
||||
initProcs(&procs, ext, state.major, state.minor);
|
||||
|
||||
OpenGLContext::initBugs(&bugs, ext, state.major, state.minor,
|
||||
initBugs(&bugs, ext, state.major, state.minor,
|
||||
state.vendor, state.renderer, state.version, state.shader);
|
||||
|
||||
glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &gets.max_renderbuffer_size);
|
||||
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &gets.max_texture_image_units);
|
||||
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &gets.max_combined_texture_image_units);
|
||||
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &gets.max_texture_size);
|
||||
glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &gets.max_cubemap_texture_size);
|
||||
glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &gets.max_3d_texture_size);
|
||||
glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &gets.max_array_texture_layers);
|
||||
|
||||
mFeatureLevel = OpenGLContext::resolveFeatureLevel(state.major, state.minor, ext, gets, bugs);
|
||||
mFeatureLevel = resolveFeatureLevel(state.major, state.minor, ext, gets, bugs);
|
||||
|
||||
#ifdef BACKEND_OPENGL_VERSION_GLES
|
||||
mShaderModel = ShaderModel::MOBILE;
|
||||
@@ -177,6 +181,14 @@ OpenGLContext::OpenGLContext(OpenGLPlatform& platform,
|
||||
<< gets.max_anisotropy << '\n'
|
||||
<< "GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS = "
|
||||
<< gets.max_combined_texture_image_units << '\n'
|
||||
<< "GL_MAX_TEXTURE_SIZE = "
|
||||
<< gets.max_texture_size << '\n'
|
||||
<< "GL_MAX_CUBE_MAP_TEXTURE_SIZE = "
|
||||
<< gets.max_cubemap_texture_size << '\n'
|
||||
<< "GL_MAX_3D_TEXTURE_SIZE = "
|
||||
<< gets.max_3d_texture_size << '\n'
|
||||
<< "GL_MAX_ARRAY_TEXTURE_LAYERS = "
|
||||
<< gets.max_array_texture_layers << '\n'
|
||||
<< "GL_MAX_DRAW_BUFFERS = "
|
||||
<< gets.max_draw_buffers << '\n'
|
||||
<< "GL_MAX_RENDERBUFFER_SIZE = "
|
||||
|
||||
@@ -203,6 +203,10 @@ public:
|
||||
GLint max_renderbuffer_size;
|
||||
GLint max_samples;
|
||||
GLint max_texture_image_units;
|
||||
GLint max_texture_size;
|
||||
GLint max_cubemap_texture_size;
|
||||
GLint max_3d_texture_size;
|
||||
GLint max_array_texture_layers;
|
||||
GLint max_transform_feedback_separate_attribs;
|
||||
GLint max_uniform_block_size;
|
||||
GLint max_uniform_buffer_bindings;
|
||||
|
||||
@@ -2460,6 +2460,26 @@ size_t OpenGLDriver::getMaxUniformBufferSize() {
|
||||
return mContext.gets.max_uniform_block_size;
|
||||
}
|
||||
|
||||
size_t OpenGLDriver::getMaxTextureSize(SamplerType target) {
|
||||
switch (target) {
|
||||
case SamplerType::SAMPLER_2D:
|
||||
case SamplerType::SAMPLER_2D_ARRAY:
|
||||
case SamplerType::SAMPLER_EXTERNAL:
|
||||
return mContext.gets.max_texture_size;
|
||||
case SamplerType::SAMPLER_CUBEMAP:
|
||||
return mContext.gets.max_cubemap_texture_size;
|
||||
case SamplerType::SAMPLER_3D:
|
||||
return mContext.gets.max_3d_texture_size;
|
||||
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
|
||||
return mContext.gets.max_cubemap_texture_size;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t OpenGLDriver::getMaxArrayTextureLayers() {
|
||||
return mContext.gets.max_array_texture_layers;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// Swap chains
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -554,26 +554,43 @@ void VulkanDriver::createTextureViewSwizzleR(Handle<HwTexture> th, Handle<HwText
|
||||
texture.inc();
|
||||
}
|
||||
|
||||
void VulkanDriver::createTextureExternalImage2R(Handle<HwTexture> th,
|
||||
backend::SamplerType target, backend::TextureFormat format,
|
||||
uint32_t width, uint32_t height, backend::TextureUsage usage,
|
||||
void VulkanDriver::createTextureExternalImage2R(Handle<HwTexture> th, backend::SamplerType target,
|
||||
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
|
||||
Platform::ExternalImageHandleRef externalImage) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
|
||||
const auto& metadata = mPlatform->getExternalImageMetadata(externalImage);
|
||||
if (metadata.isProtected) {
|
||||
usage |= backend::TextureUsage::PROTECTED;
|
||||
}
|
||||
|
||||
VkImageUsageFlags vkUsage = metadata.usage;
|
||||
if (any(usage & TextureUsage::BLIT_SRC)) {
|
||||
vkUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||
}
|
||||
|
||||
if (any(usage & (TextureUsage::BLIT_DST & TextureUsage::UPLOADABLE))) {
|
||||
vkUsage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
}
|
||||
|
||||
assert_invariant(width == metadata.width);
|
||||
assert_invariant(height == metadata.height);
|
||||
assert_invariant(fvkutils::getVkFormat(format) == metadata.format);
|
||||
|
||||
const auto& data = mPlatform->createExternalImageData(externalImage, metadata);
|
||||
VkMemoryPropertyFlags const requiredMemoryFlags = any(usage & TextureUsage::UPLOADABLE)
|
||||
? VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
|
||||
: VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
||||
uint32_t const memoryTypeIndex =
|
||||
mContext.selectMemoryType(metadata.memoryTypeBits, requiredMemoryFlags);
|
||||
FILAMENT_CHECK_POSTCONDITION(memoryTypeIndex != VK_MAX_MEMORY_TYPES)
|
||||
<< "failed to find a valid memory type for external image memory.";
|
||||
|
||||
auto texture = resource_ptr<VulkanTexture>::make(&mResourceManager, th,
|
||||
mPlatform->getDevice(), mAllocator, &mResourceManager, &mCommands, data.first, data.second, metadata.format,
|
||||
1, metadata.width, metadata.height, /*depth=*/1, usage, mStagePool);
|
||||
const auto& data =
|
||||
mPlatform->createExternalImageData(externalImage, metadata, memoryTypeIndex, vkUsage);
|
||||
|
||||
auto texture = resource_ptr<VulkanTexture>::make(&mResourceManager, th, mPlatform->getDevice(),
|
||||
mAllocator, &mResourceManager, &mCommands, data.first, data.second, metadata.format,
|
||||
metadata.samples, metadata.width, metadata.height, metadata.layerCount, usage,
|
||||
mStagePool);
|
||||
|
||||
texture.inc();
|
||||
}
|
||||
@@ -1130,6 +1147,16 @@ size_t VulkanDriver::getMaxUniformBufferSize() {
|
||||
return 32768;
|
||||
}
|
||||
|
||||
size_t VulkanDriver::getMaxTextureSize(SamplerType) {
|
||||
// TODO: return the actual size instead of hardcoded value
|
||||
return 2048;
|
||||
}
|
||||
|
||||
size_t VulkanDriver::getMaxArrayTextureLayers() {
|
||||
// TODO: return the actual size instead of hardcoded value
|
||||
return 256;
|
||||
}
|
||||
|
||||
void VulkanDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh, uint32_t index,
|
||||
Handle<HwBufferObject> boh) {
|
||||
auto vb = resource_ptr<VulkanVertexBuffer>::cast(&mResourceManager, vbh);
|
||||
|
||||
@@ -23,79 +23,6 @@ using namespace bluevk;
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
constexpr inline VkSamplerAddressMode getWrapMode(SamplerWrapMode mode) noexcept {
|
||||
switch (mode) {
|
||||
case SamplerWrapMode::REPEAT:
|
||||
return VK_SAMPLER_ADDRESS_MODE_REPEAT;
|
||||
case SamplerWrapMode::CLAMP_TO_EDGE:
|
||||
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
||||
case SamplerWrapMode::MIRRORED_REPEAT:
|
||||
return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr inline VkFilter getFilter(SamplerMinFilter filter) noexcept {
|
||||
switch (filter) {
|
||||
case SamplerMinFilter::NEAREST:
|
||||
return VK_FILTER_NEAREST;
|
||||
case SamplerMinFilter::LINEAR:
|
||||
return VK_FILTER_LINEAR;
|
||||
case SamplerMinFilter::NEAREST_MIPMAP_NEAREST:
|
||||
return VK_FILTER_NEAREST;
|
||||
case SamplerMinFilter::LINEAR_MIPMAP_NEAREST:
|
||||
return VK_FILTER_LINEAR;
|
||||
case SamplerMinFilter::NEAREST_MIPMAP_LINEAR:
|
||||
return VK_FILTER_NEAREST;
|
||||
case SamplerMinFilter::LINEAR_MIPMAP_LINEAR:
|
||||
return VK_FILTER_LINEAR;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr inline VkFilter getFilter(SamplerMagFilter filter) noexcept {
|
||||
switch (filter) {
|
||||
case SamplerMagFilter::NEAREST:
|
||||
return VK_FILTER_NEAREST;
|
||||
case SamplerMagFilter::LINEAR:
|
||||
return VK_FILTER_LINEAR;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr inline VkSamplerMipmapMode getMipmapMode(SamplerMinFilter filter) noexcept {
|
||||
switch (filter) {
|
||||
case SamplerMinFilter::NEAREST:
|
||||
return VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
||||
case SamplerMinFilter::LINEAR:
|
||||
return VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
||||
case SamplerMinFilter::NEAREST_MIPMAP_NEAREST:
|
||||
return VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
||||
case SamplerMinFilter::LINEAR_MIPMAP_NEAREST:
|
||||
return VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
||||
case SamplerMinFilter::NEAREST_MIPMAP_LINEAR:
|
||||
return VK_SAMPLER_MIPMAP_MODE_LINEAR;
|
||||
case SamplerMinFilter::LINEAR_MIPMAP_LINEAR:
|
||||
return VK_SAMPLER_MIPMAP_MODE_LINEAR;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr inline float getMaxLod(SamplerMinFilter filter) noexcept {
|
||||
switch (filter) {
|
||||
case SamplerMinFilter::NEAREST:
|
||||
case SamplerMinFilter::LINEAR:
|
||||
// The Vulkan spec recommends a max LOD of 0.25 to "disable" mipmapping.
|
||||
// See "Mapping of OpenGL to Vulkan filter modes" in the VK Spec.
|
||||
return 0.25f;
|
||||
case SamplerMinFilter::NEAREST_MIPMAP_NEAREST:
|
||||
case SamplerMinFilter::LINEAR_MIPMAP_NEAREST:
|
||||
case SamplerMinFilter::NEAREST_MIPMAP_LINEAR:
|
||||
case SamplerMinFilter::LINEAR_MIPMAP_LINEAR:
|
||||
return VK_LOD_CLAMP_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr inline VkBool32 getCompareEnable(SamplerCompareMode mode) noexcept {
|
||||
return mode == SamplerCompareMode::NONE ? VK_FALSE : VK_TRUE;
|
||||
}
|
||||
|
||||
VulkanSamplerCache::VulkanSamplerCache(VkDevice device)
|
||||
: mDevice(device) {}
|
||||
|
||||
@@ -106,18 +33,18 @@ VkSampler VulkanSamplerCache::getSampler(SamplerParams params) noexcept {
|
||||
}
|
||||
VkSamplerCreateInfo samplerInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
||||
.magFilter = getFilter(params.filterMag),
|
||||
.minFilter = getFilter(params.filterMin),
|
||||
.mipmapMode = getMipmapMode(params.filterMin),
|
||||
.addressModeU = getWrapMode(params.wrapS),
|
||||
.addressModeV = getWrapMode(params.wrapT),
|
||||
.addressModeW = getWrapMode(params.wrapR),
|
||||
.anisotropyEnable = params.anisotropyLog2 == 0 ? 0u : 1u,
|
||||
.magFilter = fvkutils::getFilter(params.filterMag),
|
||||
.minFilter = fvkutils::getFilter(params.filterMin),
|
||||
.mipmapMode = fvkutils::getMipmapMode(params.filterMin),
|
||||
.addressModeU = fvkutils::getWrapMode(params.wrapS),
|
||||
.addressModeV = fvkutils::getWrapMode(params.wrapT),
|
||||
.addressModeW = fvkutils::getWrapMode(params.wrapR),
|
||||
.anisotropyEnable = params.anisotropyLog2 == 0 ? VK_FALSE : VK_TRUE,
|
||||
.maxAnisotropy = (float)(1u << params.anisotropyLog2),
|
||||
.compareEnable = getCompareEnable(params.compareMode),
|
||||
.compareEnable = fvkutils::getCompareEnable(params.compareMode),
|
||||
.compareOp = fvkutils::getCompareOp(params.compareFunc),
|
||||
.minLod = 0.0f,
|
||||
.maxLod = getMaxLod(params.filterMin),
|
||||
.maxLod = fvkutils::getMaxLod(params.filterMin),
|
||||
.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK,
|
||||
.unnormalizedCoordinates = VK_FALSE
|
||||
};
|
||||
|
||||
@@ -983,10 +983,23 @@ VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadata(
|
||||
}
|
||||
|
||||
VulkanPlatform::ImageData VulkanPlatform::createExternalImageData(
|
||||
ExternalImageHandleRef externalImage, const ExternalImageMetadata& metadata) {
|
||||
return createExternalImageDataImpl(externalImage, mImpl->mDevice, metadata);
|
||||
ExternalImageHandleRef externalImage, const ExternalImageMetadata& metadata,
|
||||
uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
|
||||
return createExternalImageDataImpl(externalImage, mImpl->mDevice, metadata, memoryTypeIndex,
|
||||
usage);
|
||||
}
|
||||
|
||||
VkSampler VulkanPlatform::createExternalSampler(SamplerYcbcrConversion chroma,
|
||||
SamplerParams sampler, uint32_t internalFormat) {
|
||||
return createExternalSamplerImpl(mImpl->mDevice, chroma, sampler, internalFormat);
|
||||
}
|
||||
|
||||
VkImageView VulkanPlatform::createExternalImageView(SamplerYcbcrConversion chroma,
|
||||
uint32_t internalFormat, VkImage image, VkImageSubresourceRange range,
|
||||
VkImageViewType viewType, VkComponentMapping swizzle) {
|
||||
return createExternalImageViewImpl(mImpl->mDevice, chroma, internalFormat, image, range,
|
||||
viewType, swizzle);
|
||||
}
|
||||
#undef SWAPCHAIN_RET_FUNC
|
||||
|
||||
}// namespace filament::backend
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
#include "vulkan/VulkanConstants.h"
|
||||
|
||||
#include <utils/Panic.h>
|
||||
#include "vulkan/utils/Image.h"
|
||||
#include "vulkan/utils/Conversion.h"
|
||||
|
||||
#include <bluevk/BlueVK.h>
|
||||
|
||||
@@ -136,7 +138,8 @@ std::pair<VkFormat, VkImageUsageFlags> getVKFormatAndUsage(const AHardwareBuffer
|
||||
}
|
||||
|
||||
VulkanPlatform::ImageData allocateExternalImage(AHardwareBuffer* buffer, VkDevice device,
|
||||
VulkanPlatform::ExternalImageMetadata const& metadata) {
|
||||
VulkanPlatform::ExternalImageMetadata const& metadata, uint32_t memoryTypeIndex,
|
||||
VkImageUsageFlags usage) {
|
||||
VulkanPlatform::ImageData data;
|
||||
|
||||
// if external format we need to specifiy it in the allocation
|
||||
@@ -145,8 +148,8 @@ VulkanPlatform::ImageData allocateExternalImage(AHardwareBuffer* buffer, VkDevic
|
||||
const VkExternalFormatANDROID externalFormat = {
|
||||
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
|
||||
.pNext = nullptr,
|
||||
.externalFormat = metadata
|
||||
.externalFormat,// pass down the format (external means we don't have it VK defined)
|
||||
// pass down the format (external means we don't have it VK defined)
|
||||
.externalFormat = metadata.externalFormat,
|
||||
};
|
||||
const VkExternalMemoryImageCreateInfo externalCreateInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO,
|
||||
@@ -165,10 +168,8 @@ VulkanPlatform::ImageData allocateExternalImage(AHardwareBuffer* buffer, VkDevic
|
||||
};
|
||||
imageInfo.mipLevels = 1;
|
||||
imageInfo.arrayLayers = metadata.layers;
|
||||
imageInfo.usage = metadata.usage;
|
||||
// In the unprotected case add R/W capabilities
|
||||
if (metadata.isProtected == false)
|
||||
imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||
imageInfo.samples = metadata.samples;
|
||||
imageInfo.usage = usage;
|
||||
|
||||
VkResult result = vkCreateImage(device, &imageInfo, VKALLOC, &data.first);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
@@ -186,10 +187,12 @@ VulkanPlatform::ImageData allocateExternalImage(AHardwareBuffer* buffer, VkDevic
|
||||
.image = data.first,
|
||||
.buffer = VK_NULL_HANDLE,
|
||||
};
|
||||
VkMemoryAllocateInfo allocInfo = {.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||
VkMemoryAllocateInfo allocInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||
.pNext = &memoryDedicatedAllocateInfo,
|
||||
.allocationSize = metadata.allocationSize,
|
||||
.memoryTypeIndex = metadata.memoryTypeBits};
|
||||
.memoryTypeIndex = memoryTypeIndex,
|
||||
};
|
||||
result = vkAllocateMemory(device, &allocInfo, VKALLOC, &data.second);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "vkAllocateMemory failed with error=" << static_cast<int32_t>(result);
|
||||
@@ -242,10 +245,8 @@ VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadataIm
|
||||
std::tie(metadata.format, metadata.usage) =
|
||||
getVKFormatAndUsage(bufferDesc, fvkExternalImage->sRGB);
|
||||
}
|
||||
// In the unprotected case add R/W capabilities
|
||||
if (metadata.isProtected == false) {
|
||||
metadata.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||
}
|
||||
|
||||
metadata.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
|
||||
VkAndroidHardwareBufferFormatPropertiesANDROID formatInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_ANDROID,
|
||||
@@ -272,16 +273,130 @@ VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadataIm
|
||||
|
||||
VulkanPlatform::ImageData VulkanPlatform::createExternalImageDataImpl(
|
||||
ExternalImageHandleRef externalImage, VkDevice device,
|
||||
const ExternalImageMetadata& metadata) {
|
||||
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
|
||||
auto const* fvkExternalImage =
|
||||
static_cast<fvkandroid::ExternalImageVulkanAndroid const*>(externalImage.get());
|
||||
ImageData data = allocateExternalImage(fvkExternalImage->aHardwareBuffer, device, metadata);
|
||||
ImageData data = allocateExternalImage(fvkExternalImage->aHardwareBuffer, device, metadata,
|
||||
memoryTypeIndex, usage);
|
||||
VkResult result = vkBindImageMemory(device, data.first, data.second, 0);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "vkBindImageMemory error=" << static_cast<int32_t>(result);
|
||||
<< "vkBindImageMemory error=" << static_cast<int32_t>(result);
|
||||
return data;
|
||||
}
|
||||
|
||||
VkImageView VulkanPlatform::createExternalImageViewImpl(VkDevice device, SamplerYcbcrConversion chroma,
|
||||
uint32_t internalFormat, VkImage image, VkImageSubresourceRange range,
|
||||
VkImageViewType viewType, VkComponentMapping swizzle){
|
||||
VkExternalFormatANDROID externalFormat = {
|
||||
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
|
||||
.pNext = nullptr,
|
||||
.externalFormat = internalFormat,
|
||||
};
|
||||
|
||||
TextureSwizzle const swizzleArray[] = {chroma.r, chroma.g, chroma.b, chroma.a};
|
||||
VkSamplerYcbcrConversionCreateInfo conversionInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO,
|
||||
.pNext = &externalFormat,
|
||||
.format = VK_FORMAT_UNDEFINED,
|
||||
.ycbcrModel = fvkutils::getYcbcrModelConversion(chroma.ycbcrModel),
|
||||
.ycbcrRange = fvkutils::getYcbcrRange(chroma.ycbcrRange),
|
||||
.components = fvkutils::getSwizzleMap(swizzleArray),
|
||||
.xChromaOffset = fvkutils::getChromaLocation(chroma.xChromaOffset),
|
||||
.yChromaOffset = fvkutils::getChromaLocation(chroma.yChromaOffset),
|
||||
.chromaFilter = fvkutils::getFilter(chroma.chromaFilter),
|
||||
};
|
||||
VkSamplerYcbcrConversion conversion = VK_NULL_HANDLE;
|
||||
VkResult result = vkCreateSamplerYcbcrConversion(device, &conversionInfo,
|
||||
nullptr, &conversion);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "Unable to create Ycbcr Conversion."
|
||||
<< " error=" << static_cast<int32_t>(result);
|
||||
|
||||
VkSamplerYcbcrConversionInfo samplerYcbcrConversionInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO,
|
||||
.pNext = nullptr,
|
||||
.conversion = conversion,
|
||||
};
|
||||
|
||||
VkImageViewCreateInfo viewInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
.pNext = &samplerYcbcrConversionInfo,
|
||||
.flags = 0,
|
||||
.image = image,
|
||||
.viewType = viewType,
|
||||
.format = VK_FORMAT_UNDEFINED,
|
||||
.components = swizzle,
|
||||
.subresourceRange = range,
|
||||
};
|
||||
VkImageView imageView;
|
||||
result = vkCreateImageView(device, &viewInfo, VKALLOC, &imageView);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "Unable to create VkImageView."
|
||||
<< " error=" << static_cast<int32_t>(result);
|
||||
|
||||
return imageView;
|
||||
}
|
||||
|
||||
VkSampler VulkanPlatform::createExternalSamplerImpl(
|
||||
VkDevice device, SamplerYcbcrConversion chroma, SamplerParams params,
|
||||
uint32_t internalFormat) {
|
||||
VkExternalFormatANDROID externalFormat = {
|
||||
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
|
||||
.pNext = nullptr,
|
||||
.externalFormat = internalFormat,
|
||||
};
|
||||
|
||||
TextureSwizzle const swizzleArray[] = {chroma.r, chroma.g, chroma.b, chroma.a};
|
||||
VkSamplerYcbcrConversionCreateInfo conversionInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO,
|
||||
.pNext = &externalFormat,
|
||||
.format = VK_FORMAT_UNDEFINED,
|
||||
.ycbcrModel = fvkutils::getYcbcrModelConversion(chroma.ycbcrModel),
|
||||
.ycbcrRange = fvkutils::getYcbcrRange(chroma.ycbcrRange),
|
||||
.components = fvkutils::getSwizzleMap(swizzleArray),
|
||||
.xChromaOffset = fvkutils::getChromaLocation(chroma.xChromaOffset),
|
||||
.yChromaOffset = fvkutils::getChromaLocation(chroma.yChromaOffset),
|
||||
.chromaFilter = fvkutils::getFilter(chroma.chromaFilter),
|
||||
};
|
||||
VkSamplerYcbcrConversion conversion = VK_NULL_HANDLE;
|
||||
VkResult result = vkCreateSamplerYcbcrConversion(device, &conversionInfo,
|
||||
nullptr, &conversion);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "Unable to create Ycbcr Conversion."
|
||||
<< " error=" << static_cast<int32_t>(result);
|
||||
|
||||
VkSamplerYcbcrConversionInfo samplerYcbcrConversionInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO,
|
||||
.pNext = nullptr,
|
||||
.conversion = conversion,
|
||||
};
|
||||
|
||||
VkSamplerCreateInfo samplerInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
||||
.pNext = &samplerYcbcrConversionInfo,
|
||||
.magFilter = fvkutils::getFilter(params.filterMag),
|
||||
.minFilter = fvkutils::getFilter(params.filterMin),
|
||||
.mipmapMode = fvkutils::getMipmapMode(params.filterMin),
|
||||
.addressModeU = fvkutils::getWrapMode(params.wrapS),
|
||||
.addressModeV = fvkutils::getWrapMode(params.wrapT),
|
||||
.addressModeW = fvkutils::getWrapMode(params.wrapR),
|
||||
.anisotropyEnable = params.anisotropyLog2 == 0 ? VK_FALSE : VK_TRUE,
|
||||
.maxAnisotropy = (float)(1u << params.anisotropyLog2),
|
||||
.compareEnable = fvkutils::getCompareEnable(params.compareMode),
|
||||
.compareOp = fvkutils::getCompareOp(params.compareFunc),
|
||||
.minLod = 0.0f,
|
||||
.maxLod = fvkutils::getMaxLod(params.filterMin),
|
||||
.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK,
|
||||
.unnormalizedCoordinates = VK_FALSE,
|
||||
};
|
||||
VkSampler sampler;
|
||||
result = vkCreateSampler(device, &samplerInfo, VKALLOC, &sampler);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "Unable to create sampler."
|
||||
<< " error=" << static_cast<int32_t>(result);
|
||||
return sampler;
|
||||
}
|
||||
|
||||
VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensions() {
|
||||
return {
|
||||
VK_KHR_ANDROID_SURFACE_EXTENSION_NAME,
|
||||
|
||||
@@ -70,10 +70,23 @@ VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadataIm
|
||||
|
||||
VulkanPlatform::ImageData VulkanPlatform::createExternalImageDataImpl(
|
||||
ExternalImageHandleRef externalImage, VkDevice device,
|
||||
const ExternalImageMetadata& metadata) {
|
||||
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
|
||||
return {};
|
||||
}
|
||||
|
||||
VkSampler VulkanPlatform::createExternalSamplerImpl(VkDevice device,
|
||||
SamplerYcbcrConversion chroma,
|
||||
SamplerParams sampler,
|
||||
uint32_t internalFormat) {
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
VkImageView VulkanPlatform::createExternalImageViewImpl(VkDevice device,
|
||||
SamplerYcbcrConversion chroma, uint32_t internalFormat, VkImage image,
|
||||
VkImageSubresourceRange range, VkImageViewType viewType, VkComponentMapping swizzle) {
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHR(void* nativeWindow,
|
||||
VkInstance instance, uint64_t flags) noexcept {
|
||||
VkSurfaceKHR surface;
|
||||
|
||||
@@ -91,10 +91,23 @@ VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadataIm
|
||||
|
||||
VulkanPlatform::ImageData VulkanPlatform::createExternalImageDataImpl(
|
||||
ExternalImageHandleRef externalImage, VkDevice device,
|
||||
const ExternalImageMetadata& metadata) {
|
||||
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
|
||||
return {};
|
||||
}
|
||||
|
||||
VkSampler VulkanPlatform::createExternalSamplerImpl(VkDevice device,
|
||||
SamplerYcbcrConversion chroma,
|
||||
SamplerParams sampler,
|
||||
uint32_t internalFormat) {
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
VkImageView VulkanPlatform::createExternalImageViewImpl(VkDevice device,
|
||||
SamplerYcbcrConversion chroma, uint32_t internalFormat, VkImage image,
|
||||
VkImageSubresourceRange range, VkImageViewType viewType, VkComponentMapping swizzle) {
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensions() {
|
||||
VulkanPlatform::ExtensionSet const ret = {
|
||||
#if defined(__linux__) && defined(FILAMENT_SUPPORTS_WAYLAND)
|
||||
|
||||
@@ -588,6 +588,79 @@ VkComponentMapping getSwizzleMap(TextureSwizzle const swizzle[4]) {
|
||||
return map;
|
||||
}
|
||||
|
||||
VkFilter getFilter(SamplerMinFilter filter) {
|
||||
switch (filter) {
|
||||
case SamplerMinFilter::NEAREST:
|
||||
return VK_FILTER_NEAREST;
|
||||
case SamplerMinFilter::LINEAR:
|
||||
return VK_FILTER_LINEAR;
|
||||
case SamplerMinFilter::NEAREST_MIPMAP_NEAREST:
|
||||
return VK_FILTER_NEAREST;
|
||||
case SamplerMinFilter::LINEAR_MIPMAP_NEAREST:
|
||||
return VK_FILTER_LINEAR;
|
||||
case SamplerMinFilter::NEAREST_MIPMAP_LINEAR:
|
||||
return VK_FILTER_NEAREST;
|
||||
case SamplerMinFilter::LINEAR_MIPMAP_LINEAR:
|
||||
return VK_FILTER_LINEAR;
|
||||
}
|
||||
}
|
||||
|
||||
VkFilter getFilter(SamplerMagFilter filter) {
|
||||
switch (filter) {
|
||||
case SamplerMagFilter::NEAREST:
|
||||
return VK_FILTER_NEAREST;
|
||||
case SamplerMagFilter::LINEAR:
|
||||
return VK_FILTER_LINEAR;
|
||||
}
|
||||
}
|
||||
|
||||
VkSamplerMipmapMode getMipmapMode(SamplerMinFilter filter) {
|
||||
switch (filter) {
|
||||
case SamplerMinFilter::NEAREST:
|
||||
return VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
||||
case SamplerMinFilter::LINEAR:
|
||||
return VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
||||
case SamplerMinFilter::NEAREST_MIPMAP_NEAREST:
|
||||
return VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
||||
case SamplerMinFilter::LINEAR_MIPMAP_NEAREST:
|
||||
return VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
||||
case SamplerMinFilter::NEAREST_MIPMAP_LINEAR:
|
||||
return VK_SAMPLER_MIPMAP_MODE_LINEAR;
|
||||
case SamplerMinFilter::LINEAR_MIPMAP_LINEAR:
|
||||
return VK_SAMPLER_MIPMAP_MODE_LINEAR;
|
||||
}
|
||||
}
|
||||
|
||||
VkSamplerAddressMode getWrapMode(SamplerWrapMode mode) {
|
||||
switch (mode) {
|
||||
case SamplerWrapMode::REPEAT:
|
||||
return VK_SAMPLER_ADDRESS_MODE_REPEAT;
|
||||
case SamplerWrapMode::CLAMP_TO_EDGE:
|
||||
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
||||
case SamplerWrapMode::MIRRORED_REPEAT:
|
||||
return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
|
||||
}
|
||||
}
|
||||
|
||||
VkBool32 getCompareEnable(SamplerCompareMode mode) {
|
||||
return mode == SamplerCompareMode::NONE ? VK_FALSE : VK_TRUE;
|
||||
}
|
||||
|
||||
float getMaxLod(SamplerMinFilter filter) {
|
||||
switch (filter) {
|
||||
case SamplerMinFilter::NEAREST:
|
||||
case SamplerMinFilter::LINEAR:
|
||||
// The Vulkan spec recommends a max LOD of 0.25 to "disable" mipmapping.
|
||||
// See "Mapping of OpenGL to Vulkan filter modes" in the VK Spec.
|
||||
return 0.25f;
|
||||
case SamplerMinFilter::NEAREST_MIPMAP_NEAREST:
|
||||
case SamplerMinFilter::LINEAR_MIPMAP_NEAREST:
|
||||
case SamplerMinFilter::NEAREST_MIPMAP_LINEAR:
|
||||
case SamplerMinFilter::LINEAR_MIPMAP_LINEAR:
|
||||
return VK_LOD_CLAMP_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
VkShaderStageFlags getShaderStageFlags(ShaderStageFlags stageFlags) {
|
||||
VkShaderStageFlags flags = 0x0;
|
||||
if (any(stageFlags & ShaderStageFlags::VERTEX)) flags |= VK_SHADER_STAGE_VERTEX_BIT;
|
||||
|
||||
@@ -56,6 +56,19 @@ uint32_t getComponentCount(VkFormat format);
|
||||
VkComponentMapping getSwizzleMap(TextureSwizzle const swizzle[4]);
|
||||
VkShaderStageFlags getShaderStageFlags(ShaderStageFlags stageFlags);
|
||||
|
||||
// Needed by the Platform for external sampler creation
|
||||
VkFilter getFilter(SamplerMinFilter filter);
|
||||
VkFilter getFilter(SamplerMagFilter filter);
|
||||
VkSamplerMipmapMode getMipmapMode(SamplerMinFilter filter);
|
||||
VkSamplerAddressMode getWrapMode(SamplerWrapMode mode);
|
||||
VkBool32 getCompareEnable(SamplerCompareMode mode);
|
||||
float getMaxLod(SamplerMinFilter filter);
|
||||
|
||||
// Ycbcr related functions
|
||||
VkSamplerYcbcrModelConversion getYcbcrModelConversion(SamplerYcbcrModelConversion model);
|
||||
VkSamplerYcbcrRange getYcbcrRange(SamplerYcbcrRange range);
|
||||
VkChromaLocation getChromaLocation(ChromaLocation loc);
|
||||
|
||||
inline VkImageViewType getViewType(SamplerType target) {
|
||||
switch (target) {
|
||||
case SamplerType::SAMPLER_CUBEMAP:
|
||||
|
||||
@@ -193,6 +193,49 @@ uint8_t reduceSampleCount(uint8_t sampleCount, VkSampleCountFlags mask) {
|
||||
return mostSignificantBit((sampleCount - 1) & mask);
|
||||
}
|
||||
|
||||
VkSamplerYcbcrModelConversion getYcbcrModelConversion(
|
||||
SamplerYcbcrModelConversion model) {
|
||||
switch (model) {
|
||||
case SamplerYcbcrModelConversion::RGB_IDENTITY:
|
||||
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY;
|
||||
case SamplerYcbcrModelConversion::YCBCR_IDENTITY:
|
||||
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY;
|
||||
case SamplerYcbcrModelConversion::YCBCR_709:
|
||||
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709;
|
||||
case SamplerYcbcrModelConversion::YCBCR_601:
|
||||
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601;
|
||||
case SamplerYcbcrModelConversion::YCBCR_2020:
|
||||
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020;
|
||||
default:
|
||||
assert_invariant(false &&
|
||||
"Unknown data type, conversion is not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
VkSamplerYcbcrRange getYcbcrRange(SamplerYcbcrRange range) {
|
||||
switch (range) {
|
||||
case SamplerYcbcrRange::ITU_FULL:
|
||||
return VK_SAMPLER_YCBCR_RANGE_ITU_FULL;
|
||||
case SamplerYcbcrRange::ITU_NARROW:
|
||||
return VK_SAMPLER_YCBCR_RANGE_ITU_NARROW;
|
||||
default:
|
||||
assert_invariant(false &&
|
||||
"Unknown data type, conversion is not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
VkChromaLocation getChromaLocation(ChromaLocation loc) {
|
||||
switch (loc) {
|
||||
case ChromaLocation::COSITED_EVEN:
|
||||
return VK_CHROMA_LOCATION_COSITED_EVEN;
|
||||
case ChromaLocation::MIDPOINT:
|
||||
return VK_CHROMA_LOCATION_MIDPOINT;
|
||||
default:
|
||||
assert_invariant(false &&
|
||||
"Unknown data type, conversion is not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace filament::backend::fvkutils
|
||||
|
||||
bool operator<(const VkImageSubresourceRange& a, const VkImageSubresourceRange& b) {
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_BACKEND_WEBGPUCONSTANTS_H
|
||||
#define TNT_FILAMENT_BACKEND_WEBGPUCONSTANTS_H
|
||||
|
||||
#include <utils/Log.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
// FWGPU is short for Filament WebGPU
|
||||
|
||||
// turn on runtime validation, namely for debugging, that would normally not run (for release)
|
||||
#define FWGPU_DEBUG_VALIDATION 0x00000001
|
||||
// print/log system component details to the console, e.g. about the
|
||||
// instance, surface, adapter, device, etc.
|
||||
#define FWGPU_PRINT_SYSTEM 0x00000002
|
||||
|
||||
// Set this to enable logging "only" to one output stream. This is useful in the case where we want
|
||||
// to debug with print statements and want ordered logging (e.g slog.i and slog.e will not appear in
|
||||
// order of calls).
|
||||
#define FWGPU_DEBUG_FORCE_LOG_TO_I 0x00000004
|
||||
|
||||
// Useful default combinations
|
||||
#define FWGPU_DEBUG_EVERYTHING 0xFFFFFFFF
|
||||
|
||||
#if defined(FILAMENT_BACKEND_DEBUG_FLAG)
|
||||
#define FWGPU_DEBUG_FORWARDED_FLAG (FILAMENT_BACKEND_DEBUG_FLAG & FWGPU_DEBUG_EVERYTHING)
|
||||
#else
|
||||
#define FWGPU_DEBUG_FORWARDED_FLAG 0
|
||||
#endif
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define FWGPU_DEBUG_FLAGS FWGPU_DEBUG_FORWARDED_FLAG
|
||||
#else
|
||||
#define FWGPU_DEBUG_FLAGS 0
|
||||
#endif
|
||||
|
||||
#define FWGPU_ENABLED(flags) (((FWGPU_DEBUG_FLAGS) & (flags)) == (flags))
|
||||
|
||||
#if FWGPU_ENABLED(FWGPU_DEBUG_FORCE_LOG_TO_I)
|
||||
#define FWGPU_LOGI (utils::slog.i)
|
||||
#define FWGPU_LOGD FWGPU_LOGI
|
||||
#define FWGPU_LOGE FWGPU_LOGI
|
||||
#define FWGPU_LOGW FWGPU_LOGI
|
||||
#else
|
||||
#define FWGPU_LOGE (utils::slog.e)
|
||||
#define FWGPU_LOGW (utils::slog.w)
|
||||
#define FWGPU_LOGD (utils::slog.d)
|
||||
#define FWGPU_LOGI (utils::slog.i)
|
||||
#endif
|
||||
|
||||
#endif// TNT_FILAMENT_BACKEND_WEBGPUCONSTANTS_H
|
||||
@@ -14,21 +14,254 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "webgpu/WebGPUDriver.h"
|
||||
|
||||
#include "webgpu/WebGPUConstants.h"
|
||||
#include <backend/platforms/WebGPUPlatform.h>
|
||||
|
||||
#include "CommandStreamDispatcher.h"
|
||||
#include "DriverBase.h"
|
||||
#include "private/backend/Dispatcher.h"
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
|
||||
#include "webgpu/WebGPUDriver.h"
|
||||
#include "CommandStreamDispatcher.h"
|
||||
#include <math/mat3.h>
|
||||
#include <utils/CString.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <dawn/webgpu_cpp_print.h>
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <sstream>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
Driver* WebGPUDriver::create() {
|
||||
return new WebGPUDriver();
|
||||
namespace {
|
||||
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
void printInstanceDetails(wgpu::Instance const& instance) {
|
||||
wgpu::SupportedWGSLLanguageFeatures supportedWGSLLanguageFeatures{};
|
||||
if (!instance.GetWGSLLanguageFeatures(&supportedWGSLLanguageFeatures)) {
|
||||
FWGPU_LOGW << "Failed to get WebGPU instance supported WGSL language features"
|
||||
<< utils::io::endl;
|
||||
} else {
|
||||
FWGPU_LOGI << "WebGPU instance supported WGSL language features ("
|
||||
<< supportedWGSLLanguageFeatures.featureCount << "):" << utils::io::endl;
|
||||
if (supportedWGSLLanguageFeatures.featureCount > 0 &&
|
||||
supportedWGSLLanguageFeatures.features != nullptr) {
|
||||
std::for_each(supportedWGSLLanguageFeatures.features,
|
||||
supportedWGSLLanguageFeatures.features +
|
||||
supportedWGSLLanguageFeatures.featureCount,
|
||||
[](wgpu::WGSLLanguageFeatureName const featureName) {
|
||||
std::stringstream nameStream{};
|
||||
nameStream << featureName;
|
||||
FWGPU_LOGI << " " << nameStream.str() << utils::io::endl;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
void printLimit(std::string_view name, const std::variant<uint32_t, uint64_t> value) {
|
||||
FWGPU_LOGI << " " << name.data() << ": ";
|
||||
bool undefined = true;
|
||||
if (std::holds_alternative<uint32_t>(value)) {
|
||||
if (std::get<uint32_t>(value) != WGPU_LIMIT_U32_UNDEFINED) {
|
||||
undefined = false;
|
||||
FWGPU_LOGI << std::get<uint32_t>(value);
|
||||
}
|
||||
} else if (std::holds_alternative<uint64_t>(value)) {
|
||||
if (std::get<uint64_t>(value) != WGPU_LIMIT_U64_UNDEFINED) {
|
||||
undefined = false;
|
||||
FWGPU_LOGI << std::get<uint64_t>(value);
|
||||
}
|
||||
}
|
||||
if (undefined) {
|
||||
FWGPU_LOGI << "UNDEFINED";
|
||||
}
|
||||
FWGPU_LOGI << utils::io::endl;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
void printLimits(wgpu::Limits const& limits) {
|
||||
printLimit("maxTextureDimension1D", limits.maxTextureDimension1D);
|
||||
printLimit("maxTextureDimension2D", limits.maxTextureDimension2D);
|
||||
printLimit("maxTextureDimension3D", limits.maxTextureDimension3D);
|
||||
printLimit("maxTextureArrayLayers", limits.maxTextureArrayLayers);
|
||||
printLimit("maxBindGroups", limits.maxBindGroups);
|
||||
printLimit("maxBindGroupsPlusVertexBuffers", limits.maxBindGroupsPlusVertexBuffers);
|
||||
printLimit("maxBindingsPerBindGroup", limits.maxBindingsPerBindGroup);
|
||||
printLimit("maxDynamicUniformBuffersPerPipelineLayout",
|
||||
limits.maxDynamicUniformBuffersPerPipelineLayout);
|
||||
printLimit("maxDynamicStorageBuffersPerPipelineLayout",
|
||||
limits.maxDynamicStorageBuffersPerPipelineLayout);
|
||||
printLimit("maxSampledTexturesPerShaderStage", limits.maxSampledTexturesPerShaderStage);
|
||||
printLimit("maxSamplersPerShaderStage", limits.maxSamplersPerShaderStage);
|
||||
printLimit("maxStorageBuffersPerShaderStage", limits.maxStorageBuffersPerShaderStage);
|
||||
printLimit("maxStorageTexturesPerShaderStage", limits.maxStorageTexturesPerShaderStage);
|
||||
printLimit("maxUniformBuffersPerShaderStage", limits.maxUniformBuffersPerShaderStage);
|
||||
printLimit("maxUniformBufferBindingSize", limits.maxUniformBufferBindingSize);
|
||||
printLimit("maxStorageBufferBindingSize", limits.maxStorageBufferBindingSize);
|
||||
printLimit("minUniformBufferOffsetAlignment", limits.minUniformBufferOffsetAlignment);
|
||||
printLimit("minStorageBufferOffsetAlignment", limits.minStorageBufferOffsetAlignment);
|
||||
printLimit("maxVertexBuffers", limits.maxVertexBuffers);
|
||||
printLimit("maxBufferSize", limits.maxBufferSize);
|
||||
printLimit("maxVertexAttributes", limits.maxVertexAttributes);
|
||||
printLimit("maxVertexBufferArrayStride", limits.maxVertexBufferArrayStride);
|
||||
printLimit("maxInterStageShaderComponents", limits.maxInterStageShaderComponents);
|
||||
printLimit("maxInterStageShaderVariables", limits.maxInterStageShaderVariables);
|
||||
printLimit("maxColorAttachments", limits.maxColorAttachments);
|
||||
printLimit("maxColorAttachmentBytesPerSample", limits.maxColorAttachmentBytesPerSample);
|
||||
printLimit("maxComputeWorkgroupStorageSize", limits.maxComputeWorkgroupStorageSize);
|
||||
printLimit("maxComputeInvocationsPerWorkgroup", limits.maxComputeInvocationsPerWorkgroup);
|
||||
printLimit("maxComputeWorkgroupSizeX", limits.maxComputeWorkgroupSizeX);
|
||||
printLimit("maxComputeWorkgroupSizeY", limits.maxComputeWorkgroupSizeY);
|
||||
printLimit("maxComputeWorkgroupSizeZ", limits.maxComputeWorkgroupSizeZ);
|
||||
printLimit("maxComputeWorkgroupsPerDimension", limits.maxComputeWorkgroupsPerDimension);
|
||||
printLimit("maxStorageBuffersInVertexStage", limits.maxStorageBuffersInVertexStage);
|
||||
printLimit("maxStorageTexturesInVertexStage", limits.maxStorageTexturesInVertexStage);
|
||||
printLimit("maxStorageBuffersInFragmentStage", limits.maxStorageBuffersInFragmentStage);
|
||||
printLimit("maxStorageTexturesInFragmentStage", limits.maxStorageTexturesInFragmentStage);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
void printAdapterDetails(wgpu::Adapter const& adapter) {
|
||||
wgpu::DawnAdapterPropertiesPowerPreference powerPreferenceProperties{};
|
||||
wgpu::AdapterInfo adapterInfo{};
|
||||
adapterInfo.nextInChain = &powerPreferenceProperties;
|
||||
if (!adapter.GetInfo(&adapterInfo)) {
|
||||
FWGPU_LOGW << "Failed to get WebGPU adapter info" << utils::io::endl;
|
||||
} else {
|
||||
std::stringstream backendTypeStream{};
|
||||
backendTypeStream << adapterInfo.backendType;
|
||||
std::stringstream adapterTypeStream{};
|
||||
adapterTypeStream << adapterInfo.adapterType;
|
||||
std::stringstream powerPreferenceStream{};
|
||||
powerPreferenceStream << powerPreferenceProperties.powerPreference;
|
||||
FWGPU_LOGI << "WebGPU adapter info:" << utils::io::endl;
|
||||
FWGPU_LOGI << " vendor: " << adapterInfo.vendor.data << utils::io::endl;
|
||||
FWGPU_LOGI << " architecture: " << adapterInfo.architecture.data << utils::io::endl;
|
||||
FWGPU_LOGI << " device: " << adapterInfo.device.data << utils::io::endl;
|
||||
FWGPU_LOGI << " description: " << adapterInfo.description.data << utils::io::endl;
|
||||
FWGPU_LOGI << " backend type: " << backendTypeStream.str().data() << utils::io::endl;
|
||||
FWGPU_LOGI << " adapter type: " << adapterTypeStream.str().data() << utils::io::endl;
|
||||
FWGPU_LOGI << " device ID: " << adapterInfo.deviceID << utils::io::endl;
|
||||
FWGPU_LOGI << " vendor ID: " << adapterInfo.vendorID << utils::io::endl;
|
||||
FWGPU_LOGI << " subgroup min size: " << adapterInfo.subgroupMinSize << utils::io::endl;
|
||||
FWGPU_LOGI << " subgroup max size: " << adapterInfo.subgroupMaxSize << utils::io::endl;
|
||||
FWGPU_LOGI << " compatibility mode: " << bool(adapterInfo.compatibilityMode)
|
||||
<< utils::io::endl;
|
||||
FWGPU_LOGI << " power preference: " << powerPreferenceStream.str() << utils::io::endl;
|
||||
}
|
||||
wgpu::SupportedFeatures supportedFeatures{};
|
||||
adapter.GetFeatures(&supportedFeatures);
|
||||
FWGPU_LOGI << "WebGPU adapter supported features (" << supportedFeatures.featureCount
|
||||
<< "):" << utils::io::endl;
|
||||
if (supportedFeatures.featureCount > 0 && supportedFeatures.features != nullptr) {
|
||||
std::for_each(supportedFeatures.features,
|
||||
supportedFeatures.features + supportedFeatures.featureCount,
|
||||
[](wgpu::FeatureName const name) {
|
||||
std::stringstream nameStream{};
|
||||
nameStream << name;
|
||||
FWGPU_LOGI << " " << nameStream.str().data() << utils::io::endl;
|
||||
});
|
||||
}
|
||||
wgpu::SupportedLimits supportedLimits{};
|
||||
if (!adapter.GetLimits(&supportedLimits)) {
|
||||
FWGPU_LOGW << "Failed to get WebGPU adapter supported limits" << utils::io::endl;
|
||||
} else {
|
||||
FWGPU_LOGI << "WebGPU adapter supported limits:" << utils::io::endl;
|
||||
printLimits(supportedLimits.limits);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
void printSurfaceCapabilitiesDetails(wgpu::SurfaceCapabilities const& capabilities) {
|
||||
std::stringstream usages_stream{};
|
||||
usages_stream << capabilities.usages;
|
||||
FWGPU_LOGI << "WebGPU surface capabilities:" << utils::io::endl;
|
||||
FWGPU_LOGI << " surface usages: " << usages_stream.str().data() << utils::io::endl;
|
||||
FWGPU_LOGI << " surface formats (" << capabilities.formatCount << "):" << utils::io::endl;
|
||||
if (capabilities.formatCount > 0 && capabilities.formats != nullptr) {
|
||||
std::for_each(capabilities.formats, capabilities.formats + capabilities.formatCount,
|
||||
[](wgpu::TextureFormat const format) {
|
||||
std::stringstream format_stream{};
|
||||
format_stream << format;
|
||||
FWGPU_LOGI << " " << format_stream.str().data() << utils::io::endl;
|
||||
});
|
||||
}
|
||||
FWGPU_LOGI << " surface present modes (" << capabilities.presentModeCount
|
||||
<< "):" << utils::io::endl;
|
||||
if (capabilities.presentModeCount > 0 && capabilities.presentModes != nullptr) {
|
||||
std::for_each(capabilities.presentModes,
|
||||
capabilities.presentModes + capabilities.presentModeCount,
|
||||
[](wgpu::PresentMode const presentMode) {
|
||||
std::stringstream present_mode_stream{};
|
||||
present_mode_stream << presentMode;
|
||||
FWGPU_LOGI << " " << present_mode_stream.str().data() << utils::io::endl;
|
||||
});
|
||||
}
|
||||
FWGPU_LOGI << " surface alpha modes (" << capabilities.alphaModeCount
|
||||
<< "):" << utils::io::endl;
|
||||
if (capabilities.alphaModeCount > 0 && capabilities.alphaModes != nullptr) {
|
||||
std::for_each(capabilities.alphaModes,
|
||||
capabilities.alphaModes + capabilities.alphaModeCount,
|
||||
[](wgpu::CompositeAlphaMode const alphaMode) {
|
||||
std::stringstream alpha_mode_stream{};
|
||||
alpha_mode_stream << alphaMode;
|
||||
FWGPU_LOGI << " " << alpha_mode_stream.str().data() << utils::io::endl;
|
||||
});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
void printDeviceDetails(wgpu::Device const& device) {
|
||||
wgpu::SupportedFeatures supportedFeatures{};
|
||||
device.GetFeatures(&supportedFeatures);
|
||||
FWGPU_LOGI << "WebGPU device supported features (" << supportedFeatures.featureCount
|
||||
<< "):" << utils::io::endl;
|
||||
if (supportedFeatures.featureCount > 0 && supportedFeatures.features != nullptr) {
|
||||
std::for_each(supportedFeatures.features,
|
||||
supportedFeatures.features + supportedFeatures.featureCount,
|
||||
[](wgpu::FeatureName const name) {
|
||||
std::stringstream nameStream{};
|
||||
nameStream << name;
|
||||
FWGPU_LOGI << " " << nameStream.str().data() << utils::io::endl;
|
||||
});
|
||||
}
|
||||
wgpu::SupportedLimits supportedLimits{};
|
||||
if (!device.GetLimits(&supportedLimits)) {
|
||||
FWGPU_LOGW << "Failed to get WebGPU supported device limits" << utils::io::endl;
|
||||
} else {
|
||||
FWGPU_LOGI << "WebGPU device supported limits:" << utils::io::endl;
|
||||
printLimits(supportedLimits.limits);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
}// namespace
|
||||
|
||||
Driver* WebGPUDriver::create(WebGPUPlatform& platform) noexcept {
|
||||
return new WebGPUDriver(platform);
|
||||
}
|
||||
|
||||
WebGPUDriver::WebGPUDriver() noexcept = default;
|
||||
WebGPUDriver::WebGPUDriver(WebGPUPlatform& platform) noexcept
|
||||
: mPlatform(platform) {
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
printInstanceDetails(mPlatform.getInstance());
|
||||
#endif
|
||||
}
|
||||
|
||||
WebGPUDriver::~WebGPUDriver() noexcept = default;
|
||||
|
||||
@@ -123,6 +356,188 @@ void WebGPUDriver::destroyDescriptorSetLayout(Handle<HwDescriptorSetLayout> tqh)
|
||||
void WebGPUDriver::destroyDescriptorSet(Handle<HwDescriptorSet> tqh) {
|
||||
}
|
||||
|
||||
Handle<HwSwapChain> WebGPUDriver::createSwapChainS() noexcept {
|
||||
return Handle<HwSwapChain>((Handle<HwSwapChain>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwSwapChain> WebGPUDriver::createSwapChainHeadlessS() noexcept {
|
||||
return Handle<HwSwapChain>((Handle<HwSwapChain>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwTexture> WebGPUDriver::createTextureS() noexcept {
|
||||
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwTexture> WebGPUDriver::importTextureS() noexcept {
|
||||
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwProgram> WebGPUDriver::createProgramS() noexcept {
|
||||
return Handle<HwProgram>((Handle<HwProgram>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwFence> WebGPUDriver::createFenceS() noexcept {
|
||||
return Handle<HwFence>((Handle<HwFence>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwTimerQuery> WebGPUDriver::createTimerQueryS() noexcept {
|
||||
return Handle<HwTimerQuery>((Handle<HwTimerQuery>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwIndexBuffer> WebGPUDriver::createIndexBufferS() noexcept {
|
||||
return Handle<HwIndexBuffer>((Handle<HwIndexBuffer>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwTexture> WebGPUDriver::createTextureViewS() noexcept {
|
||||
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwBufferObject> WebGPUDriver::createBufferObjectS() noexcept {
|
||||
return Handle<HwBufferObject>((Handle<HwBufferObject>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwRenderTarget> WebGPUDriver::createRenderTargetS() noexcept {
|
||||
return Handle<HwRenderTarget>((Handle<HwRenderTarget>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwVertexBuffer> WebGPUDriver::createVertexBufferS() noexcept {
|
||||
return Handle<HwVertexBuffer>((Handle<HwVertexBuffer>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwDescriptorSet> WebGPUDriver::createDescriptorSetS() noexcept {
|
||||
return Handle<HwDescriptorSet>((Handle<HwDescriptorSet>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwRenderPrimitive> WebGPUDriver::createRenderPrimitiveS() noexcept {
|
||||
return Handle<HwRenderPrimitive>((Handle<HwRenderPrimitive>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwVertexBufferInfo> WebGPUDriver::createVertexBufferInfoS() noexcept {
|
||||
return Handle<HwVertexBufferInfo>((Handle<HwVertexBufferInfo>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwTexture> WebGPUDriver::createTextureViewSwizzleS() noexcept {
|
||||
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwRenderTarget> WebGPUDriver::createDefaultRenderTargetS() noexcept {
|
||||
return Handle<HwRenderTarget>((Handle<HwRenderTarget>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwDescriptorSetLayout> WebGPUDriver::createDescriptorSetLayoutS() noexcept {
|
||||
return Handle<HwDescriptorSetLayout>(
|
||||
(Handle<HwDescriptorSetLayout>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwTexture> WebGPUDriver::createTextureExternalImageS() noexcept {
|
||||
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwTexture> WebGPUDriver::createTextureExternalImage2S() noexcept {
|
||||
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwTexture> WebGPUDriver::createTextureExternalImagePlaneS() noexcept {
|
||||
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
void WebGPUDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags) {
|
||||
mSurface= mPlatform.createSurface(nativeWindow, flags);
|
||||
mAdapter = mPlatform.requestAdapter(mSurface);
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
printAdapterDetails(mAdapter);
|
||||
wgpu::SurfaceCapabilities surfaceCapabilities{};
|
||||
if (!mSurface.GetCapabilities(mAdapter, &surfaceCapabilities)) {
|
||||
FWGPU_LOGW << "Failed to get WebGPU surface capabilities" << utils::io::endl;
|
||||
} else {
|
||||
printSurfaceCapabilitiesDetails(surfaceCapabilities);
|
||||
}
|
||||
#endif
|
||||
mDevice = mPlatform.requestDevice(mAdapter);
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
printDeviceDetails(mDevice);
|
||||
#endif
|
||||
mQueue = mDevice.GetQueue();
|
||||
// TODO configure the surface (maybe before or after creating the swapchain?
|
||||
// how do we get the surface extent?)
|
||||
// TODO actually create the swapchain
|
||||
FWGPU_LOGW << "WebGPU support is still essentially a no-op at this point in development (only "
|
||||
"background components have been instantiated/selected, such as surface/screen, "
|
||||
"graphics device/GPU, etc.), thus nothing is being drawn to the screen."
|
||||
<< utils::io::endl;
|
||||
#if !FWGPU_ENABLED(FWGPU_PRINT_SYSTEM) && !defined(NDEBUG)
|
||||
FWGPU_LOGI << "If the FILAMENT_BACKEND_DEBUG_FLAG variable were set with the " << utils::io::hex
|
||||
<< FWGPU_PRINT_SYSTEM << utils::io::dec
|
||||
<< " bit flag on during build time the application would print system details "
|
||||
"about the selected graphics device, surface, etc. To see this try "
|
||||
"rebuilding Filament with that flag, e.g. ./build.sh -x "
|
||||
<< FWGPU_PRINT_SYSTEM << " ..." << utils::io::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
void WebGPUDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch, uint32_t width,
|
||||
uint32_t height, uint64_t flags) {}
|
||||
|
||||
void WebGPUDriver::createVertexBufferInfoR(Handle<HwVertexBufferInfo> vbih, uint8_t bufferCount,
|
||||
uint8_t attributeCount, AttributeArray attributes) {}
|
||||
|
||||
void WebGPUDriver::createVertexBufferR(Handle<HwVertexBuffer> vbh, uint32_t vertexCount,
|
||||
Handle<HwVertexBufferInfo> vbih) {}
|
||||
|
||||
void WebGPUDriver::createIndexBufferR(Handle<HwIndexBuffer> ibh, ElementType elementType,
|
||||
uint32_t indexCount, BufferUsage usage) {}
|
||||
|
||||
void WebGPUDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byteCount,
|
||||
BufferObjectBinding bindingType, BufferUsage usage) {}
|
||||
|
||||
void WebGPUDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint8_t levels,
|
||||
TextureFormat format, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth,
|
||||
TextureUsage usage) {}
|
||||
|
||||
void WebGPUDriver::createTextureViewR(Handle<HwTexture> th, Handle<HwTexture> srch,
|
||||
uint8_t baseLevel, uint8_t levelCount) {}
|
||||
|
||||
void WebGPUDriver::createTextureViewSwizzleR(Handle<HwTexture> th, Handle<HwTexture> srch,
|
||||
backend::TextureSwizzle r, backend::TextureSwizzle g, backend::TextureSwizzle b,
|
||||
backend::TextureSwizzle a) {}
|
||||
|
||||
void WebGPUDriver::createTextureExternalImage2R(Handle<HwTexture> th, backend::SamplerType target,
|
||||
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
|
||||
Platform::ExternalImageHandleRef externalImage) {}
|
||||
|
||||
void WebGPUDriver::createTextureExternalImageR(Handle<HwTexture> th, backend::SamplerType target,
|
||||
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
|
||||
void* externalImage) {}
|
||||
|
||||
void WebGPUDriver::createTextureExternalImagePlaneR(Handle<HwTexture> th,
|
||||
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
|
||||
void* image, uint32_t plane) {}
|
||||
|
||||
void WebGPUDriver::importTextureR(Handle<HwTexture> th, intptr_t id, SamplerType target,
|
||||
uint8_t levels, TextureFormat format, uint8_t samples, uint32_t w, uint32_t h,
|
||||
uint32_t depth, TextureUsage usage) {}
|
||||
|
||||
void WebGPUDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph, Handle<HwVertexBuffer> vbh,
|
||||
Handle<HwIndexBuffer> ibh, PrimitiveType pt) {}
|
||||
|
||||
void WebGPUDriver::createProgramR(Handle<HwProgram> ph, Program&& program) {}
|
||||
|
||||
void WebGPUDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, int) {}
|
||||
|
||||
void WebGPUDriver::createRenderTargetR(Handle<HwRenderTarget> rth, TargetBufferFlags targets,
|
||||
uint32_t width, uint32_t height, uint8_t samples, uint8_t layerCount, MRT color,
|
||||
TargetBufferInfo depth, TargetBufferInfo stencil) {}
|
||||
|
||||
void WebGPUDriver::createFenceR(Handle<HwFence> fh, int) {}
|
||||
|
||||
void WebGPUDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {}
|
||||
|
||||
void WebGPUDriver::createDescriptorSetLayoutR(Handle<HwDescriptorSetLayout> dslh,
|
||||
backend::DescriptorSetLayout&& info) {}
|
||||
|
||||
void WebGPUDriver::createDescriptorSetR(Handle<HwDescriptorSet> dsh,
|
||||
Handle<HwDescriptorSetLayout> dslh) {}
|
||||
|
||||
Handle<HwStream> WebGPUDriver::createStreamNative(void* nativeStream) {
|
||||
return {};
|
||||
}
|
||||
@@ -236,10 +651,18 @@ uint8_t WebGPUDriver::getMaxDrawBuffers() {
|
||||
return MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT;
|
||||
}
|
||||
|
||||
size_t WebGPUDriver::getMaxUniformBufferSize() {
|
||||
size_t WebGPUDriver::getMaxUniformBufferSize(SamplerType) {
|
||||
return 16384u;
|
||||
}
|
||||
|
||||
size_t WebGPUDriver::getMaxTextureSize() {
|
||||
return 2048u;
|
||||
}
|
||||
|
||||
size_t WebGPUDriver::getMaxArrayTextureLayers() {
|
||||
return 256u;
|
||||
}
|
||||
|
||||
void WebGPUDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor&& p,
|
||||
uint32_t byteOffset) {
|
||||
scheduleDestroy(std::move(p));
|
||||
|
||||
@@ -17,26 +17,44 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_WEBGPUDRIVER_H
|
||||
#define TNT_FILAMENT_BACKEND_WEBGPUDRIVER_H
|
||||
|
||||
#include "private/backend/Driver.h"
|
||||
#include <backend/platforms/WebGPUPlatform.h>
|
||||
|
||||
#include "DriverBase.h"
|
||||
#include "private/backend/Dispatcher.h"
|
||||
#include "private/backend/Driver.h"
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
/**
|
||||
* WebGPU backend (driver) implementation
|
||||
*/
|
||||
class WebGPUDriver final : public DriverBase {
|
||||
WebGPUDriver() noexcept;
|
||||
~WebGPUDriver() noexcept override;
|
||||
Dispatcher getDispatcher() const noexcept final;
|
||||
|
||||
public:
|
||||
static Driver* create();
|
||||
~WebGPUDriver() noexcept override;
|
||||
|
||||
[[nodiscard]] Dispatcher getDispatcher() const noexcept final;
|
||||
[[nodiscard]] static Driver* create(WebGPUPlatform& platform) noexcept;
|
||||
|
||||
private:
|
||||
ShaderModel getShaderModel() const noexcept final;
|
||||
ShaderLanguage getShaderLanguage() const noexcept final;
|
||||
explicit WebGPUDriver(WebGPUPlatform& platform) noexcept;
|
||||
[[nodiscard]] ShaderModel getShaderModel() const noexcept final;
|
||||
[[nodiscard]] ShaderLanguage getShaderLanguage() const noexcept final;
|
||||
|
||||
uint64_t nextFakeHandle = 1;
|
||||
// the platform (e.g. OS) specific aspects of the WebGPU backend are strictly only
|
||||
// handled in the WebGPUPlatform
|
||||
WebGPUPlatform& mPlatform;
|
||||
wgpu::Surface mSurface = nullptr;
|
||||
wgpu::Adapter mAdapter = nullptr;
|
||||
wgpu::Device mDevice = nullptr;
|
||||
wgpu::Queue mQueue = nullptr;
|
||||
uint64_t mNextFakeHandle = 1;
|
||||
|
||||
/*
|
||||
* Driver interface
|
||||
@@ -45,20 +63,19 @@ private:
|
||||
template<typename T>
|
||||
friend class ConcreteDispatcher;
|
||||
|
||||
#define DECL_DRIVER_API(methodName, paramsDecl, params) \
|
||||
#define DECL_DRIVER_API(methodName, paramsDecl, params) \
|
||||
UTILS_ALWAYS_INLINE inline void methodName(paramsDecl);
|
||||
|
||||
#define DECL_DRIVER_API_SYNCHRONOUS(RetType, methodName, paramsDecl, params) \
|
||||
#define DECL_DRIVER_API_SYNCHRONOUS(RetType, methodName, paramsDecl, params) \
|
||||
RetType methodName(paramsDecl) override;
|
||||
|
||||
#define DECL_DRIVER_API_RETURN(RetType, methodName, paramsDecl, params) \
|
||||
RetType methodName##S() noexcept override { \
|
||||
return RetType((RetType::HandleId)nextFakeHandle++); } \
|
||||
UTILS_ALWAYS_INLINE inline void methodName##R(RetType, paramsDecl) { }
|
||||
#define DECL_DRIVER_API_RETURN(RetType, methodName, paramsDecl, params) \
|
||||
RetType methodName##S() noexcept override; \
|
||||
UTILS_ALWAYS_INLINE inline void methodName##R(RetType, paramsDecl);
|
||||
|
||||
#include "private/backend/DriverAPI.inc"
|
||||
};
|
||||
|
||||
} // namespace filament
|
||||
}// namespace filament::backend
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_WEBGPUDRIVER_H
|
||||
#endif// TNT_FILAMENT_BACKEND_WEBGPUDRIVER_H
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "webgpu/WebGPUPlatform.h"
|
||||
#include "webgpu/WebGPUDriver.h"
|
||||
|
||||
#include "utils/Log.h"
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
Driver* WebGPUPlatform::createDriver(void* const sharedContext , const Platform::DriverConfig& driverConfig) noexcept {
|
||||
wgpu::InstanceDescriptor instance_descriptor {};
|
||||
wgpu::Instance instance = wgpu::CreateInstance(&instance_descriptor);
|
||||
if (instance) {
|
||||
utils::slog.i << " WebGPU instance created\n";
|
||||
} else {
|
||||
utils::slog.e << " WebGPU instance failed to create\n";
|
||||
return nullptr;
|
||||
}
|
||||
return WebGPUDriver::create();
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_BACKEND_WEBGPUPLATFORM_H
|
||||
#define TNT_FILAMENT_BACKEND_WEBGPUPLATFORM_H
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Platform.h>
|
||||
|
||||
#include "webgpu/webgpu_cpp.h"
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class WebGPUPlatform
|
||||
final : public Platform {
|
||||
public:
|
||||
|
||||
int getOSVersion() const noexcept final { return 0; }
|
||||
|
||||
~WebGPUPlatform() noexcept override = default;
|
||||
|
||||
protected:
|
||||
|
||||
Driver* createDriver(void* sharedContext, const Platform::DriverConfig& driverConfig) noexcept override;
|
||||
};
|
||||
|
||||
} // namespace filament
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_WEBGPUPLATFORM_H
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <backend/platforms/WebGPUPlatform.h>
|
||||
|
||||
#include "webgpu/WebGPUConstants.h"
|
||||
#include "webgpu/WebGPUDriver.h"
|
||||
|
||||
#include <backend/Platform.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#include <dawn/webgpu_cpp_print.h>
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <sstream>
|
||||
|
||||
/**
|
||||
* WebGPU Backend implementation common across platforms or operating systems (at least for now).
|
||||
* Some of these functions may likely be refactored to platform/OS-specific implementations
|
||||
* over time as needed. The caller of the WebGPUPlatform doesn't need to care which is the case.
|
||||
*/
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
namespace {
|
||||
|
||||
//either returns a valid instance or panics
|
||||
[[nodiscard]] wgpu::Instance createInstance() {
|
||||
wgpu::DawnTogglesDescriptor dawnTogglesDescriptor{};
|
||||
#if defined(FILAMENT_WEBGPU_IMMEDIATE_ERROR_HANDLING)
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
FWGPU_LOGI << "setting on toggle enable_immediate_error_handling" << utils::io::endl;
|
||||
#endif
|
||||
/**
|
||||
* Have the un-captured error callback invoked immediately when an error occurs, rather than
|
||||
* waiting for the next Tick. This enables using the stack trace in which the un-captured error
|
||||
* occurred when breaking into the un-captured error callback.
|
||||
* https://crbug.com/dawn/1789
|
||||
*/
|
||||
static const char* toggleName = "enable_immediate_error_handling";
|
||||
dawnTogglesDescriptor.enabledToggleCount = 1;
|
||||
dawnTogglesDescriptor.enabledToggles = &toggleName;
|
||||
#endif
|
||||
wgpu::InstanceDescriptor instanceDescriptor{
|
||||
.nextInChain = &dawnTogglesDescriptor,
|
||||
.capabilities = {
|
||||
.timedWaitAnyEnable = true// TODO consider using pure async instead
|
||||
}
|
||||
};
|
||||
wgpu::Instance instance = wgpu::CreateInstance(&instanceDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(instance != nullptr) << "Unable to create webgpu instance.";
|
||||
return instance;
|
||||
}
|
||||
|
||||
}// namespace
|
||||
|
||||
wgpu::Adapter WebGPUPlatform::requestAdapter(wgpu::Surface const& surface) {
|
||||
// TODO consider power preference etc. (can be custom preferences passed to the platform or
|
||||
// based on whether this is a Mobile or Desktop system,
|
||||
// etc...)
|
||||
wgpu::RequestAdapterOptions adaptorOptions{ .compatibleSurface = surface };
|
||||
// note this just gets the first adapter
|
||||
wgpu::Adapter adapter = nullptr;
|
||||
wgpu::WaitStatus status = mInstance.WaitAny(
|
||||
mInstance.RequestAdapter(&adaptorOptions, wgpu::CallbackMode::WaitAnyOnly,
|
||||
[&adapter](wgpu::RequestAdapterStatus const status,
|
||||
wgpu::Adapter const& readyAdapter, wgpu::StringView const message) {
|
||||
// TODO consider more robust error handling
|
||||
FILAMENT_CHECK_POSTCONDITION(status == wgpu::RequestAdapterStatus::Success)
|
||||
<< "Unable to request a WebGPU adapter. Status "
|
||||
<< static_cast<uint32_t>(status)
|
||||
<< " with message: " << message.data;
|
||||
adapter = readyAdapter;
|
||||
}),
|
||||
UINT16_MAX);// TODO define reasonable timeout (or do this asynchronously)
|
||||
FILAMENT_CHECK_POSTCONDITION(status == wgpu::WaitStatus::Success)
|
||||
<< "Non-successful wait status requesting a WebGPU adapter "
|
||||
<< static_cast<uint32_t>(status);
|
||||
FILAMENT_CHECK_POSTCONDITION(adapter != nullptr)
|
||||
<< "Failed to get a WebGPU adapter for the platform";
|
||||
// TODO consider validating adapter has required features and/or limits
|
||||
return adapter;
|
||||
}
|
||||
|
||||
wgpu::Device WebGPUPlatform::requestDevice(wgpu::Adapter const& adapter) {
|
||||
// TODO consider passing required features and/or limits
|
||||
wgpu::DeviceDescriptor deviceDescriptor{};
|
||||
deviceDescriptor.label = "graphics_device";
|
||||
deviceDescriptor.defaultQueue.label = "default_queue";
|
||||
deviceDescriptor.SetDeviceLostCallback(wgpu::CallbackMode::AllowSpontaneous,
|
||||
[](wgpu::Device const&, wgpu::DeviceLostReason const& reason,
|
||||
wgpu::StringView message) {
|
||||
if (reason == wgpu::DeviceLostReason::Destroyed) {
|
||||
FWGPU_LOGD << "WebGPU device lost due to being destroyed (expected)"
|
||||
<< utils::io::endl;
|
||||
return;
|
||||
}
|
||||
// TODO try recreating the device instead of just panicking
|
||||
std::stringstream reasonStream{};
|
||||
reasonStream << reason;
|
||||
FILAMENT_CHECK_POSTCONDITION(reason != wgpu::DeviceLostReason::Destroyed)
|
||||
<< "WebGPU device lost: " << reasonStream.str() << " " << message.data;
|
||||
});
|
||||
deviceDescriptor.SetUncapturedErrorCallback(
|
||||
[](wgpu::Device const&, wgpu::ErrorType errorType, wgpu::StringView message) {
|
||||
std::stringstream typeStream{};
|
||||
typeStream << errorType;
|
||||
FWGPU_LOGE << "WebGPU device error: " << typeStream.str() << " " << message.data
|
||||
<< utils::io::endl;
|
||||
});
|
||||
wgpu::Device device = nullptr;
|
||||
wgpu::WaitStatus status = mInstance.WaitAny(
|
||||
adapter.RequestDevice(&deviceDescriptor, wgpu::CallbackMode::WaitAnyOnly,
|
||||
[&device](wgpu::RequestDeviceStatus const status,
|
||||
wgpu::Device const& readyDevice, wgpu::StringView const message) {
|
||||
FILAMENT_CHECK_POSTCONDITION(status == wgpu::RequestDeviceStatus::Success)
|
||||
<< "Unable to request a WebGPU device. Status: "
|
||||
<< static_cast<uint32_t>(status)
|
||||
<< " with message: " << message.data;
|
||||
device = readyDevice;
|
||||
}),
|
||||
UINT64_MAX);// TODO define reasonable timeout (or do this asynchronously)
|
||||
FILAMENT_CHECK_POSTCONDITION(status == wgpu::WaitStatus::Success)
|
||||
<< "Non-successful wait status requesting a WebGPU device "
|
||||
<< static_cast<uint32_t>(status);
|
||||
FILAMENT_CHECK_POSTCONDITION(device != nullptr)
|
||||
<< "Failed to get a WebGPU device for the platform.";
|
||||
return device;
|
||||
}
|
||||
|
||||
Driver* WebGPUPlatform::createDriver(void* const sharedContext,
|
||||
const Platform::DriverConfig& /*driverConfig*/) noexcept {
|
||||
if (sharedContext) {
|
||||
FWGPU_LOGW << "sharedContext is ignored/unused in the WebGPU backend. A non-null "
|
||||
"sharedContext was provided, but it will be ignored."
|
||||
<< utils::io::endl;
|
||||
}
|
||||
return WebGPUDriver::create(*this);
|
||||
}
|
||||
|
||||
WebGPUPlatform::WebGPUPlatform()
|
||||
: mInstance(createInstance()) {}
|
||||
|
||||
}// namespace filament::backend
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <backend/platforms/WebGPUPlatform.h>
|
||||
|
||||
#include <utils/Panic.h>
|
||||
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
/**
|
||||
* Android OS specific implementation aspects of the WebGPU backend
|
||||
*/
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t /*flags*/) {
|
||||
wgpu::SurfaceSourceAndroidNativeWindow surfaceSourceAndroidWindow{};
|
||||
surfaceSourceAndroidWindow.window = nativeWindow;
|
||||
wgpu::SurfaceDescriptor surfaceDescriptor{
|
||||
.nextInChain = &surfaceSourceAndroidWindow,
|
||||
.label = "android_surface"
|
||||
};
|
||||
wgpu::Surface surface = mInstance.CreateSurface(&surfaceDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(surface != nullptr) << "Unable to create Android-backed surface.";
|
||||
return surface;
|
||||
}
|
||||
|
||||
}// namespace filament::backend
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <backend/platforms/WebGPUPlatform.h>
|
||||
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
// Platform specific includes and defines
|
||||
#if defined(__APPLE__)
|
||||
#include <Cocoa/Cocoa.h>
|
||||
#import <QuartzCore/CAMetalLayer.h>
|
||||
#elif defined(FILAMENT_IOS)
|
||||
// Metal is not available when building for the iOS simulator on Desktop.
|
||||
#define METAL_AVAILABLE __has_include(<QuartzCore/CAMetalLayer.h>)
|
||||
#if METAL_AVAILABLE
|
||||
#import <Metal/Metal.h>
|
||||
#import <QuartzCore/CAMetalLayer.h>
|
||||
#endif
|
||||
// is this needed?
|
||||
#define METALVIEW_TAG 255
|
||||
#else
|
||||
#error Not a supported Apple + WebGPU platform
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Apple (Mac OS and IOS) specific implementation aspects of the WebGPU backend
|
||||
*/
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t /*flags*/) {
|
||||
wgpu::Surface surface = nullptr;
|
||||
#if defined(__APPLE__)
|
||||
auto nsView = (__bridge NSView*) nativeWindow;
|
||||
FILAMENT_CHECK_POSTCONDITION(nsView) << "Unable to obtain Metal-backed NSView.";
|
||||
[nsView setWantsLayer:YES];
|
||||
id metalLayer = [CAMetalLayer layer];
|
||||
[nsView setLayer:metalLayer];
|
||||
wgpu::SurfaceSourceMetalLayer surfaceSourceMetalLayer{};
|
||||
surfaceSourceMetalLayer.layer = (__bridge void*) metalLayer;
|
||||
wgpu::SurfaceDescriptor surfaceDescriptor = {
|
||||
.nextInChain = &surfaceSourceMetalLayer,
|
||||
.label = "metal_surface",
|
||||
};
|
||||
surface = mInstance.CreateSurface(&surfaceDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(surface != nullptr) << "Unable to create Metal-backed surface.";
|
||||
#elif defined(FILAMENT_IOS)
|
||||
CAMetalLayer* metalLayer = (CAMetalLayer*) nativeWindow;
|
||||
wgpu::SurfaceSourceMetalLayer surfaceSourceMetalLayer{};
|
||||
surfaceSourceMetalLayer.layer = (__bridge void*) metalLayer;
|
||||
wgpu::SurfaceDescriptor surfaceDescriptor = {
|
||||
.nextInChain = &surfaceSourceMetalLayer,
|
||||
.label = "metal_surface",
|
||||
};
|
||||
surface = mInstance.CreateSurface(&surfaceDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(surface != nullptr) << "Unable to create Metal-backed surface.";
|
||||
#else
|
||||
#error Not a supported Apple + WebGPU platform
|
||||
#endif
|
||||
return surface;
|
||||
}
|
||||
|
||||
}// namespace filament::backend
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#include <backend/platforms/WebGPUPlatform.h>
|
||||
|
||||
#include <utils/Panic.h>
|
||||
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#if defined(__linux__) || defined(__FreeBSD__)
|
||||
#define LINUX_OR_FREEBSD 1
|
||||
#endif
|
||||
|
||||
// Platform specific includes and defines
|
||||
#if defined(__linux__) && defined(FILAMENT_SUPPORTS_WAYLAND)
|
||||
#include <dlfcn.h>
|
||||
namespace {
|
||||
typedef struct _wl {
|
||||
struct wl_display* display;
|
||||
struct wl_surface* surface;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
} wl;
|
||||
}// namespace
|
||||
#elif defined(LINUX_OR_FREEBSD) && defined(FILAMENT_SUPPORTS_X11)
|
||||
// TODO: we should allow for headless on Linux explicitly. Right now this is the headless path
|
||||
// (with no FILAMENT_SUPPORTS_XCB or FILAMENT_SUPPORTS_XLIB).
|
||||
#include <dlfcn.h>
|
||||
#if defined(FILAMENT_SUPPORTS_XCB)
|
||||
#include <xcb/xcb.h>
|
||||
namespace {
|
||||
typedef xcb_connection_t* (*XCB_CONNECT)(const char* displayname, int* screenp);
|
||||
}// namespace
|
||||
#endif
|
||||
#if defined(FILAMENT_SUPPORTS_XLIB)
|
||||
#include <X11/Xlib.h>
|
||||
namespace {
|
||||
typedef Display* (*X11_OPEN_DISPLAY)(const char*);
|
||||
}// namespace
|
||||
#endif
|
||||
static constexpr const char* LIBRARY_X11 = "libX11.so.6";
|
||||
namespace {
|
||||
struct XEnv {
|
||||
#if defined(FILAMENT_SUPPORTS_XCB)
|
||||
XCB_CONNECT xcbConnect;
|
||||
xcb_connection_t* connection;
|
||||
#endif
|
||||
#if defined(FILAMENT_SUPPORTS_XLIB)
|
||||
X11_OPEN_DISPLAY openDisplay;
|
||||
Display* display;
|
||||
#endif
|
||||
void* library = nullptr;
|
||||
} g_x11;
|
||||
}// namespace
|
||||
#else
|
||||
#error Not a supported Linux or FeeBSD + WebGPU platform
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Linux OS specific implementation aspects of the WebGPU Backend
|
||||
*/
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t flags) {
|
||||
wgpu::Surface surface = nullptr;
|
||||
#if defined(__linux__) && defined(FILAMENT_SUPPORTS_WAYLAND)
|
||||
wl* ptrval = reinterpret_cast<wl*>(nativeWindow);
|
||||
wgpu::SurfaceSourceWaylandSurface surfaceSourceWayland{};
|
||||
surfaceSourceWayland.display = ptrval->display;
|
||||
surfaceSourceWayland.surface = ptrval->surface;
|
||||
wgpu::SurfaceDescriptor surfaceDescriptor{
|
||||
.nextInChain = &surfaceSourceWayland,
|
||||
.label = "linux_wayland_surface"
|
||||
};
|
||||
surface = mInstance.CreateSurface(&surfaceDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(surface != nullptr)
|
||||
<< "Unable to create Linux Wayland-backed surface.";
|
||||
#elif defined(LINUX_OR_FREEBSD) && defined(FILAMENT_SUPPORTS_X11)
|
||||
if (g_x11.library == nullptr) {
|
||||
g_x11.library = dlopen(LIBRARY_X11, RTLD_LOCAL | RTLD_NOW);
|
||||
FILAMENT_CHECK_PRECONDITION(g_x11.library) << "Unable to open X11 library.";
|
||||
#if defined(FILAMENT_SUPPORTS_XCB)
|
||||
g_x11.xcbConnect = (XCB_CONNECT) dlsym(g_x11.library, "xcb_connect");
|
||||
int screen = 0;
|
||||
g_x11.connection = g_x11.xcbConnect(nullptr, &screen);
|
||||
#endif
|
||||
#if defined(FILAMENT_SUPPORTS_XLIB)
|
||||
g_x11.openDisplay = (X11_OPEN_DISPLAY) dlsym(g_x11.library, "XOpenDisplay");
|
||||
g_x11.display = g_x11.openDisplay(NULL);
|
||||
FILAMENT_CHECK_PRECONDITION(g_x11.display) << "Unable to open X11 display.";
|
||||
#endif
|
||||
}
|
||||
#if defined(FILAMENT_SUPPORTS_XCB) || defined(FILAMENT_SUPPORTS_XLIB)
|
||||
bool useXcb = false;
|
||||
#endif
|
||||
#if defined(FILAMENT_SUPPORTS_XCB)
|
||||
#if defined(FILAMENT_SUPPORTS_XLIB)
|
||||
useXcb = (flags & SWAP_CHAIN_CONFIG_ENABLE_XCB) != 0;
|
||||
#else
|
||||
useXcb = true;
|
||||
#endif
|
||||
if (useXcb) {
|
||||
wgpu::SurfaceSourceXCBWindow surfaceSourceXcb{};
|
||||
surfaceSourceXcb.connection = g_x11.connection;
|
||||
surfaceSourceXcb.window = reinterpret_cast<uint32_t>(nativeWindow);
|
||||
wgpu::SurfaceDescriptor surfaceDescriptor{
|
||||
.nextInChain = &surfaceSourceXcb,
|
||||
.label = "linux_xcb_surface"
|
||||
};
|
||||
surface = mInstance.CreateSurface(&surfaceDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(surface != nullptr)
|
||||
<< "Unable to create Linux (or FreeBSD) XCB-backed surface.";
|
||||
}
|
||||
#endif
|
||||
#if defined(FILAMENT_SUPPORTS_XLIB)
|
||||
if (!useXcb) {
|
||||
wgpu::SurfaceSourceXlibWindow surfaceSourceXlib{};
|
||||
surfaceSourceXlib.display = g_x11.display;
|
||||
surfaceSourceXlib.window = reinterpret_cast<uint64_t>(nativeWindow);
|
||||
wgpu::SurfaceDescriptor surfaceDescriptor{
|
||||
.nextInChain = &surfaceSourceXlib,
|
||||
.label = "linux_xlib_surface"
|
||||
};
|
||||
surface = mInstance.CreateSurface(&surfaceDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(surface != nullptr)
|
||||
<< "Unable to create Linux (or FreeBSD) XLib-backed surface.";
|
||||
}
|
||||
#endif
|
||||
FILAMENT_CHECK_POSTCONDITION(surface != nullptr)
|
||||
<< "Cannot create WebGPU X11 surface for Linux (or FreeBSD) OS "
|
||||
"(not built with support for XCB or XLIB?)";
|
||||
#elif defined(__linux__)
|
||||
FILAMENT_CHECK_POSTCONDITION(surface != nullptr)
|
||||
<< "Cannot create WebGPU surface for Linux (or FreeBSD) OS "
|
||||
"(not built with support for Wayland or X11?)";
|
||||
#else
|
||||
FILAMENT_CHECK_POSTCONDITION(surface != nullptr)
|
||||
<< "Not a supported (Linux) OS + WebGPU platform";
|
||||
#endif
|
||||
return surface;
|
||||
}
|
||||
|
||||
}// namespace filament::backend
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <backend/platforms/WebGPUPlatform.h>
|
||||
|
||||
#include <utils/Panic.h>
|
||||
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
/**
|
||||
* Windows OS specific implementation aspects of the WebGPU backend
|
||||
*/
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t /*flags*/) {
|
||||
// TODO verify this is necessary for Dawn implementation as well:
|
||||
// On (at least) NVIDIA drivers, the Vulkan implementation (specifically the call to
|
||||
// vkGetPhysicalDeviceSurfaceCapabilitiesKHR()) does not correctly handle the fact that
|
||||
// each native window has its own DPI_AWARENESS_CONTEXT, and erroneously uses the context
|
||||
// of the calling thread. As a workaround, we set the current thread's DPI_AWARENESS_CONTEXT
|
||||
// to that of the native window we've been given. This isn't a perfect solution, because an
|
||||
// application could create swap chains on multiple native windows with varying DPI-awareness,
|
||||
// but even then, at least one of the windows would be guaranteed to work correctly.
|
||||
SetThreadDpiAwarenessContext(GetWindowDpiAwarenessContext((HWND) nativeWindow));
|
||||
wgpu::SurfaceSourceWindowsHWND surfaceSourceWin{};
|
||||
surfaceSourceWin.hinstance = GetModuleHandle(nullptr);
|
||||
surfaceSourceWin.hwnd = nativeWindow;
|
||||
wgpu::SurfaceDescriptor surfaceDescriptor{
|
||||
.nextInChain = &surfaceSourceWin,
|
||||
.label = "windows_surface"
|
||||
};
|
||||
wgpu::Surface surface = mInstance.CreateSurface(&surfaceDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(surface != nullptr) << "Unable to create Windows-backed surface.";
|
||||
return surface;
|
||||
}
|
||||
|
||||
}// namespace filament::backend
|
||||
@@ -106,6 +106,13 @@ public:
|
||||
/** @return Whether a combination of texture format, pixel format and type is valid. */
|
||||
static bool validatePixelFormatAndType(InternalFormat internalFormat, Format format, Type type) noexcept;
|
||||
|
||||
/** @return the maximum size in texels of a texture of type \p type. At least 2048 for
|
||||
* 2D textures, 256 for 3D textures. */
|
||||
static size_t getMaxTextureSize(Engine& engine, Sampler type) noexcept;
|
||||
|
||||
/** @return the maximum number of layers supported by texture arrays. At least 256. */
|
||||
static size_t getMaxArrayTextureLayers(Engine& engine) noexcept;
|
||||
|
||||
/**
|
||||
* Options for environment prefiltering into reflection map
|
||||
*
|
||||
|
||||
@@ -108,4 +108,12 @@ bool Texture::validatePixelFormatAndType(InternalFormat internalFormat, Format f
|
||||
return FTexture::validatePixelFormatAndType(internalFormat, format, type);
|
||||
}
|
||||
|
||||
size_t Texture::getMaxTextureSize(Engine& engine, Sampler type) noexcept {
|
||||
return FTexture::getMaxTextureSize(downcast(engine), type);
|
||||
|
||||
}
|
||||
size_t Texture::getMaxArrayTextureLayers(Engine& engine) noexcept {
|
||||
return FTexture::getMaxArrayTextureLayers(downcast(engine));
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -164,6 +164,20 @@ Texture* Texture::Builder::build(Engine& engine) {
|
||||
(isProtectedTexturesSupported && useProtectedMemory) || !useProtectedMemory)
|
||||
<< "Texture is PROTECTED but protected textures are not supported";
|
||||
|
||||
size_t const maxTextureDimension = getMaxTextureSize(engine, mImpl->mTarget);
|
||||
size_t const maxTextureDepth = (mImpl->mTarget == Sampler::SAMPLER_2D_ARRAY ||
|
||||
mImpl->mTarget == Sampler::SAMPLER_CUBEMAP_ARRAY)
|
||||
? getMaxArrayTextureLayers(engine)
|
||||
: maxTextureDimension;
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(
|
||||
mImpl->mWidth <= maxTextureDimension &&
|
||||
mImpl->mHeight <= maxTextureDimension &&
|
||||
mImpl->mDepth <= maxTextureDepth) << "Texture dimensions out of range: "
|
||||
<< "width= " << mImpl->mWidth << " (>" << maxTextureDimension << ")"
|
||||
<<", height= " << mImpl->mHeight << " (>" << maxTextureDimension << ")"
|
||||
<< ", depth= " << mImpl->mDepth << " (>" << maxTextureDepth << ")";
|
||||
|
||||
const auto validateSamplerType = [&engine = downcast(engine)](SamplerType const sampler) -> bool {
|
||||
switch (sampler) {
|
||||
case SamplerType::SAMPLER_2D:
|
||||
@@ -672,6 +686,14 @@ bool FTexture::isTextureSwizzleSupported(FEngine& engine) noexcept {
|
||||
return engine.getDriverApi().isTextureSwizzleSupported();
|
||||
}
|
||||
|
||||
size_t FTexture::getMaxTextureSize(FEngine& engine, Sampler type) noexcept {
|
||||
return engine.getDriverApi().getMaxTextureSize(type);
|
||||
}
|
||||
|
||||
size_t FTexture::getMaxArrayTextureLayers(FEngine& engine) noexcept {
|
||||
return engine.getDriverApi().getMaxArrayTextureLayers();
|
||||
}
|
||||
|
||||
size_t FTexture::computeTextureDataSize(Format const format, Type const type,
|
||||
size_t const stride, size_t const height, size_t const alignment) noexcept {
|
||||
return PixelBufferDescriptor::computeDataSize(format, type, stride, height, alignment);
|
||||
|
||||
@@ -124,6 +124,10 @@ public:
|
||||
static bool validatePixelFormatAndType(backend::TextureFormat internalFormat,
|
||||
backend::PixelDataFormat format, backend::PixelDataType type) noexcept;
|
||||
|
||||
static size_t getMaxTextureSize(FEngine& engine, Sampler type) noexcept;
|
||||
|
||||
static size_t getMaxArrayTextureLayers(FEngine& engine) noexcept;
|
||||
|
||||
bool textureHandleCanMutate() const noexcept;
|
||||
void updateLodRange(uint8_t level) noexcept;
|
||||
|
||||
|
||||
@@ -78,6 +78,10 @@ fragment {
|
||||
void dummy(){}
|
||||
|
||||
vec4 resolveFragment() {
|
||||
#ifdef TARGET_WEBGPU_ENVIRONMENT
|
||||
//WebGPU Can not handle subpasses.
|
||||
return vec4(0,0,0,1);
|
||||
#else
|
||||
#if POST_PROCESS_OPAQUE
|
||||
return vec4(subpassLoad(materialParams_colorBuffer).rgb, 1.0);
|
||||
#else
|
||||
@@ -85,6 +89,7 @@ vec4 resolveFragment() {
|
||||
color.rgb *= 1.0 / (color.a + FLT_EPS);
|
||||
return color;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void postProcess(inout PostProcessInputs postProcess) {
|
||||
|
||||
@@ -39,11 +39,16 @@ fragment {
|
||||
void dummy(){}
|
||||
|
||||
vec4 resolveFragment() {
|
||||
#ifdef TARGET_WEBGPU_ENVIRONMENT
|
||||
//WebGPU Can not handle subpasses.
|
||||
return vec4(0,0,0,1);
|
||||
#else
|
||||
#if POST_PROCESS_OPAQUE
|
||||
return vec4(subpassLoad(materialParams_colorBuffer).rgb, 1.0);
|
||||
#else
|
||||
return subpassLoad(materialParams_colorBuffer);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void postProcess(inout PostProcessInputs postProcess) {
|
||||
|
||||
@@ -144,6 +144,12 @@ set(FILAMAT_LIB_NAME ${CMAKE_STATIC_LIBRARY_PREFIX}filamat${CMAKE_STATIC_LIBRARY
|
||||
install(FILES "${FILAMAT_COMBINED_OUTPUT}" DESTINATION lib/${DIST_DIR} RENAME ${FILAMAT_LIB_NAME})
|
||||
install(DIRECTORY ${PUBLIC_HDR_DIR}/filamat DESTINATION include)
|
||||
|
||||
# Need to install libtint for filamat on Android.
|
||||
# See libs/filamat/CMakeLists.txt and android/filamat-android/CMakeLists.txt.
|
||||
if (ANDROID)
|
||||
target_link_libraries(${TARGET} libtint_combined)
|
||||
endif()
|
||||
|
||||
# ==================================================================================================
|
||||
# Tests
|
||||
# ==================================================================================================
|
||||
|
||||
@@ -518,8 +518,13 @@ void GLSLPostProcessor::spirvToMsl(const SpirvBlob* spirv, std::string* outMsl,
|
||||
}
|
||||
}
|
||||
|
||||
bool GLSLPostProcessor::spirvToWgsl(const SpirvBlob *spirv, std::string *outWsl) {
|
||||
bool GLSLPostProcessor::spirvToWgsl(SpirvBlob *spirv, std::string *outWsl) {
|
||||
#if FILAMENT_SUPPORTS_WEBGPU
|
||||
// We need to remove dead code for our variant-filter workaround for WebGPU to work
|
||||
// This is especially relevant for removing the push constants that morphing uses when not disabled
|
||||
spv::spirvbin_t remapper(0);
|
||||
remapper.remap(*spirv, spv::spirvbin_base_t::DCE_ALL);
|
||||
|
||||
//Currently no options we want to use
|
||||
const tint::spirv::reader::Options readerOpts{};
|
||||
tint::wgsl::writer::Options writerOpts{};
|
||||
@@ -530,6 +535,7 @@ bool GLSLPostProcessor::spirvToWgsl(const SpirvBlob *spirv, std::string *outWsl)
|
||||
//TODO remove this if block once combined image sampler conversion works.
|
||||
if (tintRead.Diagnostics().Str().rfind("error: WGSL does not support combined image-samplers") != std::string::npos) {
|
||||
slog.w << "This tint reader error is currently ignored during WebGPU bringup: " << tintRead.Diagnostics().Str() << io::endl;
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
slog.e << "Tint Reader Error: " << tintRead.Diagnostics().Str() << io::endl;
|
||||
@@ -914,6 +920,10 @@ std::shared_ptr<spvtools::Optimizer> GLSLPostProcessor::createOptimizer(
|
||||
optimizer->RegisterPass(CreateRedundancyEliminationPass());
|
||||
optimizer->RegisterPass(CreateAggressiveDCEPass());
|
||||
}
|
||||
if (config.targetApi == MaterialBuilder::TargetApi::WEBGPU) {
|
||||
optimizer->RegisterPass(CreateSplitCombinedImageSamplerPass());
|
||||
optimizer->RegisterPass(CreateResolveBindingConflictsPass());
|
||||
}
|
||||
|
||||
return optimizer;
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ public:
|
||||
bool useFramebufferFetch, const DescriptorSets& descriptorSets,
|
||||
const ShaderMinifier* minifier);
|
||||
|
||||
static bool spirvToWgsl(const SpirvBlob* spirv, std::string* outWsl);
|
||||
static bool spirvToWgsl(SpirvBlob* spirv, std::string* outWsl);
|
||||
|
||||
private:
|
||||
struct InternalConfig {
|
||||
|
||||
@@ -178,6 +178,7 @@ utils::io::sstream& CodeGenerator::generateCommonProlog(utils::io::sstream& out,
|
||||
//For now, no differences so inherit the same changes.
|
||||
// TODO Define a separte environment, OR relevant checks
|
||||
out << "#define TARGET_VULKAN_ENVIRONMENT\n";
|
||||
out << "#define TARGET_WEBGPU_ENVIRONMENT\n";
|
||||
break;
|
||||
case TargetApi::ALL:
|
||||
// invalid should never happen
|
||||
|
||||
@@ -796,8 +796,10 @@ std::string ShaderGenerator::createPostProcessFragmentProgram(ShaderModel sm,
|
||||
|
||||
cg.generateCommonSamplers(fs, DescriptorSetBindingPoints::PER_MATERIAL, material.sib);
|
||||
|
||||
// subpass
|
||||
CodeGenerator::generatePostProcessSubpass(fs, material.subpass);
|
||||
// Subpasses are not yet in WebGPU, https://github.com/gpuweb/gpuweb/issues/435
|
||||
CodeGenerator::generatePostProcessSubpass(fs, targetApi == MaterialBuilderBase::TargetApi::WEBGPU
|
||||
? SubpassInfo{}
|
||||
: material.subpass);
|
||||
|
||||
CodeGenerator::generatePostProcessCommon(fs, ShaderStage::FRAGMENT);
|
||||
CodeGenerator::generatePostProcessGetters(fs, ShaderStage::FRAGMENT);
|
||||
|
||||
+28
-2
@@ -20,7 +20,6 @@ set(MATERIAL_SRCS
|
||||
materials/aiDefaultMat.mat
|
||||
materials/bakedColor.mat
|
||||
materials/bakedTexture.mat
|
||||
materials/pointSprites.mat
|
||||
materials/aoPreview.mat
|
||||
materials/arrayTexture.mat
|
||||
materials/groundShadow.mat
|
||||
@@ -42,6 +41,12 @@ set(MATERIAL_SRCS
|
||||
materials/texturedLit.mat
|
||||
)
|
||||
|
||||
# Tint does not support setting gl_PointSize, disable the relevant sample if using WebGPU
|
||||
# https://github.com/gpuweb/gpuweb/issues/1190
|
||||
if (NOT FILAMENT_SUPPORTS_WEBGPU)
|
||||
set(MATERIAL_SRCS ${MATERIAL_SRCS} materials/pointSprites.mat)
|
||||
endif ()
|
||||
|
||||
if (CMAKE_CROSSCOMPILING)
|
||||
include(${IMPORT_EXECUTABLES})
|
||||
endif()
|
||||
@@ -240,6 +245,20 @@ function(add_demo NAME)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(add_demo_plain NAME)
|
||||
include_directories(${GENERATION_ROOT})
|
||||
add_executable(${NAME} ${NAME}.cpp)
|
||||
target_compile_options(${NAME} PRIVATE ${COMPILER_FLAGS})
|
||||
set_target_properties(${NAME} PROPERTIES FOLDER Samples)
|
||||
|
||||
# This is needed after XCode 15.3
|
||||
if (APPLE)
|
||||
set_target_properties(${NAME} PROPERTIES BUILD_WITH_INSTALL_RPATH TRUE)
|
||||
set_target_properties(${NAME} PROPERTIES INSTALL_RPATH /usr/local/lib)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
|
||||
if (NOT ANDROID)
|
||||
add_demo(animation)
|
||||
add_demo(depthtesting)
|
||||
@@ -258,7 +277,11 @@ if (NOT ANDROID)
|
||||
add_demo(lightbulb)
|
||||
add_demo(material_sandbox)
|
||||
add_demo(multiple_windows)
|
||||
add_demo(point_sprites)
|
||||
# Tint does not support setting gl_PointSize, disable the relevant sample if using WebGPU
|
||||
# https://github.com/gpuweb/gpuweb/issues/1190
|
||||
if (NOT FILAMENT_SUPPORTS_WEBGPU)
|
||||
add_demo(point_sprites)
|
||||
endif ()
|
||||
add_demo(rendertarget)
|
||||
add_demo(sample_cloth)
|
||||
add_demo(sample_full_pbr)
|
||||
@@ -271,6 +294,9 @@ if (NOT ANDROID)
|
||||
add_demo(vbotest)
|
||||
add_demo(viewtest)
|
||||
|
||||
add_demo_plain(spirv_test)
|
||||
target_link_libraries(spirv_test PRIVATE SPIRV-Headers utils)
|
||||
|
||||
# Sample app specific
|
||||
target_link_libraries(frame_generator PRIVATE imageio)
|
||||
target_link_libraries(gltf_viewer PRIVATE gltf-demo-resources uberarchive gltfio viewer)
|
||||
|
||||
@@ -77,7 +77,11 @@ static void printUsage(char* name) {
|
||||
" --help, -h\n"
|
||||
" Prints this message\n\n"
|
||||
" --api, -a\n"
|
||||
" Specify the backend API: opengl, vulkan, or metal\n"
|
||||
" Specify the backend API: opengl, vulkan, metal, or webgpu\n"
|
||||
" (note: webgpu is a no-op for now, printing backend\n"
|
||||
" component info if FILAMENT_BACKEND_DEBUG_FLAG, \n"
|
||||
" set at build time, includes the \n"
|
||||
" FWGPU_PRINT_SYSTEM bit flag 0x2)\n"
|
||||
);
|
||||
const std::string from("HELLOTRIANGLE");
|
||||
for (size_t pos = usage.find(from); pos != std::string::npos; pos = usage.find(from, pos)) {
|
||||
@@ -109,8 +113,11 @@ static int handleCommandLineArguments(int argc, char* argv[], App* app) {
|
||||
app->config.backend = Engine::Backend::VULKAN;
|
||||
} else if (arg == "metal") {
|
||||
app->config.backend = Engine::Backend::METAL;
|
||||
} else if (arg == "webgpu") {
|
||||
app->config.backend = Engine::Backend::WEBGPU;
|
||||
} else {
|
||||
std::cerr << "Unrecognized backend. Must be 'opengl'|'vulkan'|'metal'.\n";
|
||||
std::cerr << "Unrecognized backend. Must be "
|
||||
"'opengl'|'vulkan'|'metal'|'webgpu'.\n";
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
#include "spirv/unified1/spirv.hpp"
|
||||
#include <utils/Log.h>
|
||||
|
||||
#include <functional>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
size_t getFileSize(const std::string& filename) {
|
||||
std::ifstream file(filename, std::ios::binary | std::ios::ate);
|
||||
if (!file.is_open()) {
|
||||
return -1;
|
||||
}
|
||||
size_t size = file.tellg();
|
||||
file.close();
|
||||
return size;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> transform(std::vector<uint32_t> const& spirv) {
|
||||
constexpr size_t HEADER_SIZE = 5;
|
||||
size_t const dataSize = spirv.size();
|
||||
std::vector<uint32_t> output;
|
||||
output.resize(dataSize);
|
||||
uint32_t const* data = (uint32_t*) spirv.data();
|
||||
uint32_t* outputData = output.data();
|
||||
memcpy(&outputData[0], &data[0], HEADER_SIZE * 4);
|
||||
|
||||
struct Descriptor {
|
||||
uint32_t binding = 0xFFFFFFFF;
|
||||
uint32_t set = 0xFFFFFFFF;
|
||||
bool fixed = false;
|
||||
};
|
||||
|
||||
std::unordered_map<uint32_t, Descriptor> descriptors;
|
||||
auto getDesc = [&](uint32_t var) -> Descriptor& {
|
||||
using itertype = std::unordered_map<uint32_t, Descriptor>::iterator;
|
||||
itertype itr;
|
||||
if (itr = descriptors.find(var); itr == descriptors.end()) {
|
||||
itr = descriptors.insert({var, {}}).first;
|
||||
}
|
||||
return itr->second;
|
||||
};
|
||||
|
||||
auto pass = [&](uint32_t targetOp, std::function<void(uint32_t, uint32_t)> f) {
|
||||
for (uint32_t cursor = HEADER_SIZE, cursorEnd = dataSize; cursor < cursorEnd;) {
|
||||
uint32_t const firstWord = data[cursor];
|
||||
uint32_t const wordCount = firstWord >> 16;
|
||||
uint32_t const op = firstWord & 0x0000FFFF;
|
||||
if (targetOp == op) {
|
||||
f(wordCount-1, cursor+1);
|
||||
}
|
||||
cursor += wordCount;
|
||||
}
|
||||
};
|
||||
|
||||
uint32_t samplerTypeId = 0;
|
||||
uint32_t imageTypeId = 0;
|
||||
|
||||
// First we find the ID used to identify the sampler type and the image type.
|
||||
pass(spv::Op::OpTypeSampler, [&](uint32_t count, uint32_t pos) {
|
||||
samplerTypeId = data[pos];
|
||||
});
|
||||
|
||||
pass(spv::Op::OpTypeImage, [&](uint32_t count, uint32_t pos) {
|
||||
imageTypeId = data[pos];
|
||||
});
|
||||
|
||||
// Next we identify all the descriptors and record their set id and binding.
|
||||
pass(spv::Op::OpDecorate, [&](uint32_t count, uint32_t pos) {
|
||||
uint32_t const type = data[pos + 1];
|
||||
if (type == spv::Decoration::DecorationBinding) {
|
||||
uint32_t const targetVar = data[pos];
|
||||
uint32_t const binding = data[pos + 2];
|
||||
auto& desc = getDesc(targetVar);
|
||||
desc.binding = binding;
|
||||
// Note these decorations do not need to be written to the output.
|
||||
} else if (type == spv::Decoration::DecorationDescriptorSet) {
|
||||
uint32_t const targetVar = data[pos];
|
||||
uint32_t const set = data[pos + 2];
|
||||
auto& desc = getDesc(targetVar);
|
||||
desc.set = set;
|
||||
}
|
||||
});
|
||||
|
||||
// If any descriptor is loaded to be a sampler, we offset it by a fixed amount. Note that it
|
||||
// could be "loaded" multiple times, so we use a boolean to skip if it's already offsetted.
|
||||
constexpr uint32_t SAMPLER_BINDING_OFFSET = 100;
|
||||
pass(spv::Op::OpLoad, [&](uint32_t count, uint32_t pos) {
|
||||
uint32_t const retType = data[pos];
|
||||
uint32_t const targetVar = data[pos + 2];
|
||||
if (retType == samplerTypeId) {
|
||||
auto& desc = getDesc(targetVar);
|
||||
if (!desc.fixed) {
|
||||
desc.binding += SAMPLER_BINDING_OFFSET;
|
||||
desc.fixed = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Write out the offsetted bindings and copy the rest.
|
||||
for (uint32_t cursor = HEADER_SIZE, cursorEnd = dataSize; cursor < cursorEnd;) {
|
||||
uint32_t const firstWord = data[cursor];
|
||||
uint32_t const wordCount = firstWord >> 16;
|
||||
uint32_t const op = firstWord & 0x0000FFFF;
|
||||
|
||||
uint32_t* out = &outputData[cursor];
|
||||
std::memcpy(out, &data[cursor], wordCount * 4);
|
||||
|
||||
if (op == spv::Op::OpDecorate && data[cursor + 2] == spv::Decoration::DecorationBinding) {
|
||||
uint32_t const targetVar = data[cursor + 1];
|
||||
auto const& modified = getDesc(targetVar);
|
||||
out[3] = modified.binding;
|
||||
}
|
||||
cursor += wordCount;
|
||||
}
|
||||
|
||||
// Note that you should be able to do this in-place (without making another vector).
|
||||
return output;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
if (argc < 3) {
|
||||
utils::slog.e <<"need input and output spirv filenames" << utils::io::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string infname {argv[1]};
|
||||
size_t const fsize = getFileSize(infname);
|
||||
size_t const wordCount = fsize / 4;
|
||||
|
||||
std::ifstream inputFile(infname, std::ios::binary);
|
||||
|
||||
std::vector<uint32_t> words;
|
||||
words.resize(wordCount);
|
||||
inputFile.read((char*) words.data(), fsize);
|
||||
inputFile.close();
|
||||
|
||||
std::string outfname {argv[2]};
|
||||
std::vector<uint32_t> output = transform(words);
|
||||
std::ofstream outputFile(outfname, std::ios::binary);
|
||||
outputFile.write((char const*) output.data(), output.size() * 4);
|
||||
outputFile.close();
|
||||
|
||||
return 0;
|
||||
}
|
||||
Vendored
+54
-2
@@ -12,7 +12,59 @@ set(DAWN_ABSEIL_DIR ${EXTERNAL}/abseil)
|
||||
set(DAWN_SPIRV_TOOLS_DIR ${EXTERNAL}/spirv-tools/)
|
||||
set(DAWN_SPIRV_HEADERS_DIR ${EXTERNAL}/spirv-headers)
|
||||
set(DAWN_GLSLANG_DIR ${EXTERNAL}/glslang/)
|
||||
if(IS_HOST_PLATFORM)
|
||||
set(DAWN_DXC_ENABLE_ASSERTS_IN_NDEBUG OFF)
|
||||
|
||||
#set(TINT_BUILD_CMD_TOOLS OFF)
|
||||
|
||||
if (FILAMENT_BUILD_FILAMAT)
|
||||
set(TINT_BUILD_SPV_READER ON)
|
||||
endif ()
|
||||
add_subdirectory(../ ${PROJECT_BINARY_DIR}/third_party/dawn)
|
||||
|
||||
add_subdirectory(../ ${PROJECT_BINARY_DIR}/third_party/dawn)
|
||||
|
||||
# Need to install libtint for filamat on Android.
|
||||
# See third_party/dawn/tnt/CMakeLists.txt and android/filamat-android/CMakeLists.txt.
|
||||
if (ANDROID AND FILAMENT_BUILD_FILAMAT)
|
||||
set(COMBINED_TARGET libtint_combined)
|
||||
set(COMBINED_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${COMBINED_TARGET}.a")
|
||||
|
||||
# TODO: maybe use this target in place of libtint in filamat proper. (it's smaller)
|
||||
set(LIBTINT_DEPS
|
||||
tint_lang_core
|
||||
tint_lang_core_constant
|
||||
tint_lang_core_intrinsic
|
||||
tint_lang_core_type
|
||||
tint_lang_spirv_reader
|
||||
tint_lang_spirv_reader_ast_lower
|
||||
tint_lang_spirv_reader_ast_parser
|
||||
tint_lang_spirv_reader_common
|
||||
tint_lang_spirv_writer
|
||||
tint_lang_wgsl
|
||||
tint_lang_wgsl_ast
|
||||
tint_lang_wgsl_ast_transform
|
||||
tint_lang_wgsl_features
|
||||
tint_lang_wgsl_intrinsic
|
||||
tint_lang_wgsl_program
|
||||
tint_lang_wgsl_resolver
|
||||
tint_lang_wgsl_sem
|
||||
tint_lang_wgsl_writer
|
||||
tint_lang_wgsl_writer_ast_printer
|
||||
tint_results
|
||||
tint_utils
|
||||
tint_utils_diagnostic
|
||||
tint_utils_ice
|
||||
tint_utils_result
|
||||
tint_utils_rtti
|
||||
tint_utils_strconv
|
||||
tint_utils_symbol
|
||||
tint_utils_text
|
||||
)
|
||||
|
||||
# Use the following no-op definitions to introduce a library and its dependency.
|
||||
add_library(${COMBINED_TARGET} STATIC dummy.cpp)
|
||||
target_link_libraries(${COMBINED_TARGET} PRIVATE
|
||||
${LIBTINT_DEPS}
|
||||
)
|
||||
combine_static_libs(${COMBINED_TARGET} "${COMBINED_OUTPUT}" "${LIBTINT_DEPS}")
|
||||
install(FILES "${COMBINED_OUTPUT}" DESTINATION lib/${DIST_DIR})
|
||||
endif()
|
||||
|
||||
Vendored
Executable → Regular
Vendored
+80
-9
@@ -30,15 +30,44 @@
|
||||
#
|
||||
# Bazel Build for Google C++ Testing Framework(Google Test)
|
||||
|
||||
load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
exports_files(["LICENSE"])
|
||||
|
||||
config_setting(
|
||||
name = "qnx",
|
||||
constraint_values = ["@platforms//os:qnx"],
|
||||
)
|
||||
|
||||
config_setting(
|
||||
name = "windows",
|
||||
constraint_values = ["@bazel_tools//platforms:windows"],
|
||||
constraint_values = ["@platforms//os:windows"],
|
||||
)
|
||||
|
||||
config_setting(
|
||||
name = "freebsd",
|
||||
constraint_values = ["@platforms//os:freebsd"],
|
||||
)
|
||||
|
||||
config_setting(
|
||||
name = "openbsd",
|
||||
constraint_values = ["@platforms//os:openbsd"],
|
||||
)
|
||||
|
||||
# NOTE: Fuchsia is not an officially supported platform.
|
||||
config_setting(
|
||||
name = "fuchsia",
|
||||
constraint_values = ["@platforms//os:fuchsia"],
|
||||
)
|
||||
|
||||
config_setting(
|
||||
name = "msvc_compiler",
|
||||
flag_values = {
|
||||
"@bazel_tools//tools/cpp:compiler": "msvc-cl",
|
||||
},
|
||||
visibility = [":__subpackages__"],
|
||||
)
|
||||
|
||||
config_setting(
|
||||
@@ -54,6 +83,10 @@ cc_library(
|
||||
)
|
||||
|
||||
# Google Test including Google Mock
|
||||
|
||||
# For an actual test, use `gtest` and also `gtest_main` if you depend on gtest's
|
||||
# main(). For a library, use `gtest_for_library` instead if the library can be
|
||||
# testonly.
|
||||
cc_library(
|
||||
name = "gtest",
|
||||
srcs = glob(
|
||||
@@ -76,6 +109,7 @@ cc_library(
|
||||
"googlemock/include/gmock/*.h",
|
||||
]),
|
||||
copts = select({
|
||||
":qnx": [],
|
||||
":windows": [],
|
||||
"//conditions:default": ["-pthread"],
|
||||
}),
|
||||
@@ -94,22 +128,59 @@ cc_library(
|
||||
"googletest/include",
|
||||
],
|
||||
linkopts = select({
|
||||
":qnx": ["-lregex"],
|
||||
":windows": [],
|
||||
":freebsd": [
|
||||
"-lm",
|
||||
"-pthread",
|
||||
],
|
||||
":openbsd": [
|
||||
"-lm",
|
||||
"-pthread",
|
||||
],
|
||||
"//conditions:default": ["-pthread"],
|
||||
}),
|
||||
deps = select({
|
||||
":has_absl": [
|
||||
"@com_google_absl//absl/debugging:failure_signal_handler",
|
||||
"@com_google_absl//absl/debugging:stacktrace",
|
||||
"@com_google_absl//absl/debugging:symbolize",
|
||||
"@com_google_absl//absl/strings",
|
||||
"@com_google_absl//absl/types:optional",
|
||||
"@com_google_absl//absl/types:variant",
|
||||
"@abseil-cpp//absl/container:flat_hash_set",
|
||||
"@abseil-cpp//absl/debugging:failure_signal_handler",
|
||||
"@abseil-cpp//absl/debugging:stacktrace",
|
||||
"@abseil-cpp//absl/debugging:symbolize",
|
||||
"@abseil-cpp//absl/flags:flag",
|
||||
"@abseil-cpp//absl/flags:parse",
|
||||
"@abseil-cpp//absl/flags:reflection",
|
||||
"@abseil-cpp//absl/flags:usage",
|
||||
"@abseil-cpp//absl/strings",
|
||||
"@abseil-cpp//absl/types:any",
|
||||
"@abseil-cpp//absl/types:optional",
|
||||
"@abseil-cpp//absl/types:variant",
|
||||
"@re2//:re2",
|
||||
],
|
||||
"//conditions:default": [],
|
||||
}) + select({
|
||||
# `gtest-death-test.cc` has `EXPECT_DEATH` that spawns a process,
|
||||
# expects it to crash and inspects its logs with the given matcher,
|
||||
# so that's why these libraries are needed.
|
||||
# Otherwise, builds targeting Fuchsia would fail to compile.
|
||||
":fuchsia": [
|
||||
"@fuchsia_sdk//pkg/fdio",
|
||||
"@fuchsia_sdk//pkg/syslog",
|
||||
"@fuchsia_sdk//pkg/zx",
|
||||
],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
)
|
||||
|
||||
# `gtest`, but testonly. See guidance on `gtest` for when to use this.
|
||||
alias(
|
||||
name = "gtest_for_library",
|
||||
actual = ":gtest",
|
||||
testonly = True,
|
||||
)
|
||||
|
||||
# Implements main() for tests using gtest. Prefer to depend on `gtest` as well
|
||||
# to ensure compliance with the layering_check Bazel feature where only the
|
||||
# direct hdrs values are available.
|
||||
cc_library(
|
||||
name = "gtest_main",
|
||||
srcs = ["googlemock/src/gmock_main.cc"],
|
||||
|
||||
Vendored
+15
-15
@@ -1,23 +1,13 @@
|
||||
# Note: CMake support is community-based. The maintainers do not use CMake
|
||||
# internally.
|
||||
|
||||
cmake_minimum_required(VERSION 2.8.8)
|
||||
|
||||
if (POLICY CMP0048)
|
||||
cmake_policy(SET CMP0048 NEW)
|
||||
endif (POLICY CMP0048)
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
project(googletest-distribution)
|
||||
set(GOOGLETEST_VERSION 1.9.0)
|
||||
set(GOOGLETEST_VERSION 1.16.0)
|
||||
|
||||
if (CMAKE_VERSION VERSION_LESS "3.1")
|
||||
add_definitions(-std=c++11)
|
||||
else()
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
if(NOT CYGWIN)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
endif()
|
||||
if(NOT CYGWIN AND NOT MSYS AND NOT ${CMAKE_SYSTEM_NAME} STREQUAL QNX)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
endif()
|
||||
|
||||
enable_testing()
|
||||
@@ -25,9 +15,19 @@ enable_testing()
|
||||
include(CMakeDependentOption)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
#Note that googlemock target already builds googletest
|
||||
# Note that googlemock target already builds googletest.
|
||||
option(BUILD_GMOCK "Builds the googlemock subproject" ON)
|
||||
option(INSTALL_GTEST "Enable installation of googletest. (Projects embedding googletest may want to turn this OFF.)" ON)
|
||||
option(GTEST_HAS_ABSL "Use Abseil and RE2. Requires Abseil and RE2 to be separately added to the build." OFF)
|
||||
|
||||
if(GTEST_HAS_ABSL)
|
||||
if(NOT TARGET absl::base)
|
||||
find_package(absl REQUIRED)
|
||||
endif()
|
||||
if(NOT TARGET re2::re2)
|
||||
find_package(re2 REQUIRED)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(BUILD_GMOCK)
|
||||
add_subdirectory( googlemock )
|
||||
|
||||
Vendored
+31
-32
@@ -21,14 +21,14 @@ accept your pull requests.
|
||||
|
||||
## Are you a Googler?
|
||||
|
||||
If you are a Googler, please make an attempt to submit an internal change rather
|
||||
than a GitHub Pull Request. If you are not able to submit an internal change a
|
||||
If you are a Googler, please make an attempt to submit an internal contribution
|
||||
rather than a GitHub Pull Request. If you are not able to submit internally, a
|
||||
PR is acceptable as an alternative.
|
||||
|
||||
## Contributing A Patch
|
||||
|
||||
1. Submit an issue describing your proposed change to the
|
||||
[issue tracker](https://github.com/google/googletest).
|
||||
[issue tracker](https://github.com/google/googletest/issues).
|
||||
2. Please don't mix more than one logical change per submittal, because it
|
||||
makes the history hard to follow. If you want to make a change that doesn't
|
||||
have a corresponding issue in the issue tracker, please create one.
|
||||
@@ -36,7 +36,8 @@ PR is acceptable as an alternative.
|
||||
This ensures that work isn't being duplicated and communicating your plan
|
||||
early also generally leads to better patches.
|
||||
4. If your proposed change is accepted, and you haven't already done so, sign a
|
||||
Contributor License Agreement (see details above).
|
||||
Contributor License Agreement
|
||||
([see details above](#contributor-license-agreements)).
|
||||
5. Fork the desired repo, develop and test your code changes.
|
||||
6. Ensure that your code adheres to the existing style in the sample to which
|
||||
you are contributing.
|
||||
@@ -46,11 +47,11 @@ PR is acceptable as an alternative.
|
||||
## The Google Test and Google Mock Communities
|
||||
|
||||
The Google Test community exists primarily through the
|
||||
[discussion group](http://groups.google.com/group/googletestframework) and the
|
||||
[discussion group](https://groups.google.com/group/googletestframework) and the
|
||||
GitHub repository. Likewise, the Google Mock community exists primarily through
|
||||
their own [discussion group](http://groups.google.com/group/googlemock). You are
|
||||
definitely encouraged to contribute to the discussion and you can also help us
|
||||
to keep the effectiveness of the group high by following and promoting the
|
||||
their own [discussion group](https://groups.google.com/group/googlemock). You
|
||||
are definitely encouraged to contribute to the discussion and you can also help
|
||||
us to keep the effectiveness of the group high by following and promoting the
|
||||
guidelines listed here.
|
||||
|
||||
### Please Be Friendly
|
||||
@@ -79,17 +80,17 @@ fairly rigid coding style, as defined by the
|
||||
[google-styleguide](https://github.com/google/styleguide) project. All patches
|
||||
will be expected to conform to the style outlined
|
||||
[here](https://google.github.io/styleguide/cppguide.html). Use
|
||||
[.clang-format](https://github.com/google/googletest/blob/master/.clang-format)
|
||||
to check your formatting
|
||||
[.clang-format](https://github.com/google/googletest/blob/main/.clang-format) to
|
||||
check your formatting.
|
||||
|
||||
## Requirements for Contributors
|
||||
|
||||
If you plan to contribute a patch, you need to build Google Test, Google Mock,
|
||||
and their own tests from a git checkout, which has further requirements:
|
||||
|
||||
* [Python](https://www.python.org/) v2.3 or newer (for running some of the
|
||||
* [Python](https://www.python.org/) v3.6 or newer (for running some of the
|
||||
tests and re-generating certain source files from templates)
|
||||
* [CMake](https://cmake.org/) v2.6.4 or newer
|
||||
* [CMake](https://cmake.org/) v2.8.12 or newer
|
||||
|
||||
## Developing Google Test and Google Mock
|
||||
|
||||
@@ -101,42 +102,40 @@ To make sure your changes work as intended and don't break existing
|
||||
functionality, you'll want to compile and run Google Test and GoogleMock's own
|
||||
tests. For that you can use CMake:
|
||||
|
||||
mkdir mybuild
|
||||
cd mybuild
|
||||
cmake -Dgtest_build_tests=ON -Dgmock_build_tests=ON ${GTEST_REPO_DIR}
|
||||
```
|
||||
mkdir mybuild
|
||||
cd mybuild
|
||||
cmake -Dgtest_build_tests=ON -Dgmock_build_tests=ON ${GTEST_REPO_DIR}
|
||||
```
|
||||
|
||||
To choose between building only Google Test or Google Mock, you may modify your
|
||||
cmake command to be one of each
|
||||
|
||||
cmake -Dgtest_build_tests=ON ${GTEST_DIR} # sets up Google Test tests
|
||||
cmake -Dgmock_build_tests=ON ${GMOCK_DIR} # sets up Google Mock tests
|
||||
```
|
||||
cmake -Dgtest_build_tests=ON ${GTEST_DIR} # sets up Google Test tests
|
||||
cmake -Dgmock_build_tests=ON ${GMOCK_DIR} # sets up Google Mock tests
|
||||
```
|
||||
|
||||
Make sure you have Python installed, as some of Google Test's tests are written
|
||||
in Python. If the cmake command complains about not being able to find Python
|
||||
(`Could NOT find PythonInterp (missing: PYTHON_EXECUTABLE)`), try telling it
|
||||
explicitly where your Python executable can be found:
|
||||
|
||||
cmake -DPYTHON_EXECUTABLE=path/to/python ...
|
||||
```
|
||||
cmake -DPYTHON_EXECUTABLE=path/to/python ...
|
||||
```
|
||||
|
||||
Next, you can build Google Test and / or Google Mock and all desired tests. On
|
||||
\*nix, this is usually done by
|
||||
|
||||
make
|
||||
```
|
||||
make
|
||||
```
|
||||
|
||||
To run the tests, do
|
||||
|
||||
make test
|
||||
```
|
||||
make test
|
||||
```
|
||||
|
||||
All tests should pass.
|
||||
|
||||
### Regenerating Source Files
|
||||
|
||||
Some of Google Test's source files are generated from templates (not in the C++
|
||||
sense) using a script. For example, the file
|
||||
include/gtest/internal/gtest-type-util.h.pump is used to generate
|
||||
gtest-type-util.h in the same directory.
|
||||
|
||||
You don't need to worry about regenerating the source files unless you need to
|
||||
modify them. You would then modify the corresponding `.pump` files and run the
|
||||
'[pump.py](googletest/scripts/pump.py)' generator script. See the
|
||||
[Pump Manual](googletest/docs/pump_manual.md).
|
||||
|
||||
+29
@@ -5,33 +5,62 @@
|
||||
|
||||
Ajay Joshi <jaj@google.com>
|
||||
Balázs Dán <balazs.dan@gmail.com>
|
||||
Benoit Sigoure <tsuna@google.com>
|
||||
Bharat Mediratta <bharat@menalto.com>
|
||||
Bogdan Piloca <boo@google.com>
|
||||
Chandler Carruth <chandlerc@google.com>
|
||||
Chris Prince <cprince@google.com>
|
||||
Chris Taylor <taylorc@google.com>
|
||||
Dan Egnor <egnor@google.com>
|
||||
Dave MacLachlan <dmaclach@gmail.com>
|
||||
David Anderson <danderson@google.com>
|
||||
Dean Sturtevant
|
||||
Eric Roman <eroman@chromium.org>
|
||||
Gene Volovich <gv@cite.com>
|
||||
Hady Zalek <hady.zalek@gmail.com>
|
||||
Hal Burch <gmock@hburch.com>
|
||||
Jeffrey Yasskin <jyasskin@google.com>
|
||||
Jim Keller <jimkeller@google.com>
|
||||
Joe Walnes <joe@truemesh.com>
|
||||
Jon Wray <jwray@google.com>
|
||||
Jói Sigurðsson <joi@google.com>
|
||||
Keir Mierle <mierle@gmail.com>
|
||||
Keith Ray <keith.ray@gmail.com>
|
||||
Kenton Varda <kenton@google.com>
|
||||
Kostya Serebryany <kcc@google.com>
|
||||
Krystian Kuzniarek <krystian.kuzniarek@gmail.com>
|
||||
Lev Makhlis
|
||||
Manuel Klimek <klimek@google.com>
|
||||
Mario Tanev <radix@google.com>
|
||||
Mark Paskin
|
||||
Markus Heule <markus.heule@gmail.com>
|
||||
Martijn Vels <mvels@google.com>
|
||||
Matthew Simmons <simmonmt@acm.org>
|
||||
Mika Raento <mikie@iki.fi>
|
||||
Mike Bland <mbland@google.com>
|
||||
Miklós Fazekas <mfazekas@szemafor.com>
|
||||
Neal Norwitz <nnorwitz@gmail.com>
|
||||
Nermin Ozkiranartli <nermin@google.com>
|
||||
Owen Carlsen <ocarlsen@google.com>
|
||||
Paneendra Ba <paneendra@google.com>
|
||||
Pasi Valminen <pasi.valminen@gmail.com>
|
||||
Patrick Hanna <phanna@google.com>
|
||||
Patrick Riley <pfr@google.com>
|
||||
Paul Menage <menage@google.com>
|
||||
Peter Kaminski <piotrk@google.com>
|
||||
Piotr Kaminski <piotrk@google.com>
|
||||
Preston Jackson <preston.a.jackson@gmail.com>
|
||||
Rainer Klaffenboeck <rainer.klaffenboeck@dynatrace.com>
|
||||
Russ Cox <rsc@google.com>
|
||||
Russ Rufer <russ@pentad.com>
|
||||
Sean Mcafee <eefacm@gmail.com>
|
||||
Sigurður Ásgeirsson <siggi@google.com>
|
||||
Soyeon Kim <sxshx818@naver.com>
|
||||
Sverre Sundsdal <sundsdal@gmail.com>
|
||||
Szymon Sobik <sobik.szymon@gmail.com>
|
||||
Takeshi Yoshino <tyoshino@google.com>
|
||||
Tracy Bialik <tracy@pentad.com>
|
||||
Vadim Berman <vadimb@google.com>
|
||||
Vlad Losev <vladl@google.com>
|
||||
Wolfgang Klier <wklier@google.com>
|
||||
Zhanyong Wan <wan@google.com>
|
||||
Vendored
Executable → Regular
+44
-17
@@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright 2017 Google Inc.
|
||||
# Copyright 2024 Google Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
#
|
||||
@@ -29,21 +28,49 @@
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
set -eu
|
||||
# https://bazel.build/external/overview#bzlmod
|
||||
|
||||
if [ "${TRAVIS_OS_NAME}" != linux ]; then
|
||||
echo "Not a Linux build; skipping installation"
|
||||
exit 0
|
||||
fi
|
||||
module(
|
||||
name = "googletest",
|
||||
version = "head",
|
||||
compatibility_level = 1,
|
||||
)
|
||||
|
||||
# Only direct dependencies need to be listed below.
|
||||
# Please keep the versions in sync with the versions in the WORKSPACE file.
|
||||
|
||||
if [ "${TRAVIS_SUDO}" = "true" ]; then
|
||||
echo "deb [arch=amd64] http://storage.googleapis.com/bazel-apt stable jdk1.8" | \
|
||||
sudo tee /etc/apt/sources.list.d/bazel.list
|
||||
curl https://bazel.build/bazel-release.pub.gpg | sudo apt-key add -
|
||||
sudo apt-get update && sudo apt-get install -y bazel gcc-4.9 g++-4.9 clang-3.9
|
||||
elif [ "${CXX}" = "clang++" ]; then
|
||||
# Use ccache, assuming $HOME/bin is in the path, which is true in the Travis build environment.
|
||||
ln -sf /usr/bin/ccache $HOME/bin/${CXX};
|
||||
ln -sf /usr/bin/ccache $HOME/bin/${CC};
|
||||
fi
|
||||
bazel_dep(
|
||||
name = "abseil-cpp",
|
||||
version = "20250127.0",
|
||||
)
|
||||
bazel_dep(
|
||||
name = "platforms",
|
||||
version = "0.0.10",
|
||||
)
|
||||
bazel_dep(
|
||||
name = "re2",
|
||||
version = "2024-07-02",
|
||||
)
|
||||
|
||||
bazel_dep(
|
||||
name = "rules_python",
|
||||
version = "1.1.0",
|
||||
dev_dependency = True,
|
||||
)
|
||||
|
||||
# https://rules-python.readthedocs.io/en/stable/toolchains.html#library-modules-with-dev-only-python-usage
|
||||
python = use_extension(
|
||||
"@rules_python//python/extensions:python.bzl",
|
||||
"python",
|
||||
dev_dependency = True,
|
||||
)
|
||||
python.toolchain(
|
||||
ignore_root_user_error = True,
|
||||
is_default = True,
|
||||
python_version = "3.12",
|
||||
)
|
||||
|
||||
# See fake_fuchsia_sdk.bzl for instructions on how to override this with a real SDK, if needed.
|
||||
fuchsia_sdk = use_extension("//:fake_fuchsia_sdk.bzl", "fuchsia_sdk")
|
||||
fuchsia_sdk.create_fake()
|
||||
use_repo(fuchsia_sdk, "fuchsia_sdk")
|
||||
Vendored
+92
-86
@@ -1,93 +1,110 @@
|
||||
# Google Test
|
||||
# GoogleTest
|
||||
|
||||
#### Announcement:
|
||||
### Announcements
|
||||
|
||||
Please note that Sept 13 - Sept 20 most of the maintainers will be attending
|
||||
CppCon 2019. Our response time for Pull Requests and Issues will increase.
|
||||
#### Live at Head
|
||||
|
||||
#### OSS Builds Status:
|
||||
GoogleTest now follows the
|
||||
[Abseil Live at Head philosophy](https://abseil.io/about/philosophy#upgrade-support).
|
||||
We recommend
|
||||
[updating to the latest commit in the `main` branch as often as possible](https://github.com/abseil/abseil-cpp/blob/master/FAQ.md#what-is-live-at-head-and-how-do-i-do-it).
|
||||
We do publish occasional semantic versions, tagged with
|
||||
`v${major}.${minor}.${patch}` (e.g. `v1.16.0`).
|
||||
|
||||
[](https://travis-ci.org/google/googletest)
|
||||
[](https://ci.appveyor.com/project/GoogleTestAppVeyor/googletest/branch/master)
|
||||
#### Documentation Updates
|
||||
|
||||
### Future Plans
|
||||
Our documentation is now live on GitHub Pages at
|
||||
https://google.github.io/googletest/. We recommend browsing the documentation on
|
||||
GitHub Pages rather than directly in the repository.
|
||||
|
||||
#### 1.8.x Release:
|
||||
#### Release 1.16.0
|
||||
|
||||
[the 1.8.x](https://github.com/google/googletest/releases/tag/release-1.8.1) is
|
||||
the last release that works with pre-C++11 compilers. The 1.8.x will not accept
|
||||
any requests for any new features and any bugfix requests will only be accepted
|
||||
if proven "critical"
|
||||
[Release 1.16.0](https://github.com/google/googletest/releases/tag/v1.16.0) is
|
||||
now available.
|
||||
|
||||
#### Post 1.8.x:
|
||||
The 1.16.x branch requires at least C++14.
|
||||
|
||||
On-going work to improve/cleanup/pay technical debt. When this work is completed
|
||||
there will be a 1.9.x tagged release
|
||||
The 1.16.x branch will be the last to support C++14. Future development will
|
||||
[require at least C++17](https://opensource.google/documentation/policies/cplusplus-support#c_language_standard).
|
||||
|
||||
#### Post 1.9.x
|
||||
#### Continuous Integration
|
||||
|
||||
Post 1.9.x googletest will follow
|
||||
[Abseil Live at Head philosophy](https://abseil.io/about/philosophy)
|
||||
We use Google's internal systems for continuous integration.
|
||||
|
||||
## Welcome to **Google Test**, Google's C++ test framework!
|
||||
#### Coming Soon
|
||||
|
||||
* We are planning to take a dependency on
|
||||
[Abseil](https://github.com/abseil/abseil-cpp).
|
||||
|
||||
## Welcome to **GoogleTest**, Google's C++ test framework!
|
||||
|
||||
This repository is a merger of the formerly separate GoogleTest and GoogleMock
|
||||
projects. These were so closely related that it makes sense to maintain and
|
||||
release them together.
|
||||
|
||||
Please subscribe to the mailing list at googletestframework@googlegroups.com for
|
||||
questions, discussions, and development.
|
||||
### Getting Started
|
||||
|
||||
### Getting started:
|
||||
See the [GoogleTest User's Guide](https://google.github.io/googletest/) for
|
||||
documentation. We recommend starting with the
|
||||
[GoogleTest Primer](https://google.github.io/googletest/primer.html).
|
||||
|
||||
The information for **Google Test** is available in the
|
||||
[Google Test Primer](googletest/docs/primer.md) documentation.
|
||||
|
||||
**Google Mock** is an extension to Google Test for writing and using C++ mock
|
||||
classes. See the separate [Google Mock documentation](googlemock/README.md).
|
||||
|
||||
More detailed documentation for googletest is in its interior
|
||||
[googletest/README.md](googletest/README.md) file.
|
||||
More information about building GoogleTest can be found at
|
||||
[googletest/README.md](googletest/README.md).
|
||||
|
||||
## Features
|
||||
|
||||
* An [xUnit](https://en.wikipedia.org/wiki/XUnit) test framework.
|
||||
* Test discovery.
|
||||
* A rich set of assertions.
|
||||
* User-defined assertions.
|
||||
* Death tests.
|
||||
* Fatal and non-fatal failures.
|
||||
* Value-parameterized tests.
|
||||
* Type-parameterized tests.
|
||||
* Various options for running the tests.
|
||||
* XML test report generation.
|
||||
* xUnit test framework: \
|
||||
Googletest is based on the [xUnit](https://en.wikipedia.org/wiki/XUnit)
|
||||
testing framework, a popular architecture for unit testing
|
||||
* Test discovery: \
|
||||
Googletest automatically discovers and runs your tests, eliminating the need
|
||||
to manually register your tests
|
||||
* Rich set of assertions: \
|
||||
Googletest provides a variety of assertions, such as equality, inequality,
|
||||
exceptions, and more, making it easy to test your code
|
||||
* User-defined assertions: \
|
||||
You can define your own assertions with Googletest, making it simple to
|
||||
write tests that are specific to your code
|
||||
* Death tests: \
|
||||
Googletest supports death tests, which verify that your code exits in a
|
||||
certain way, making it useful for testing error-handling code
|
||||
* Fatal and non-fatal failures: \
|
||||
You can specify whether a test failure should be treated as fatal or
|
||||
non-fatal with Googletest, allowing tests to continue running even if a
|
||||
failure occurs
|
||||
* Value-parameterized tests: \
|
||||
Googletest supports value-parameterized tests, which run multiple times with
|
||||
different input values, making it useful for testing functions that take
|
||||
different inputs
|
||||
* Type-parameterized tests: \
|
||||
Googletest also supports type-parameterized tests, which run with different
|
||||
data types, making it useful for testing functions that work with different
|
||||
data types
|
||||
* Various options for running tests: \
|
||||
Googletest provides many options for running tests including running
|
||||
individual tests, running tests in a specific order and running tests in
|
||||
parallel
|
||||
|
||||
## Platforms
|
||||
## Supported Platforms
|
||||
|
||||
Google test has been used on a variety of platforms:
|
||||
GoogleTest follows Google's
|
||||
[Foundational C++ Support Policy](https://opensource.google/documentation/policies/cplusplus-support).
|
||||
See
|
||||
[this table](https://github.com/google/oss-policies-info/blob/main/foundational-cxx-support-matrix.md)
|
||||
for a list of currently supported versions of compilers, platforms, and build
|
||||
tools.
|
||||
|
||||
* Linux
|
||||
* Mac OS X
|
||||
* Windows
|
||||
* Cygwin
|
||||
* MinGW
|
||||
* Windows Mobile
|
||||
* Symbian
|
||||
* PlatformIO
|
||||
## Who Is Using GoogleTest?
|
||||
|
||||
## Who Is Using Google Test?
|
||||
|
||||
In addition to many internal projects at Google, Google Test is also used by the
|
||||
In addition to many internal projects at Google, GoogleTest is also used by the
|
||||
following notable projects:
|
||||
|
||||
* The [Chromium projects](http://www.chromium.org/) (behind the Chrome browser
|
||||
and Chrome OS).
|
||||
* The [LLVM](http://llvm.org/) compiler.
|
||||
* The [Chromium projects](https://www.chromium.org/) (behind the Chrome
|
||||
browser and Chrome OS).
|
||||
* The [LLVM](https://llvm.org/) compiler.
|
||||
* [Protocol Buffers](https://github.com/google/protobuf), Google's data
|
||||
interchange format.
|
||||
* The [OpenCV](http://opencv.org/) computer vision library.
|
||||
* [tiny-dnn](https://github.com/tiny-dnn/tiny-dnn): header only,
|
||||
dependency-free deep learning framework in C++11.
|
||||
* The [OpenCV](https://opencv.org/) computer vision library.
|
||||
|
||||
## Related Open Source Projects
|
||||
|
||||
@@ -95,13 +112,13 @@ following notable projects:
|
||||
automated test-runner and Graphical User Interface with powerful features for
|
||||
Windows and Linux platforms.
|
||||
|
||||
[Google Test UI](https://github.com/ospector/gtest-gbar) is test runner that
|
||||
[GoogleTest UI](https://github.com/ospector/gtest-gbar) is a test runner that
|
||||
runs your test binary, allows you to track its progress via a progress bar, and
|
||||
displays a list of test failures. Clicking on one shows failure text. Google
|
||||
Test UI is written in C#.
|
||||
displays a list of test failures. Clicking on one shows failure text. GoogleTest
|
||||
UI is written in C#.
|
||||
|
||||
[GTest TAP Listener](https://github.com/kinow/gtest-tap-listener) is an event
|
||||
listener for Google Test that implements the
|
||||
listener for GoogleTest that implements the
|
||||
[TAP protocol](https://en.wikipedia.org/wiki/Test_Anything_Protocol) for test
|
||||
result output. If your test runner understands TAP, you may find it useful.
|
||||
|
||||
@@ -109,31 +126,20 @@ result output. If your test runner understands TAP, you may find it useful.
|
||||
runs tests from your binary in parallel to provide significant speed-up.
|
||||
|
||||
[GoogleTest Adapter](https://marketplace.visualstudio.com/items?itemName=DavidSchuldenfrei.gtest-adapter)
|
||||
is a VS Code extension allowing to view Google Tests in a tree view, and
|
||||
run/debug your tests.
|
||||
is a VS Code extension allowing to view GoogleTest in a tree view and run/debug
|
||||
your tests.
|
||||
|
||||
## Requirements
|
||||
[C++ TestMate](https://github.com/matepek/vscode-catch2-test-adapter) is a VS
|
||||
Code extension allowing to view GoogleTest in a tree view and run/debug your
|
||||
tests.
|
||||
|
||||
Google Test is designed to have fairly minimal requirements to build and use
|
||||
with your projects, but there are some. If you notice any problems on your
|
||||
platform, please notify
|
||||
[googletestframework@googlegroups.com](https://groups.google.com/forum/#!forum/googletestframework).
|
||||
Patches for fixing them are welcome!
|
||||
[Cornichon](https://pypi.org/project/cornichon/) is a small Gherkin DSL parser
|
||||
that generates stub code for GoogleTest.
|
||||
|
||||
### Build Requirements
|
||||
## Contributing Changes
|
||||
|
||||
These are the base requirements to build and use Google Test from a source
|
||||
package:
|
||||
|
||||
* [Bazel](https://bazel.build/) or [CMake](https://cmake.org/). NOTE: Bazel is
|
||||
the build system that googletest is using internally and tests against.
|
||||
CMake is community-supported.
|
||||
|
||||
* a C++11-standard-compliant compiler
|
||||
|
||||
## Contributing change
|
||||
|
||||
Please read the [`CONTRIBUTING.md`](CONTRIBUTING.md) for details on how to
|
||||
contribute to this project.
|
||||
Please read
|
||||
[`CONTRIBUTING.md`](https://github.com/google/googletest/blob/main/CONTRIBUTING.md)
|
||||
for details on how to contribute to this project.
|
||||
|
||||
Happy testing!
|
||||
|
||||
Vendored
+54
-16
@@ -1,23 +1,61 @@
|
||||
workspace(name = "com_google_googletest")
|
||||
# Copyright 2024 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.
|
||||
|
||||
workspace(name = "googletest")
|
||||
|
||||
load("//:googletest_deps.bzl", "googletest_deps")
|
||||
googletest_deps()
|
||||
|
||||
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
|
||||
|
||||
# Abseil
|
||||
http_archive(
|
||||
name = "com_google_absl",
|
||||
urls = ["https://github.com/abseil/abseil-cpp/archive/master.zip"],
|
||||
strip_prefix = "abseil-cpp-master",
|
||||
)
|
||||
|
||||
http_archive(
|
||||
name = "rules_cc",
|
||||
strip_prefix = "rules_cc-master",
|
||||
urls = ["https://github.com/bazelbuild/rules_cc/archive/master.zip"],
|
||||
)
|
||||
|
||||
http_archive(
|
||||
name = "rules_python",
|
||||
strip_prefix = "rules_python-master",
|
||||
urls = ["https://github.com/bazelbuild/rules_python/archive/master.zip"],
|
||||
sha256 = "9c6e26911a79fbf510a8f06d8eedb40f412023cf7fa6d1461def27116bff022c",
|
||||
strip_prefix = "rules_python-1.1.0",
|
||||
url = "https://github.com/bazelbuild/rules_python/releases/download/1.1.0/rules_python-1.1.0.tar.gz",
|
||||
)
|
||||
# https://github.com/bazelbuild/rules_python/releases/tag/1.1.0
|
||||
load("@rules_python//python:repositories.bzl", "py_repositories")
|
||||
py_repositories()
|
||||
|
||||
http_archive(
|
||||
name = "bazel_skylib",
|
||||
sha256 = "cd55a062e763b9349921f0f5db8c3933288dc8ba4f76dd9416aac68acee3cb94",
|
||||
urls = ["https://github.com/bazelbuild/bazel-skylib/releases/download/1.5.0/bazel-skylib-1.5.0.tar.gz"],
|
||||
)
|
||||
|
||||
http_archive(
|
||||
name = "platforms",
|
||||
urls = [
|
||||
"https://mirror.bazel.build/github.com/bazelbuild/platforms/releases/download/0.0.10/platforms-0.0.10.tar.gz",
|
||||
"https://github.com/bazelbuild/platforms/releases/download/0.0.10/platforms-0.0.10.tar.gz",
|
||||
],
|
||||
sha256 = "218efe8ee736d26a3572663b374a253c012b716d8af0c07e842e82f238a0a7ee",
|
||||
)
|
||||
|
||||
Vendored
Executable → Regular
+6
-11
@@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright 2017 Google Inc.
|
||||
# Copyright 2024 Google Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
#
|
||||
@@ -29,12 +28,8 @@
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
set -eu
|
||||
|
||||
if [ "${TRAVIS_OS_NAME}" != "osx" ]; then
|
||||
echo "Not a macOS build; skipping installation"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
brew update
|
||||
brew install ccache gcc@4.9
|
||||
# https://bazel.build/external/migration#workspace.bzlmod
|
||||
#
|
||||
# This file is intentionally empty. When bzlmod is enabled and this
|
||||
# file exists, the content of WORKSPACE is ignored. This prevents
|
||||
# bzlmod builds from unintentionally depending on the WORKSPACE file.
|
||||
Vendored
-154
@@ -1,154 +0,0 @@
|
||||
version: '{build}'
|
||||
|
||||
os: Visual Studio 2015
|
||||
|
||||
environment:
|
||||
matrix:
|
||||
- compiler: msvc-15-seh
|
||||
generator: "Visual Studio 15 2017"
|
||||
build_system: cmake
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
|
||||
- compiler: msvc-15-seh
|
||||
generator: "Visual Studio 15 2017 Win64"
|
||||
build_system: cmake
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
enabled_on_pr: yes
|
||||
|
||||
- compiler: msvc-15-seh
|
||||
build_system: bazel
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
enabled_on_pr: yes
|
||||
|
||||
- compiler: msvc-14-seh
|
||||
build_system: cmake
|
||||
generator: "Visual Studio 14 2015"
|
||||
enabled_on_pr: yes
|
||||
|
||||
- compiler: msvc-14-seh
|
||||
build_system: cmake
|
||||
generator: "Visual Studio 14 2015 Win64"
|
||||
|
||||
- compiler: gcc-6.3.0-posix
|
||||
build_system: cmake
|
||||
generator: "MinGW Makefiles"
|
||||
cxx_path: 'C:\mingw-w64\i686-6.3.0-posix-dwarf-rt_v5-rev1\mingw32\bin'
|
||||
enabled_on_pr: yes
|
||||
|
||||
configuration:
|
||||
- Debug
|
||||
|
||||
build:
|
||||
verbosity: minimal
|
||||
|
||||
install:
|
||||
- ps: |
|
||||
Write-Output "Compiler: $env:compiler"
|
||||
Write-Output "Generator: $env:generator"
|
||||
Write-Output "Env:Configuation: $env:configuration"
|
||||
Write-Output "Env: $env"
|
||||
if (-not (Test-Path env:APPVEYOR_PULL_REQUEST_NUMBER)) {
|
||||
Write-Output "This is *NOT* a pull request build"
|
||||
} else {
|
||||
Write-Output "This is a pull request build"
|
||||
if (-not (Test-Path env:enabled_on_pr) -or $env:enabled_on_pr -ne "yes") {
|
||||
Write-Output "PR builds are *NOT* explicitly enabled"
|
||||
}
|
||||
}
|
||||
|
||||
# install Bazel
|
||||
if ($env:build_system -eq "bazel") {
|
||||
appveyor DownloadFile https://github.com/bazelbuild/bazel/releases/download/0.28.1/bazel-0.28.1-windows-x86_64.exe -FileName bazel.exe
|
||||
}
|
||||
|
||||
if ($env:build_system -eq "cmake") {
|
||||
# git bash conflicts with MinGW makefiles
|
||||
if ($env:generator -eq "MinGW Makefiles") {
|
||||
$env:path = $env:path.replace("C:\Program Files\Git\usr\bin;", "")
|
||||
if ($env:cxx_path -ne "") {
|
||||
$env:path += ";$env:cxx_path"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
before_build:
|
||||
- ps: |
|
||||
$env:root=$env:APPVEYOR_BUILD_FOLDER
|
||||
Write-Output "env:root: $env:root"
|
||||
|
||||
build_script:
|
||||
- ps: |
|
||||
# Only enable some builds for pull requests, the AppVeyor queue is too long.
|
||||
if ((Test-Path env:APPVEYOR_PULL_REQUEST_NUMBER) -And (-not (Test-Path env:enabled_on_pr) -or $env:enabled_on_pr -ne "yes")) {
|
||||
return
|
||||
} else {
|
||||
# special case - build with Bazel
|
||||
if ($env:build_system -eq "bazel") {
|
||||
& $env:root\bazel.exe build -c opt //:gtest_samples
|
||||
if ($LastExitCode -eq 0) { # bazel writes to StdErr and PowerShell interprets it as an error
|
||||
$host.SetShouldExit(0)
|
||||
} else { # a real error
|
||||
throw "Exec: $ErrorMessage"
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
# by default build with CMake
|
||||
md _build -Force | Out-Null
|
||||
cd _build
|
||||
|
||||
$conf = if ($env:generator -eq "MinGW Makefiles") {"-DCMAKE_BUILD_TYPE=$env:configuration"} else {"-DCMAKE_CONFIGURATION_TYPES=Debug;Release"}
|
||||
# Disable test for MinGW (gtest tests fail, gmock tests can not build)
|
||||
$gtest_build_tests = if ($env:generator -eq "MinGW Makefiles") {"-Dgtest_build_tests=OFF"} else {"-Dgtest_build_tests=ON"}
|
||||
$gmock_build_tests = if ($env:generator -eq "MinGW Makefiles") {"-Dgmock_build_tests=OFF"} else {"-Dgmock_build_tests=ON"}
|
||||
& cmake -G "$env:generator" $conf -Dgtest_build_samples=ON $gtest_build_tests $gmock_build_tests ..
|
||||
if ($LastExitCode -ne 0) {
|
||||
throw "Exec: $ErrorMessage"
|
||||
}
|
||||
$cmake_parallel = if ($env:generator -eq "MinGW Makefiles") {"-j2"} else {"/m"}
|
||||
& cmake --build . --config $env:configuration -- $cmake_parallel
|
||||
if ($LastExitCode -ne 0) {
|
||||
throw "Exec: $ErrorMessage"
|
||||
}
|
||||
|
||||
|
||||
skip_commits:
|
||||
files:
|
||||
- '**/*.md'
|
||||
|
||||
test_script:
|
||||
- ps: |
|
||||
# Only enable some builds for pull requests, the AppVeyor queue is too long.
|
||||
if ((Test-Path env:APPVEYOR_PULL_REQUEST_NUMBER) -And (-not (Test-Path env:enabled_on_pr) -or $env:enabled_on_pr -ne "yes")) {
|
||||
return
|
||||
}
|
||||
if ($env:build_system -eq "bazel") {
|
||||
# special case - testing with Bazel
|
||||
& $env:root\bazel.exe test //:gtest_samples
|
||||
if ($LastExitCode -eq 0) { # bazel writes to StdErr and PowerShell interprets it as an error
|
||||
$host.SetShouldExit(0)
|
||||
} else { # a real error
|
||||
throw "Exec: $ErrorMessage"
|
||||
}
|
||||
}
|
||||
if ($env:build_system -eq "cmake") {
|
||||
# built with CMake - test with CTest
|
||||
if ($env:generator -eq "MinGW Makefiles") {
|
||||
return # No test available for MinGW
|
||||
}
|
||||
|
||||
& ctest -C $env:configuration --timeout 600 --output-on-failure
|
||||
if ($LastExitCode -ne 0) {
|
||||
throw "Exec: $ErrorMessage"
|
||||
}
|
||||
}
|
||||
|
||||
artifacts:
|
||||
- path: '_build/CMakeFiles/*.log'
|
||||
name: logs
|
||||
- path: '_build/Testing/**/*.xml'
|
||||
name: test_results
|
||||
- path: 'bazel-testlogs/**/test.log'
|
||||
name: test_logs
|
||||
- path: 'bazel-testlogs/**/test.xml'
|
||||
name: test_results
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright 2017 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.
|
||||
|
||||
set -e
|
||||
|
||||
bazel version
|
||||
bazel build --curses=no //...:all
|
||||
bazel test --curses=no //...:all
|
||||
bazel test --curses=no //...:all --define absl=1
|
||||
-2
@@ -1,2 +0,0 @@
|
||||
# run PlatformIO builds
|
||||
platformio run
|
||||
Vendored
-41
@@ -1,41 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright 2017 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.
|
||||
|
||||
#
|
||||
# This file should be sourced, and not executed as a standalone script.
|
||||
#
|
||||
|
||||
# TODO() - we can check if this is being sourced using $BASH_VERSION and $BASH_SOURCE[0] != ${0}.
|
||||
|
||||
if [ "${TRAVIS_OS_NAME}" = "linux" ]; then
|
||||
if [ "$CXX" = "g++" ]; then export CXX="g++-4.9" CC="gcc-4.9"; fi
|
||||
if [ "$CXX" = "clang++" ]; then export CXX="clang++-3.9" CC="clang-3.9"; fi
|
||||
fi
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright 2017 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.
|
||||
|
||||
# This file is typically sourced by another script.
|
||||
# if possible, ask for the precise number of processors,
|
||||
# otherwise take 2 processors as reasonable default; see
|
||||
# https://docs.travis-ci.com/user/speeding-up-the-build/#Makefile-optimization
|
||||
if [ -x /usr/bin/getconf ]; then
|
||||
NPROCESSORS=$(/usr/bin/getconf _NPROCESSORS_ONLN)
|
||||
else
|
||||
NPROCESSORS=2
|
||||
fi
|
||||
|
||||
# as of 2017-09-04 Travis CI reports 32 processors, but GCC build
|
||||
# crashes if parallelized too much (maybe memory consumption problem),
|
||||
# so limit to 4 processors for the time being.
|
||||
if [ $NPROCESSORS -gt 4 ] ; then
|
||||
echo "$0:Note: Limiting processors to use by make from $NPROCESSORS to 4."
|
||||
NPROCESSORS=4
|
||||
fi
|
||||
@@ -1,5 +0,0 @@
|
||||
# install PlatformIO
|
||||
sudo pip install -U platformio
|
||||
|
||||
# update PlatformIO
|
||||
platformio update
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright 2020, 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.
|
||||
|
||||
set -euox pipefail
|
||||
|
||||
readonly LINUX_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_hybrid-latest:20241218"
|
||||
readonly LINUX_GCC_FLOOR_CONTAINER="gcr.io/google.com/absl-177019/linux_gcc-floor:20250205"
|
||||
|
||||
if [[ -z ${GTEST_ROOT:-} ]]; then
|
||||
GTEST_ROOT="$(realpath $(dirname ${0})/..)"
|
||||
fi
|
||||
|
||||
if [[ -z ${STD:-} ]]; then
|
||||
STD="c++17 c++20"
|
||||
fi
|
||||
|
||||
# Test CMake + GCC
|
||||
for cmake_off_on in OFF ON; do
|
||||
time docker run \
|
||||
--volume="${GTEST_ROOT}:/src:ro" \
|
||||
--tmpfs="/build:exec" \
|
||||
--workdir="/build" \
|
||||
--rm \
|
||||
--env="CC=/usr/local/bin/gcc" \
|
||||
--env=CXXFLAGS="-Werror -Wdeprecated" \
|
||||
${LINUX_LATEST_CONTAINER} \
|
||||
/bin/bash -c "
|
||||
cmake /src \
|
||||
-DCMAKE_CXX_STANDARD=17 \
|
||||
-Dgtest_build_samples=ON \
|
||||
-Dgtest_build_tests=ON \
|
||||
-Dgmock_build_tests=ON \
|
||||
-Dcxx_no_exception=${cmake_off_on} \
|
||||
-Dcxx_no_rtti=${cmake_off_on} && \
|
||||
make -j$(nproc) && \
|
||||
ctest -j$(nproc) --output-on-failure"
|
||||
done
|
||||
|
||||
# Test CMake + Clang
|
||||
for cmake_off_on in OFF ON; do
|
||||
time docker run \
|
||||
--volume="${GTEST_ROOT}:/src:ro" \
|
||||
--tmpfs="/build:exec" \
|
||||
--workdir="/build" \
|
||||
--rm \
|
||||
--env="CC=/opt/llvm/clang/bin/clang" \
|
||||
--env=CXXFLAGS="-Werror -Wdeprecated --gcc-toolchain=/usr/local" \
|
||||
${LINUX_LATEST_CONTAINER} \
|
||||
/bin/bash -c "
|
||||
cmake /src \
|
||||
-DCMAKE_CXX_STANDARD=17 \
|
||||
-Dgtest_build_samples=ON \
|
||||
-Dgtest_build_tests=ON \
|
||||
-Dgmock_build_tests=ON \
|
||||
-Dcxx_no_exception=${cmake_off_on} \
|
||||
-Dcxx_no_rtti=${cmake_off_on} && \
|
||||
make -j$(nproc) && \
|
||||
ctest -j$(nproc) --output-on-failure"
|
||||
done
|
||||
|
||||
# Do one test with an older version of GCC
|
||||
time docker run \
|
||||
--volume="${GTEST_ROOT}:/src:ro" \
|
||||
--workdir="/src" \
|
||||
--rm \
|
||||
--env="CC=/usr/local/bin/gcc" \
|
||||
--env="BAZEL_CXXOPTS=-std=c++17" \
|
||||
${LINUX_GCC_FLOOR_CONTAINER} \
|
||||
/usr/local/bin/bazel test ... \
|
||||
--copt="-Wall" \
|
||||
--copt="-Werror" \
|
||||
--copt="-Wuninitialized" \
|
||||
--copt="-Wundef" \
|
||||
--copt="-Wno-error=pragmas" \
|
||||
--enable_bzlmod=false \
|
||||
--features=external_include_paths \
|
||||
--keep_going \
|
||||
--show_timestamps \
|
||||
--test_output=errors
|
||||
|
||||
# Test GCC
|
||||
for std in ${STD}; do
|
||||
for absl in 0 1; do
|
||||
time docker run \
|
||||
--volume="${GTEST_ROOT}:/src:ro" \
|
||||
--workdir="/src" \
|
||||
--rm \
|
||||
--env="CC=/usr/local/bin/gcc" \
|
||||
--env="BAZEL_CXXOPTS=-std=${std}" \
|
||||
${LINUX_LATEST_CONTAINER} \
|
||||
/usr/local/bin/bazel test ... \
|
||||
--copt="-Wall" \
|
||||
--copt="-Werror" \
|
||||
--copt="-Wuninitialized" \
|
||||
--copt="-Wundef" \
|
||||
--define="absl=${absl}" \
|
||||
--enable_bzlmod=true \
|
||||
--features=external_include_paths \
|
||||
--keep_going \
|
||||
--show_timestamps \
|
||||
--test_output=errors
|
||||
done
|
||||
done
|
||||
|
||||
# Test Clang
|
||||
for std in ${STD}; do
|
||||
for absl in 0 1; do
|
||||
time docker run \
|
||||
--volume="${GTEST_ROOT}:/src:ro" \
|
||||
--workdir="/src" \
|
||||
--rm \
|
||||
--env="CC=/opt/llvm/clang/bin/clang" \
|
||||
--env="BAZEL_CXXOPTS=-std=${std}" \
|
||||
${LINUX_LATEST_CONTAINER} \
|
||||
/usr/local/bin/bazel test ... \
|
||||
--copt="--gcc-toolchain=/usr/local" \
|
||||
--copt="-Wall" \
|
||||
--copt="-Werror" \
|
||||
--copt="-Wuninitialized" \
|
||||
--copt="-Wundef" \
|
||||
--define="absl=${absl}" \
|
||||
--enable_bzlmod=true \
|
||||
--features=external_include_paths \
|
||||
--keep_going \
|
||||
--linkopt="--gcc-toolchain=/usr/local" \
|
||||
--show_timestamps \
|
||||
--test_output=errors
|
||||
done
|
||||
done
|
||||
third_party/libgtest/googlemock/scripts/upload_gmock.py → third_party/libgtest/ci/macos-presubmit.sh
Vendored
Executable → Regular
+45
-43
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright 2009, Google Inc.
|
||||
# Copyright 2020, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
@@ -29,50 +29,52 @@
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
"""upload_gmock.py v0.1.0 -- uploads a Google Mock patch for review.
|
||||
set -euox pipefail
|
||||
|
||||
This simple wrapper passes all command line flags and
|
||||
--cc=googlemock@googlegroups.com to upload.py.
|
||||
# Use Xcode 16.0
|
||||
sudo xcode-select -s /Applications/Xcode_16.0.app/Contents/Developer
|
||||
|
||||
USAGE: upload_gmock.py [options for upload.py]
|
||||
"""
|
||||
if [[ -z ${GTEST_ROOT:-} ]]; then
|
||||
GTEST_ROOT="$(realpath $(dirname ${0})/..)"
|
||||
fi
|
||||
|
||||
__author__ = 'wan@google.com (Zhanyong Wan)'
|
||||
# Test the CMake build
|
||||
for cmake_off_on in OFF ON; do
|
||||
BUILD_DIR=$(mktemp -d build_dir.XXXXXXXX)
|
||||
cd ${BUILD_DIR}
|
||||
time cmake ${GTEST_ROOT} \
|
||||
-DCMAKE_CXX_STANDARD=17 \
|
||||
-Dgtest_build_samples=ON \
|
||||
-Dgtest_build_tests=ON \
|
||||
-Dgmock_build_tests=ON \
|
||||
-Dcxx_no_exception=${cmake_off_on} \
|
||||
-Dcxx_no_rtti=${cmake_off_on}
|
||||
time make -j$(nproc)
|
||||
time ctest -j$(nproc) --output-on-failure
|
||||
done
|
||||
|
||||
import os
|
||||
import sys
|
||||
# Test the Bazel build
|
||||
|
||||
CC_FLAG = '--cc='
|
||||
GMOCK_GROUP = 'googlemock@googlegroups.com'
|
||||
# If we are running on Kokoro, check for a versioned Bazel binary.
|
||||
KOKORO_GFILE_BAZEL_BIN="bazel-8.0.0-darwin-x86_64"
|
||||
if [[ ${KOKORO_GFILE_DIR:-} ]] && [[ -f ${KOKORO_GFILE_DIR}/${KOKORO_GFILE_BAZEL_BIN} ]]; then
|
||||
BAZEL_BIN="${KOKORO_GFILE_DIR}/${KOKORO_GFILE_BAZEL_BIN}"
|
||||
chmod +x ${BAZEL_BIN}
|
||||
else
|
||||
BAZEL_BIN="bazel"
|
||||
fi
|
||||
|
||||
|
||||
def main():
|
||||
# Finds the path to upload.py, assuming it is in the same directory
|
||||
# as this file.
|
||||
my_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
upload_py_path = os.path.join(my_dir, 'upload.py')
|
||||
|
||||
# Adds Google Mock discussion group to the cc line if it's not there
|
||||
# already.
|
||||
upload_py_argv = [upload_py_path]
|
||||
found_cc_flag = False
|
||||
for arg in sys.argv[1:]:
|
||||
if arg.startswith(CC_FLAG):
|
||||
found_cc_flag = True
|
||||
cc_line = arg[len(CC_FLAG):]
|
||||
cc_list = [addr for addr in cc_line.split(',') if addr]
|
||||
if GMOCK_GROUP not in cc_list:
|
||||
cc_list.append(GMOCK_GROUP)
|
||||
upload_py_argv.append(CC_FLAG + ','.join(cc_list))
|
||||
else:
|
||||
upload_py_argv.append(arg)
|
||||
|
||||
if not found_cc_flag:
|
||||
upload_py_argv.append(CC_FLAG + GMOCK_GROUP)
|
||||
|
||||
# Invokes upload.py with the modified command line flags.
|
||||
os.execv(upload_py_path, upload_py_argv)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
cd ${GTEST_ROOT}
|
||||
for absl in 0 1; do
|
||||
${BAZEL_BIN} test ... \
|
||||
--copt="-Wall" \
|
||||
--copt="-Werror" \
|
||||
--copt="-Wundef" \
|
||||
--cxxopt="-std=c++17" \
|
||||
--define="absl=${absl}" \
|
||||
--enable_bzlmod=true \
|
||||
--features=external_include_paths \
|
||||
--keep_going \
|
||||
--show_timestamps \
|
||||
--test_output=errors
|
||||
done
|
||||
Vendored
-44
@@ -1,44 +0,0 @@
|
||||
#!/usr/bin/env sh
|
||||
set -evx
|
||||
|
||||
. ci/get-nprocessors.sh
|
||||
|
||||
# if possible, ask for the precise number of processors,
|
||||
# otherwise take 2 processors as reasonable default; see
|
||||
# https://docs.travis-ci.com/user/speeding-up-the-build/#Makefile-optimization
|
||||
if [ -x /usr/bin/getconf ]; then
|
||||
NPROCESSORS=$(/usr/bin/getconf _NPROCESSORS_ONLN)
|
||||
else
|
||||
NPROCESSORS=2
|
||||
fi
|
||||
# as of 2017-09-04 Travis CI reports 32 processors, but GCC build
|
||||
# crashes if parallelized too much (maybe memory consumption problem),
|
||||
# so limit to 4 processors for the time being.
|
||||
if [ $NPROCESSORS -gt 4 ] ; then
|
||||
echo "$0:Note: Limiting processors to use by make from $NPROCESSORS to 4."
|
||||
NPROCESSORS=4
|
||||
fi
|
||||
# Tell make to use the processors. No preceding '-' required.
|
||||
MAKEFLAGS="j${NPROCESSORS}"
|
||||
export MAKEFLAGS
|
||||
|
||||
env | sort
|
||||
|
||||
# Set default values to OFF for these variables if not specified.
|
||||
: "${NO_EXCEPTION:=OFF}"
|
||||
: "${NO_RTTI:=OFF}"
|
||||
: "${COMPILER_IS_GNUCXX:=OFF}"
|
||||
|
||||
mkdir build || true
|
||||
cd build
|
||||
cmake -Dgtest_build_samples=ON \
|
||||
-Dgtest_build_tests=ON \
|
||||
-Dgmock_build_tests=ON \
|
||||
-Dcxx_no_exception=$NO_EXCEPTION \
|
||||
-Dcxx_no_rtti=$NO_RTTI \
|
||||
-DCMAKE_COMPILER_IS_GNUCXX=$COMPILER_IS_GNUCXX \
|
||||
-DCMAKE_CXX_FLAGS=$CXX_FLAGS \
|
||||
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
|
||||
..
|
||||
make
|
||||
CTEST_OUTPUT_ON_FAILURE=1 make test
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
SETLOCAL ENABLEDELAYEDEXPANSION
|
||||
|
||||
SET BAZEL_EXE=%KOKORO_GFILE_DIR%\bazel-8.0.0-windows-x86_64.exe
|
||||
|
||||
SET PATH=C:\Python34;%PATH%
|
||||
SET BAZEL_PYTHON=C:\python34\python.exe
|
||||
SET BAZEL_SH=C:\tools\msys64\usr\bin\bash.exe
|
||||
SET CMAKE_BIN="cmake.exe"
|
||||
SET CTEST_BIN="ctest.exe"
|
||||
SET CTEST_OUTPUT_ON_FAILURE=1
|
||||
SET CMAKE_BUILD_PARALLEL_LEVEL=16
|
||||
SET CTEST_PARALLEL_LEVEL=16
|
||||
|
||||
SET GTEST_ROOT=%~dp0\..
|
||||
IF %errorlevel% neq 0 EXIT /B 1
|
||||
|
||||
:: ----------------------------------------------------------------------------
|
||||
:: CMake
|
||||
SET CMAKE_BUILD_PATH=cmake_msvc2022
|
||||
MKDIR %CMAKE_BUILD_PATH%
|
||||
CD %CMAKE_BUILD_PATH%
|
||||
|
||||
%CMAKE_BIN% %GTEST_ROOT% ^
|
||||
-G "Visual Studio 17 2022" ^
|
||||
-DCMAKE_CXX_STANDARD=17 ^
|
||||
-DPYTHON_EXECUTABLE:FILEPATH=c:\python37\python.exe ^
|
||||
-DPYTHON_INCLUDE_DIR:PATH=c:\python37\include ^
|
||||
-DPYTHON_LIBRARY:FILEPATH=c:\python37\lib\site-packages\pip ^
|
||||
-Dgtest_build_samples=ON ^
|
||||
-Dgtest_build_tests=ON ^
|
||||
-Dgmock_build_tests=ON
|
||||
IF %errorlevel% neq 0 EXIT /B 1
|
||||
|
||||
%CMAKE_BIN% --build . --target ALL_BUILD --config Debug -- -maxcpucount
|
||||
IF %errorlevel% neq 0 EXIT /B 1
|
||||
|
||||
%CTEST_BIN% -C Debug --timeout 600
|
||||
IF %errorlevel% neq 0 EXIT /B 1
|
||||
|
||||
CD %GTEST_ROOT%
|
||||
RMDIR /S /Q %CMAKE_BUILD_PATH%
|
||||
|
||||
:: ----------------------------------------------------------------------------
|
||||
:: Bazel
|
||||
|
||||
:: The default home directory on Kokoro is a long path which causes errors
|
||||
:: because of Windows limitations on path length.
|
||||
:: --output_user_root=C:\tmp causes Bazel to use a shorter path.
|
||||
SET BAZEL_VS=C:\Program Files\Microsoft Visual Studio\2022\Community
|
||||
|
||||
:: C++17
|
||||
%BAZEL_EXE% ^
|
||||
--output_user_root=C:\tmp ^
|
||||
test ... ^
|
||||
--compilation_mode=dbg ^
|
||||
--copt=/std:c++17 ^
|
||||
--copt=/WX ^
|
||||
--enable_bzlmod=true ^
|
||||
--keep_going ^
|
||||
--test_output=errors ^
|
||||
--test_tag_filters=-no_test_msvc2017
|
||||
IF %errorlevel% neq 0 EXIT /B 1
|
||||
|
||||
:: C++20
|
||||
%BAZEL_EXE% ^
|
||||
--output_user_root=C:\tmp ^
|
||||
test ... ^
|
||||
--compilation_mode=dbg ^
|
||||
--copt=/std:c++20 ^
|
||||
--copt=/WX ^
|
||||
--enable_bzlmod=true ^
|
||||
--keep_going ^
|
||||
--test_output=errors ^
|
||||
--test_tag_filters=-no_test_msvc2017
|
||||
IF %errorlevel% neq 0 EXIT /B 1
|
||||
+1
@@ -0,0 +1 @@
|
||||
title: GoogleTest
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
nav:
|
||||
- section: "Get Started"
|
||||
items:
|
||||
- title: "Supported Platforms"
|
||||
url: "/platforms.html"
|
||||
- title: "Quickstart: Bazel"
|
||||
url: "/quickstart-bazel.html"
|
||||
- title: "Quickstart: CMake"
|
||||
url: "/quickstart-cmake.html"
|
||||
- section: "Guides"
|
||||
items:
|
||||
- title: "GoogleTest Primer"
|
||||
url: "/primer.html"
|
||||
- title: "Advanced Topics"
|
||||
url: "/advanced.html"
|
||||
- title: "Mocking for Dummies"
|
||||
url: "/gmock_for_dummies.html"
|
||||
- title: "Mocking Cookbook"
|
||||
url: "/gmock_cook_book.html"
|
||||
- title: "Mocking Cheat Sheet"
|
||||
url: "/gmock_cheat_sheet.html"
|
||||
- section: "References"
|
||||
items:
|
||||
- title: "Testing Reference"
|
||||
url: "/reference/testing.html"
|
||||
- title: "Mocking Reference"
|
||||
url: "/reference/mocking.html"
|
||||
- title: "Assertions"
|
||||
url: "/reference/assertions.html"
|
||||
- title: "Matchers"
|
||||
url: "/reference/matchers.html"
|
||||
- title: "Actions"
|
||||
url: "/reference/actions.html"
|
||||
- title: "Testing FAQ"
|
||||
url: "/faq.html"
|
||||
- title: "Mocking FAQ"
|
||||
url: "/gmock_faq.html"
|
||||
- title: "Code Samples"
|
||||
url: "/samples.html"
|
||||
- title: "Using pkg-config"
|
||||
url: "/pkgconfig.html"
|
||||
- title: "Community Documentation"
|
||||
url: "/community_created_documentation.html"
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ site.lang | default: "en-US" }}">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
{% seo %}
|
||||
<link rel="stylesheet" href="{{ "/assets/css/style.css?v=" | append: site.github.build_revision | relative_url }}">
|
||||
<script>
|
||||
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
|
||||
ga('create', 'UA-197576187-1', { 'storage': 'none' });
|
||||
ga('set', 'referrer', document.referrer.split('?')[0]);
|
||||
ga('set', 'location', window.location.href.split('?')[0]);
|
||||
ga('set', 'anonymizeIp', true);
|
||||
ga('send', 'pageview');
|
||||
</script>
|
||||
<script async src='https://www.google-analytics.com/analytics.js'></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="sidebar">
|
||||
<div class="header">
|
||||
<h1><a href="{{ "/" | relative_url }}">{{ site.title | default: "Documentation" }}</a></h1>
|
||||
</div>
|
||||
<input type="checkbox" id="nav-toggle" class="nav-toggle">
|
||||
<label for="nav-toggle" class="expander">
|
||||
<span class="arrow"></span>
|
||||
</label>
|
||||
<nav>
|
||||
{% for item in site.data.navigation.nav %}
|
||||
<h2>{{ item.section }}</h2>
|
||||
<ul>
|
||||
{% for subitem in item.items %}
|
||||
<a href="{{subitem.url | relative_url }}">
|
||||
<li class="{% if subitem.url == page.url %}active{% endif %}">
|
||||
{{ subitem.title }}
|
||||
</li>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endfor %}
|
||||
</nav>
|
||||
</div>
|
||||
<div class="main markdown-body">
|
||||
<div class="main-inner">
|
||||
{{ content }}
|
||||
</div>
|
||||
<div class="footer">
|
||||
GoogleTest ·
|
||||
<a href="https://github.com/google/googletest">GitHub Repository</a> ·
|
||||
<a href="https://github.com/google/googletest/blob/main/LICENSE">License</a> ·
|
||||
<a href="https://policies.google.com/privacy">Privacy Policy</a>
|
||||
</div>
|
||||
</div>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/anchor-js/4.1.0/anchor.min.js" integrity="sha256-lZaRhKri35AyJSypXXs4o6OPFTbTmUoltBbDCbdzegg=" crossorigin="anonymous"></script>
|
||||
<script>anchors.add('.main h2, .main h3, .main h4, .main h5, .main h6');</script>
|
||||
</body>
|
||||
</html>
|
||||
+200
@@ -0,0 +1,200 @@
|
||||
// Styles for GoogleTest docs website on GitHub Pages.
|
||||
// Color variables are defined in
|
||||
// https://github.com/pages-themes/primer/tree/master/_sass/primer-support/lib/variables
|
||||
|
||||
$sidebar-width: 260px;
|
||||
|
||||
body {
|
||||
display: flex;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
background: $black;
|
||||
color: $text-white;
|
||||
flex-shrink: 0;
|
||||
height: 100vh;
|
||||
overflow: auto;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
width: $sidebar-width;
|
||||
}
|
||||
|
||||
.sidebar h1 {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
.sidebar h2 {
|
||||
color: $gray-light;
|
||||
font-size: 0.8em;
|
||||
font-weight: normal;
|
||||
margin-bottom: 0.8em;
|
||||
padding-left: 2.5em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.sidebar .header {
|
||||
background: $black;
|
||||
padding: 2em;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.sidebar .header a {
|
||||
color: $text-white;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.sidebar .nav-toggle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.sidebar .expander {
|
||||
cursor: pointer;
|
||||
display: none;
|
||||
height: 3em;
|
||||
position: absolute;
|
||||
right: 1em;
|
||||
top: 1.5em;
|
||||
width: 3em;
|
||||
}
|
||||
|
||||
.sidebar .expander .arrow {
|
||||
border: solid $white;
|
||||
border-width: 0 3px 3px 0;
|
||||
display: block;
|
||||
height: 0.7em;
|
||||
margin: 1em auto;
|
||||
transform: rotate(45deg);
|
||||
transition: transform 0.5s;
|
||||
width: 0.7em;
|
||||
}
|
||||
|
||||
.sidebar nav {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.sidebar nav ul {
|
||||
list-style-type: none;
|
||||
margin-bottom: 1em;
|
||||
padding: 0;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
li {
|
||||
color: $text-white;
|
||||
padding-left: 2em;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
li.active {
|
||||
background: $border-gray-darker;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
li:hover {
|
||||
background: $border-gray-darker;
|
||||
}
|
||||
}
|
||||
|
||||
.main {
|
||||
background-color: $bg-gray;
|
||||
width: calc(100% - #{$sidebar-width});
|
||||
}
|
||||
|
||||
.main .main-inner {
|
||||
background-color: $white;
|
||||
padding: 2em;
|
||||
}
|
||||
|
||||
.main .footer {
|
||||
margin: 0;
|
||||
padding: 2em;
|
||||
}
|
||||
|
||||
.main table th {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.main .callout {
|
||||
border-left: 0.25em solid $white;
|
||||
padding: 1em;
|
||||
|
||||
a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
&.important {
|
||||
background-color: $bg-yellow-light;
|
||||
border-color: $bg-yellow;
|
||||
color: $black;
|
||||
}
|
||||
|
||||
&.note {
|
||||
background-color: $bg-blue-light;
|
||||
border-color: $text-blue;
|
||||
color: $text-blue;
|
||||
}
|
||||
|
||||
&.tip {
|
||||
background-color: $green-000;
|
||||
border-color: $green-700;
|
||||
color: $green-700;
|
||||
}
|
||||
|
||||
&.warning {
|
||||
background-color: $red-000;
|
||||
border-color: $text-red;
|
||||
color: $text-red;
|
||||
}
|
||||
}
|
||||
|
||||
.main .good pre {
|
||||
background-color: $bg-green-light;
|
||||
}
|
||||
|
||||
.main .bad pre {
|
||||
background-color: $red-000;
|
||||
}
|
||||
|
||||
@media all and (max-width: 768px) {
|
||||
body {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
height: auto;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.sidebar .expander {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.sidebar nav {
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.sidebar .nav-toggle:checked {
|
||||
& ~ nav {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
& + .expander .arrow {
|
||||
transform: rotate(-135deg);
|
||||
}
|
||||
}
|
||||
|
||||
.main {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
+643
-744
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,5 @@
|
||||
---
|
||||
---
|
||||
|
||||
@import "jekyll-theme-primer";
|
||||
@import "main";
|
||||
@@ -0,0 +1,7 @@
|
||||
# Community-Created Documentation
|
||||
|
||||
The following is a list, in no particular order, of links to documentation
|
||||
created by the Googletest community.
|
||||
|
||||
* [Googlemock Insights](https://github.com/ElectricRCAircraftGuy/eRCaGuy_dotfiles/blob/master/googletest/insights.md),
|
||||
by [ElectricRCAircraftGuy](https://github.com/ElectricRCAircraftGuy)
|
||||
+93
-175
@@ -1,9 +1,12 @@
|
||||
# Googletest FAQ
|
||||
|
||||
<!-- GOOGLETEST_CM0014 DO NOT DELETE -->
|
||||
# GoogleTest FAQ
|
||||
|
||||
## Why should test suite names and test names not contain underscore?
|
||||
|
||||
{: .callout .note}
|
||||
Note: GoogleTest reserves underscore (`_`) for special-purpose keywords, such as
|
||||
[the `DISABLED_` prefix](advanced.md#temporarily-disabling-tests), in addition
|
||||
to the following rationale.
|
||||
|
||||
Underscore (`_`) is special, as C++ reserves the following to be used by the
|
||||
compiler and the standard library:
|
||||
|
||||
@@ -30,9 +33,9 @@ contains `_`?
|
||||
`TestSuiteName_Bar__Test`, which is invalid.
|
||||
|
||||
So clearly `TestSuiteName` and `TestName` cannot start or end with `_`
|
||||
(Actually, `TestSuiteName` can start with `_` -- as long as the `_` isn't
|
||||
followed by an upper-case letter. But that's getting complicated. So for
|
||||
simplicity we just say that it cannot start with `_`.).
|
||||
(Actually, `TestSuiteName` can start with `_`—as long as the `_` isn't followed
|
||||
by an upper-case letter. But that's getting complicated. So for simplicity we
|
||||
just say that it cannot start with `_`.).
|
||||
|
||||
It may seem fine for `TestSuiteName` and `TestName` to contain `_` in the
|
||||
middle. However, consider this:
|
||||
@@ -47,42 +50,42 @@ Now, the two `TEST`s will both generate the same class
|
||||
|
||||
So for simplicity, we just ask the users to avoid `_` in `TestSuiteName` and
|
||||
`TestName`. The rule is more constraining than necessary, but it's simple and
|
||||
easy to remember. It also gives googletest some wiggle room in case its
|
||||
easy to remember. It also gives GoogleTest some wiggle room in case its
|
||||
implementation needs to change in the future.
|
||||
|
||||
If you violate the rule, there may not be immediate consequences, but your test
|
||||
may (just may) break with a new compiler (or a new version of the compiler you
|
||||
are using) or with a new version of googletest. Therefore it's best to follow
|
||||
are using) or with a new version of GoogleTest. Therefore it's best to follow
|
||||
the rule.
|
||||
|
||||
## Why does googletest support `EXPECT_EQ(NULL, ptr)` and `ASSERT_EQ(NULL, ptr)` but not `EXPECT_NE(NULL, ptr)` and `ASSERT_NE(NULL, ptr)`?
|
||||
## Why does GoogleTest support `EXPECT_EQ(NULL, ptr)` and `ASSERT_EQ(NULL, ptr)` but not `EXPECT_NE(NULL, ptr)` and `ASSERT_NE(NULL, ptr)`?
|
||||
|
||||
First of all you can use `EXPECT_NE(nullptr, ptr)` and `ASSERT_NE(nullptr,
|
||||
ptr)`. This is the preferred syntax in the style guide because nullptr does not
|
||||
have the type problems that NULL does. Which is why NULL does not work.
|
||||
First of all, you can use `nullptr` with each of these macros, e.g.
|
||||
`EXPECT_EQ(ptr, nullptr)`, `EXPECT_NE(ptr, nullptr)`, `ASSERT_EQ(ptr, nullptr)`,
|
||||
`ASSERT_NE(ptr, nullptr)`. This is the preferred syntax in the style guide
|
||||
because `nullptr` does not have the type problems that `NULL` does.
|
||||
|
||||
Due to some peculiarity of C++, it requires some non-trivial template meta
|
||||
programming tricks to support using `NULL` as an argument of the `EXPECT_XX()`
|
||||
and `ASSERT_XX()` macros. Therefore we only do it where it's most needed
|
||||
(otherwise we make the implementation of googletest harder to maintain and more
|
||||
(otherwise we make the implementation of GoogleTest harder to maintain and more
|
||||
error-prone than necessary).
|
||||
|
||||
The `EXPECT_EQ()` macro takes the *expected* value as its first argument and the
|
||||
*actual* value as the second. It's reasonable that someone wants to write
|
||||
`EXPECT_EQ(NULL, some_expression)`, and this indeed was requested several times.
|
||||
Therefore we implemented it.
|
||||
Historically, the `EXPECT_EQ()` macro took the *expected* value as its first
|
||||
argument and the *actual* value as the second, though this argument order is now
|
||||
discouraged. It was reasonable that someone wanted
|
||||
to write `EXPECT_EQ(NULL, some_expression)`, and this indeed was requested
|
||||
several times. Therefore we implemented it.
|
||||
|
||||
The need for `EXPECT_NE(NULL, ptr)` isn't nearly as strong. When the assertion
|
||||
The need for `EXPECT_NE(NULL, ptr)` wasn't nearly as strong. When the assertion
|
||||
fails, you already know that `ptr` must be `NULL`, so it doesn't add any
|
||||
information to print `ptr` in this case. That means `EXPECT_TRUE(ptr != NULL)`
|
||||
works just as well.
|
||||
|
||||
If we were to support `EXPECT_NE(NULL, ptr)`, for consistency we'll have to
|
||||
support `EXPECT_NE(ptr, NULL)` as well, as unlike `EXPECT_EQ`, we don't have a
|
||||
convention on the order of the two arguments for `EXPECT_NE`. This means using
|
||||
the template meta programming tricks twice in the implementation, making it even
|
||||
harder to understand and maintain. We believe the benefit doesn't justify the
|
||||
cost.
|
||||
If we were to support `EXPECT_NE(NULL, ptr)`, for consistency we'd have to
|
||||
support `EXPECT_NE(ptr, NULL)` as well. This means using the template meta
|
||||
programming tricks twice in the implementation, making it even harder to
|
||||
understand and maintain. We believe the benefit doesn't justify the cost.
|
||||
|
||||
Finally, with the growth of the gMock matcher library, we are encouraging people
|
||||
to use the unified `EXPECT_THAT(value, matcher)` syntax more often in tests. One
|
||||
@@ -125,29 +128,9 @@ both approaches a try. Practice is a much better way to grasp the subtle
|
||||
differences between the two tools. Once you have some concrete experience, you
|
||||
can much more easily decide which one to use the next time.
|
||||
|
||||
## I got some run-time errors about invalid proto descriptors when using `ProtocolMessageEquals`. Help!
|
||||
|
||||
**Note:** `ProtocolMessageEquals` and `ProtocolMessageEquiv` are *deprecated*
|
||||
now. Please use `EqualsProto`, etc instead.
|
||||
|
||||
`ProtocolMessageEquals` and `ProtocolMessageEquiv` were redefined recently and
|
||||
are now less tolerant of invalid protocol buffer definitions. In particular, if
|
||||
you have a `foo.proto` that doesn't fully qualify the type of a protocol message
|
||||
it references (e.g. `message<Bar>` where it should be `message<blah.Bar>`), you
|
||||
will now get run-time errors like:
|
||||
|
||||
```
|
||||
... descriptor.cc:...] Invalid proto descriptor for file "path/to/foo.proto":
|
||||
... descriptor.cc:...] blah.MyMessage.my_field: ".Bar" is not defined.
|
||||
```
|
||||
|
||||
If you see this, your `.proto` file is broken and needs to be fixed by making
|
||||
the types fully qualified. The new definition of `ProtocolMessageEquals` and
|
||||
`ProtocolMessageEquiv` just happen to reveal your bug.
|
||||
|
||||
## My death test modifies some state, but the change seems lost after the death test finishes. Why?
|
||||
|
||||
Death tests (`EXPECT_DEATH`, etc) are executed in a sub-process s.t. the
|
||||
Death tests (`EXPECT_DEATH`, etc.) are executed in a sub-process s.t. the
|
||||
expected crash won't kill the test program (i.e. the parent process). As a
|
||||
result, any in-memory side effects they incur are observable in their respective
|
||||
sub-processes, but not in the parent process. You can think of them as running
|
||||
@@ -158,7 +141,7 @@ methods, the parent process will think the calls have never occurred. Therefore,
|
||||
you may want to move your `EXPECT_CALL` statements inside the `EXPECT_DEATH`
|
||||
macro.
|
||||
|
||||
## EXPECT_EQ(htonl(blah), blah_blah) generates weird compiler errors in opt mode. Is this a googletest bug?
|
||||
## EXPECT_EQ(htonl(blah), blah_blah) generates weird compiler errors in opt mode. Is this a GoogleTest bug?
|
||||
|
||||
Actually, the bug is in `htonl()`.
|
||||
|
||||
@@ -176,18 +159,6 @@ template argument, and thus doesn't compile in opt mode when `a` contains a call
|
||||
to `htonl()`. It is difficult to make `EXPECT_EQ` bypass the `htonl()` bug, as
|
||||
the solution must work with different compilers on various platforms.
|
||||
|
||||
`htonl()` has some other problems as described in `//util/endian/endian.h`,
|
||||
which defines `ghtonl()` to replace it. `ghtonl()` does the same thing `htonl()`
|
||||
does, only without its problems. We suggest you to use `ghtonl()` instead of
|
||||
`htonl()`, both in your tests and production code.
|
||||
|
||||
`//util/endian/endian.h` also defines `ghtons()`, which solves similar problems
|
||||
in `htons()`.
|
||||
|
||||
Don't forget to add `//util/endian` to the list of dependencies in the `BUILD`
|
||||
file wherever `ghtonl()` and `ghtons()` are used. The library consists of a
|
||||
single header file and will not bloat your binary.
|
||||
|
||||
## The compiler complains about "undefined references" to some static const member variables, but I did define them in the class body. What's wrong?
|
||||
|
||||
If your class has a static data member:
|
||||
@@ -200,16 +171,28 @@ class Foo {
|
||||
};
|
||||
```
|
||||
|
||||
You also need to define it *outside* of the class body in `foo.cc`:
|
||||
you also need to define it *outside* of the class body in `foo.cc`:
|
||||
|
||||
```c++
|
||||
const int Foo::kBar; // No initializer here.
|
||||
```
|
||||
|
||||
Otherwise your code is **invalid C++**, and may break in unexpected ways. In
|
||||
particular, using it in googletest comparison assertions (`EXPECT_EQ`, etc) will
|
||||
generate an "undefined reference" linker error. The fact that "it used to work"
|
||||
doesn't mean it's valid. It just means that you were lucky. :-)
|
||||
particular, using it in GoogleTest comparison assertions (`EXPECT_EQ`, etc.)
|
||||
will generate an "undefined reference" linker error. The fact that "it used to
|
||||
work" doesn't mean it's valid. It just means that you were lucky. :-)
|
||||
|
||||
If the declaration of the static data member is `constexpr` then it is
|
||||
implicitly an `inline` definition, and a separate definition in `foo.cc` is not
|
||||
needed:
|
||||
|
||||
```c++
|
||||
// foo.h
|
||||
class Foo {
|
||||
...
|
||||
static constexpr int kBar = 100; // Defines kBar, no need to do it in foo.cc.
|
||||
};
|
||||
```
|
||||
|
||||
## Can I derive a test fixture from another?
|
||||
|
||||
@@ -221,7 +204,7 @@ cases may want to use the same or slightly different fixtures. For example, you
|
||||
may want to make sure that all of a GUI library's test suites don't leak
|
||||
important system resources like fonts and brushes.
|
||||
|
||||
In googletest, you share a fixture among test suites by putting the shared logic
|
||||
In GoogleTest, you share a fixture among test suites by putting the shared logic
|
||||
in a base test fixture, then deriving from that base a separate fixture for each
|
||||
test suite that wants to use this common logic. You then use `TEST_F()` to write
|
||||
tests using each derived fixture.
|
||||
@@ -260,10 +243,10 @@ TEST_F(FooTest, Baz) { ... }
|
||||
```
|
||||
|
||||
If necessary, you can continue to derive test fixtures from a derived fixture.
|
||||
googletest has no limit on how deep the hierarchy can be.
|
||||
GoogleTest has no limit on how deep the hierarchy can be.
|
||||
|
||||
For a complete example using derived test fixtures, see
|
||||
[sample5_unittest.cc](../samples/sample5_unittest.cc).
|
||||
[sample5_unittest.cc](https://github.com/google/googletest/blob/main/googletest/samples/sample5_unittest.cc).
|
||||
|
||||
## My compiler complains "void value not ignored as it ought to be." What does this mean?
|
||||
|
||||
@@ -274,9 +257,10 @@ disabled by our build system. Please see more details
|
||||
|
||||
## My death test hangs (or seg-faults). How do I fix it?
|
||||
|
||||
In googletest, death tests are run in a child process and the way they work is
|
||||
delicate. To write death tests you really need to understand how they work.
|
||||
Please make sure you have read [this](advanced.md#how-it-works).
|
||||
In GoogleTest, death tests are run in a child process and the way they work is
|
||||
delicate. To write death tests you really need to understand how they work—see
|
||||
the details at [Death Assertions](reference/assertions.md#death) in the
|
||||
Assertions Reference.
|
||||
|
||||
In particular, death tests don't like having multiple threads in the parent
|
||||
process. So the first thing you can try is to eliminate creating threads outside
|
||||
@@ -295,18 +279,18 @@ program from the beginning in the child process. Therefore make sure your
|
||||
program can run side-by-side with itself and is deterministic.
|
||||
|
||||
In the end, this boils down to good concurrent programming. You have to make
|
||||
sure that there is no race conditions or dead locks in your program. No silver
|
||||
sure that there are no race conditions or deadlocks in your program. No silver
|
||||
bullet - sorry!
|
||||
|
||||
## Should I use the constructor/destructor of the test fixture or SetUp()/TearDown()? {#CtorVsSetUp}
|
||||
|
||||
The first thing to remember is that googletest does **not** reuse the same test
|
||||
fixture object across multiple tests. For each `TEST_F`, googletest will create
|
||||
The first thing to remember is that GoogleTest does **not** reuse the same test
|
||||
fixture object across multiple tests. For each `TEST_F`, GoogleTest will create
|
||||
a **fresh** test fixture object, immediately call `SetUp()`, run the test body,
|
||||
call `TearDown()`, and then delete the test fixture object.
|
||||
|
||||
When you need to write per-test set-up and tear-down logic, you have the choice
|
||||
between using the test fixture constructor/destructor or `SetUp()/TearDown()`.
|
||||
between using the test fixture constructor/destructor or `SetUp()`/`TearDown()`.
|
||||
The former is usually preferred, as it has the following benefits:
|
||||
|
||||
* By initializing a member variable in the constructor, we have the option to
|
||||
@@ -323,7 +307,7 @@ You may still want to use `SetUp()/TearDown()` in the following cases:
|
||||
|
||||
* C++ does not allow virtual function calls in constructors and destructors.
|
||||
You can call a method declared as virtual, but it will not use dynamic
|
||||
dispatch, it will use the definition from the class the constructor of which
|
||||
dispatch. It will use the definition from the class the constructor of which
|
||||
is currently executing. This is because calling a virtual method before the
|
||||
derived class constructor has a chance to run is very dangerous - the
|
||||
virtual method might operate on uninitialized data. Therefore, if you need
|
||||
@@ -332,89 +316,25 @@ You may still want to use `SetUp()/TearDown()` in the following cases:
|
||||
* In the body of a constructor (or destructor), it's not possible to use the
|
||||
`ASSERT_xx` macros. Therefore, if the set-up operation could cause a fatal
|
||||
test failure that should prevent the test from running, it's necessary to
|
||||
use `abort` <!-- GOOGLETEST_CM0015 DO NOT DELETE --> and abort the whole test executable,
|
||||
or to use `SetUp()` instead of a constructor.
|
||||
use `abort` and abort the whole test
|
||||
executable, or to use `SetUp()` instead of a constructor.
|
||||
* If the tear-down operation could throw an exception, you must use
|
||||
`TearDown()` as opposed to the destructor, as throwing in a destructor leads
|
||||
to undefined behavior and usually will kill your program right away. Note
|
||||
that many standard libraries (like STL) may throw when exceptions are
|
||||
enabled in the compiler. Therefore you should prefer `TearDown()` if you
|
||||
want to write portable tests that work with or without exceptions.
|
||||
* The googletest team is considering making the assertion macros throw on
|
||||
* The GoogleTest team is considering making the assertion macros throw on
|
||||
platforms where exceptions are enabled (e.g. Windows, Mac OS, and Linux
|
||||
client-side), which will eliminate the need for the user to propagate
|
||||
failures from a subroutine to its caller. Therefore, you shouldn't use
|
||||
googletest assertions in a destructor if your code could run on such a
|
||||
GoogleTest assertions in a destructor if your code could run on such a
|
||||
platform.
|
||||
|
||||
## The compiler complains "no matching function to call" when I use ASSERT_PRED*. How do I fix it?
|
||||
## The compiler complains "no matching function to call" when I use `ASSERT_PRED*`. How do I fix it?
|
||||
|
||||
If the predicate function you use in `ASSERT_PRED*` or `EXPECT_PRED*` is
|
||||
overloaded or a template, the compiler will have trouble figuring out which
|
||||
overloaded version it should use. `ASSERT_PRED_FORMAT*` and
|
||||
`EXPECT_PRED_FORMAT*` don't have this problem.
|
||||
|
||||
If you see this error, you might want to switch to
|
||||
`(ASSERT|EXPECT)_PRED_FORMAT*`, which will also give you a better failure
|
||||
message. If, however, that is not an option, you can resolve the problem by
|
||||
explicitly telling the compiler which version to pick.
|
||||
|
||||
For example, suppose you have
|
||||
|
||||
```c++
|
||||
bool IsPositive(int n) {
|
||||
return n > 0;
|
||||
}
|
||||
|
||||
bool IsPositive(double x) {
|
||||
return x > 0;
|
||||
}
|
||||
```
|
||||
|
||||
you will get a compiler error if you write
|
||||
|
||||
```c++
|
||||
EXPECT_PRED1(IsPositive, 5);
|
||||
```
|
||||
|
||||
However, this will work:
|
||||
|
||||
```c++
|
||||
EXPECT_PRED1(static_cast<bool (*)(int)>(IsPositive), 5);
|
||||
```
|
||||
|
||||
(The stuff inside the angled brackets for the `static_cast` operator is the type
|
||||
of the function pointer for the `int`-version of `IsPositive()`.)
|
||||
|
||||
As another example, when you have a template function
|
||||
|
||||
```c++
|
||||
template <typename T>
|
||||
bool IsNegative(T x) {
|
||||
return x < 0;
|
||||
}
|
||||
```
|
||||
|
||||
you can use it in a predicate assertion like this:
|
||||
|
||||
```c++
|
||||
ASSERT_PRED1(IsNegative<int>, -5);
|
||||
```
|
||||
|
||||
Things are more interesting if your template has more than one parameters. The
|
||||
following won't compile:
|
||||
|
||||
```c++
|
||||
ASSERT_PRED2(GreaterThan<int, int>, 5, 0);
|
||||
```
|
||||
|
||||
as the C++ pre-processor thinks you are giving `ASSERT_PRED2` 4 arguments, which
|
||||
is one more than expected. The workaround is to wrap the predicate function in
|
||||
parentheses:
|
||||
|
||||
```c++
|
||||
ASSERT_PRED2((GreaterThan<int, int>), 5, 0);
|
||||
```
|
||||
See details for [`EXPECT_PRED*`](reference/assertions.md#EXPECT_PRED) in the
|
||||
Assertions Reference.
|
||||
|
||||
## My compiler complains about "ignoring return value" when I call RUN_ALL_TESTS(). Why?
|
||||
|
||||
@@ -434,7 +354,7 @@ they write
|
||||
This is **wrong and dangerous**. The testing services needs to see the return
|
||||
value of `RUN_ALL_TESTS()` in order to determine if a test has passed. If your
|
||||
`main()` function ignores it, your test will be considered successful even if it
|
||||
has a googletest assertion failure. Very bad.
|
||||
has a GoogleTest assertion failure. Very bad.
|
||||
|
||||
We have decided to fix this (thanks to Michael Chastain for the idea). Now, your
|
||||
code will no longer be able to ignore `RUN_ALL_TESTS()` when compiled with
|
||||
@@ -469,8 +389,7 @@ C++ is case-sensitive. Did you spell it as `Setup()`?
|
||||
Similarly, sometimes people spell `SetUpTestSuite()` as `SetupTestSuite()` and
|
||||
wonder why it's never called.
|
||||
|
||||
|
||||
## I have several test suites which share the same test fixture logic, do I have to define a new test fixture class for each of them? This seems pretty tedious.
|
||||
## I have several test suites which share the same test fixture logic; do I have to define a new test fixture class for each of them? This seems pretty tedious.
|
||||
|
||||
You don't have to. Instead of
|
||||
|
||||
@@ -500,14 +419,14 @@ TEST_F(BarTest, Abc) { ... }
|
||||
TEST_F(BarTest, Def) { ... }
|
||||
```
|
||||
|
||||
## googletest output is buried in a whole bunch of LOG messages. What do I do?
|
||||
## GoogleTest output is buried in a whole bunch of LOG messages. What do I do?
|
||||
|
||||
The googletest output is meant to be a concise and human-friendly report. If
|
||||
your test generates textual output itself, it will mix with the googletest
|
||||
The GoogleTest output is meant to be a concise and human-friendly report. If
|
||||
your test generates textual output itself, it will mix with the GoogleTest
|
||||
output, making it hard to read. However, there is an easy solution to this
|
||||
problem.
|
||||
|
||||
Since `LOG` messages go to stderr, we decided to let googletest output go to
|
||||
Since `LOG` messages go to stderr, we decided to let GoogleTest output go to
|
||||
stdout. This way, you can easily separate the two using redirection. For
|
||||
example:
|
||||
|
||||
@@ -531,8 +450,8 @@ There are several good reasons:
|
||||
|
||||
## What can the statement argument in ASSERT_DEATH() be?
|
||||
|
||||
`ASSERT_DEATH(*statement*, *regex*)` (or any death assertion macro) can be used
|
||||
wherever `*statement*` is valid. So basically `*statement*` can be any C++
|
||||
`ASSERT_DEATH(statement, matcher)` (or any death assertion macro) can be used
|
||||
wherever *`statement`* is valid. So basically *`statement`* can be any C++
|
||||
statement that makes sense in the current context. In particular, it can
|
||||
reference global and/or local variables, and can be:
|
||||
|
||||
@@ -555,7 +474,7 @@ TEST(MyDeathTest, ComplexExpression) {
|
||||
"(Func1|Method) failed");
|
||||
}
|
||||
|
||||
// Death assertions can be used any where in a function. In
|
||||
// Death assertions can be used anywhere in a function. In
|
||||
// particular, they can be inside a loop.
|
||||
TEST(MyDeathTest, InsideLoop) {
|
||||
// Verifies that Foo(0), Foo(1), ..., and Foo(4) all die.
|
||||
@@ -578,11 +497,9 @@ TEST(MyDeathTest, CompoundStatement) {
|
||||
}
|
||||
```
|
||||
|
||||
gtest-death-test_test.cc contains more examples if you are interested.
|
||||
|
||||
## I have a fixture class `FooTest`, but `TEST_F(FooTest, Bar)` gives me error ``"no matching function for call to `FooTest::FooTest()'"``. Why?
|
||||
|
||||
Googletest needs to be able to create objects of your test fixture class, so it
|
||||
GoogleTest needs to be able to create objects of your test fixture class, so it
|
||||
must have a default constructor. Normally the compiler will define one for you.
|
||||
However, there are cases where you have to define your own:
|
||||
|
||||
@@ -597,7 +514,7 @@ However, there are cases where you have to define your own:
|
||||
## Why does ASSERT_DEATH complain about previous threads that were already joined?
|
||||
|
||||
With the Linux pthread library, there is no turning back once you cross the line
|
||||
from single thread to multiple threads. The first time you create a thread, a
|
||||
from a single thread to multiple threads. The first time you create a thread, a
|
||||
manager thread is created in addition, so you get 3, not 2, threads. Later when
|
||||
the thread you create joins the main thread, the thread count decrements by 1,
|
||||
but the manager thread will never be killed, so you still have 2 threads, which
|
||||
@@ -607,12 +524,12 @@ The new NPTL thread library doesn't suffer from this problem, as it doesn't
|
||||
create a manager thread. However, if you don't control which machine your test
|
||||
runs on, you shouldn't depend on this.
|
||||
|
||||
## Why does googletest require the entire test suite, instead of individual tests, to be named *DeathTest when it uses ASSERT_DEATH?
|
||||
## Why does GoogleTest require the entire test suite, instead of individual tests, to be named `*DeathTest` when it uses `ASSERT_DEATH`?
|
||||
|
||||
googletest does not interleave tests from different test suites. That is, it
|
||||
GoogleTest does not interleave tests from different test suites. That is, it
|
||||
runs all tests in one test suite first, and then runs all tests in the next test
|
||||
suite, and so on. googletest does this because it needs to set up a test suite
|
||||
before the first test in it is run, and tear it down afterwords. Splitting up
|
||||
suite, and so on. GoogleTest does this because it needs to set up a test suite
|
||||
before the first test in it is run, and tear it down afterwards. Splitting up
|
||||
the test case would require multiple set-up and tear-down processes, which is
|
||||
inefficient and makes the semantics unclean.
|
||||
|
||||
@@ -632,7 +549,7 @@ interleave tests from different test suites, we need to run all tests in the
|
||||
`FooTest` case before running any test in the `BarTest` case. This contradicts
|
||||
with the requirement to run `BarTest.DefDeathTest` before `FooTest.Uvw`.
|
||||
|
||||
## But I don't like calling my entire test suite \*DeathTest when it contains both death tests and non-death tests. What do I do?
|
||||
## But I don't like calling my entire test suite `*DeathTest` when it contains both death tests and non-death tests. What do I do?
|
||||
|
||||
You don't have to, but if you like, you may split up the test suite into
|
||||
`FooTest` and `FooDeathTest`, where the names make it clear that they are
|
||||
@@ -650,29 +567,30 @@ TEST_F(FooDeathTest, Uvw) { ... EXPECT_DEATH(...) ... }
|
||||
TEST_F(FooDeathTest, Xyz) { ... ASSERT_DEATH(...) ... }
|
||||
```
|
||||
|
||||
## googletest prints the LOG messages in a death test's child process only when the test fails. How can I see the LOG messages when the death test succeeds?
|
||||
## GoogleTest prints the LOG messages in a death test's child process only when the test fails. How can I see the LOG messages when the death test succeeds?
|
||||
|
||||
Printing the LOG messages generated by the statement inside `EXPECT_DEATH()`
|
||||
makes it harder to search for real problems in the parent's log. Therefore,
|
||||
googletest only prints them when the death test has failed.
|
||||
GoogleTest only prints them when the death test has failed.
|
||||
|
||||
If you really need to see such LOG messages, a workaround is to temporarily
|
||||
break the death test (e.g. by changing the regex pattern it is expected to
|
||||
match). Admittedly, this is a hack. We'll consider a more permanent solution
|
||||
after the fork-and-exec-style death tests are implemented.
|
||||
|
||||
## The compiler complains about "no match for 'operator<<'" when I use an assertion. What gives?
|
||||
## The compiler complains about `no match for 'operator<<'` when I use an assertion. What gives?
|
||||
|
||||
If you use a user-defined type `FooType` in an assertion, you must make sure
|
||||
there is an `std::ostream& operator<<(std::ostream&, const FooType&)` function
|
||||
defined such that we can print a value of `FooType`.
|
||||
|
||||
In addition, if `FooType` is declared in a name space, the `<<` operator also
|
||||
needs to be defined in the *same* name space. See https://abseil.io/tips/49 for details.
|
||||
needs to be defined in the *same* name space. See
|
||||
[Tip of the Week #49](https://abseil.io/tips/49) for details.
|
||||
|
||||
## How do I suppress the memory leak messages on Windows?
|
||||
|
||||
Since the statically initialized googletest singleton requires allocations on
|
||||
Since the statically initialized GoogleTest singleton requires allocations on
|
||||
the heap, the Visual C++ memory leak detector will report memory leaks at the
|
||||
end of the program run. The easiest way to avoid this is to use the
|
||||
`_CrtMemCheckpoint` and `_CrtMemDumpAllObjectsSince` calls to not report any
|
||||
@@ -686,7 +604,7 @@ things accordingly, you are leaking test-only logic into production code and
|
||||
there is no easy way to ensure that the test-only code paths aren't run by
|
||||
mistake in production. Such cleverness also leads to
|
||||
[Heisenbugs](https://en.wikipedia.org/wiki/Heisenbug). Therefore we strongly
|
||||
advise against the practice, and googletest doesn't provide a way to do it.
|
||||
advise against the practice, and GoogleTest doesn't provide a way to do it.
|
||||
|
||||
In general, the recommended way to cause the code to behave differently under
|
||||
test is [Dependency Injection](https://en.wikipedia.org/wiki/Dependency_injection). You can inject
|
||||
@@ -703,19 +621,19 @@ whether the code is under test.
|
||||
## How do I temporarily disable a test?
|
||||
|
||||
If you have a broken test that you cannot fix right away, you can add the
|
||||
DISABLED_ prefix to its name. This will exclude it from execution. This is
|
||||
better than commenting out the code or using #if 0, as disabled tests are still
|
||||
compiled (and thus won't rot).
|
||||
`DISABLED_` prefix to its name. This will exclude it from execution. This is
|
||||
better than commenting out the code or using `#if 0`, as disabled tests are
|
||||
still compiled (and thus won't rot).
|
||||
|
||||
To include disabled tests in test execution, just invoke the test program with
|
||||
the --gtest_also_run_disabled_tests flag.
|
||||
the `--gtest_also_run_disabled_tests` flag.
|
||||
|
||||
## Is it OK if I have two separate `TEST(Foo, Bar)` test methods defined in different namespaces?
|
||||
|
||||
Yes.
|
||||
|
||||
The rule is **all test methods in the same test suite must use the same fixture
|
||||
class.** This means that the following is **allowed** because both tests use the
|
||||
class**. This means that the following is **allowed** because both tests use the
|
||||
same fixture class (`::testing::Test`).
|
||||
|
||||
```c++
|
||||
@@ -733,7 +651,7 @@ TEST(CoolTest, DoSomething) {
|
||||
```
|
||||
|
||||
However, the following code is **not allowed** and will produce a runtime error
|
||||
from googletest because the test methods are using different test fixture
|
||||
from GoogleTest because the test methods are using different test fixture
|
||||
classes with the same test suite name.
|
||||
|
||||
```c++
|
||||
+241
@@ -0,0 +1,241 @@
|
||||
# gMock Cheat Sheet
|
||||
|
||||
## Defining a Mock Class
|
||||
|
||||
### Mocking a Normal Class {#MockClass}
|
||||
|
||||
Given
|
||||
|
||||
```cpp
|
||||
class Foo {
|
||||
public:
|
||||
virtual ~Foo();
|
||||
virtual int GetSize() const = 0;
|
||||
virtual string Describe(const char* name) = 0;
|
||||
virtual string Describe(int type) = 0;
|
||||
virtual bool Process(Bar elem, int count) = 0;
|
||||
};
|
||||
```
|
||||
|
||||
(note that `~Foo()` **must** be virtual) we can define its mock as
|
||||
|
||||
```cpp
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
class MockFoo : public Foo {
|
||||
public:
|
||||
MOCK_METHOD(int, GetSize, (), (const, override));
|
||||
MOCK_METHOD(string, Describe, (const char* name), (override));
|
||||
MOCK_METHOD(string, Describe, (int type), (override));
|
||||
MOCK_METHOD(bool, Process, (Bar elem, int count), (override));
|
||||
};
|
||||
```
|
||||
|
||||
To create a "nice" mock, which ignores all uninteresting calls, a "naggy" mock,
|
||||
which warns on all uninteresting calls, or a "strict" mock, which treats them as
|
||||
failures:
|
||||
|
||||
```cpp
|
||||
using ::testing::NiceMock;
|
||||
using ::testing::NaggyMock;
|
||||
using ::testing::StrictMock;
|
||||
|
||||
NiceMock<MockFoo> nice_foo; // The type is a subclass of MockFoo.
|
||||
NaggyMock<MockFoo> naggy_foo; // The type is a subclass of MockFoo.
|
||||
StrictMock<MockFoo> strict_foo; // The type is a subclass of MockFoo.
|
||||
```
|
||||
|
||||
{: .callout .note}
|
||||
**Note:** A mock object is currently naggy by default. We may make it nice by
|
||||
default in the future.
|
||||
|
||||
### Mocking a Class Template {#MockTemplate}
|
||||
|
||||
Class templates can be mocked just like any class.
|
||||
|
||||
To mock
|
||||
|
||||
```cpp
|
||||
template <typename Elem>
|
||||
class StackInterface {
|
||||
public:
|
||||
virtual ~StackInterface();
|
||||
virtual int GetSize() const = 0;
|
||||
virtual void Push(const Elem& x) = 0;
|
||||
};
|
||||
```
|
||||
|
||||
(note that all member functions that are mocked, including `~StackInterface()`
|
||||
**must** be virtual).
|
||||
|
||||
```cpp
|
||||
template <typename Elem>
|
||||
class MockStack : public StackInterface<Elem> {
|
||||
public:
|
||||
MOCK_METHOD(int, GetSize, (), (const, override));
|
||||
MOCK_METHOD(void, Push, (const Elem& x), (override));
|
||||
};
|
||||
```
|
||||
|
||||
### Specifying Calling Conventions for Mock Functions
|
||||
|
||||
If your mock function doesn't use the default calling convention, you can
|
||||
specify it by adding `Calltype(convention)` to `MOCK_METHOD`'s 4th parameter.
|
||||
For example,
|
||||
|
||||
```cpp
|
||||
MOCK_METHOD(bool, Foo, (int n), (Calltype(STDMETHODCALLTYPE)));
|
||||
MOCK_METHOD(int, Bar, (double x, double y),
|
||||
(const, Calltype(STDMETHODCALLTYPE)));
|
||||
```
|
||||
|
||||
where `STDMETHODCALLTYPE` is defined by `<objbase.h>` on Windows.
|
||||
|
||||
## Using Mocks in Tests {#UsingMocks}
|
||||
|
||||
The typical work flow is:
|
||||
|
||||
1. Import the gMock names you need to use. All gMock symbols are in the
|
||||
`testing` namespace unless they are macros or otherwise noted.
|
||||
2. Create the mock objects.
|
||||
3. Optionally, set the default actions of the mock objects.
|
||||
4. Set your expectations on the mock objects (How will they be called? What
|
||||
will they do?).
|
||||
5. Exercise code that uses the mock objects; if necessary, check the result
|
||||
using googletest assertions.
|
||||
6. When a mock object is destructed, gMock automatically verifies that all
|
||||
expectations on it have been satisfied.
|
||||
|
||||
Here's an example:
|
||||
|
||||
```cpp
|
||||
using ::testing::Return; // #1
|
||||
|
||||
TEST(BarTest, DoesThis) {
|
||||
MockFoo foo; // #2
|
||||
|
||||
ON_CALL(foo, GetSize()) // #3
|
||||
.WillByDefault(Return(1));
|
||||
// ... other default actions ...
|
||||
|
||||
EXPECT_CALL(foo, Describe(5)) // #4
|
||||
.Times(3)
|
||||
.WillRepeatedly(Return("Category 5"));
|
||||
// ... other expectations ...
|
||||
|
||||
EXPECT_EQ(MyProductionFunction(&foo), "good"); // #5
|
||||
} // #6
|
||||
```
|
||||
|
||||
## Setting Default Actions {#OnCall}
|
||||
|
||||
gMock has a **built-in default action** for any function that returns `void`,
|
||||
`bool`, a numeric value, or a pointer. In C++11, it will additionally returns
|
||||
the default-constructed value, if one exists for the given type.
|
||||
|
||||
To customize the default action for functions with return type `T`, use
|
||||
[`DefaultValue<T>`](reference/mocking.md#DefaultValue). For example:
|
||||
|
||||
```cpp
|
||||
// Sets the default action for return type std::unique_ptr<Buzz> to
|
||||
// creating a new Buzz every time.
|
||||
DefaultValue<std::unique_ptr<Buzz>>::SetFactory(
|
||||
[] { return std::make_unique<Buzz>(AccessLevel::kInternal); });
|
||||
|
||||
// When this fires, the default action of MakeBuzz() will run, which
|
||||
// will return a new Buzz object.
|
||||
EXPECT_CALL(mock_buzzer_, MakeBuzz("hello")).Times(AnyNumber());
|
||||
|
||||
auto buzz1 = mock_buzzer_.MakeBuzz("hello");
|
||||
auto buzz2 = mock_buzzer_.MakeBuzz("hello");
|
||||
EXPECT_NE(buzz1, nullptr);
|
||||
EXPECT_NE(buzz2, nullptr);
|
||||
EXPECT_NE(buzz1, buzz2);
|
||||
|
||||
// Resets the default action for return type std::unique_ptr<Buzz>,
|
||||
// to avoid interfere with other tests.
|
||||
DefaultValue<std::unique_ptr<Buzz>>::Clear();
|
||||
```
|
||||
|
||||
To customize the default action for a particular method of a specific mock
|
||||
object, use [`ON_CALL`](reference/mocking.md#ON_CALL). `ON_CALL` has a similar
|
||||
syntax to `EXPECT_CALL`, but it is used for setting default behaviors when you
|
||||
do not require that the mock method is called. See
|
||||
[Knowing When to Expect](gmock_cook_book.md#UseOnCall) for a more detailed
|
||||
discussion.
|
||||
|
||||
## Setting Expectations {#ExpectCall}
|
||||
|
||||
See [`EXPECT_CALL`](reference/mocking.md#EXPECT_CALL) in the Mocking Reference.
|
||||
|
||||
## Matchers {#MatcherList}
|
||||
|
||||
See the [Matchers Reference](reference/matchers.md).
|
||||
|
||||
## Actions {#ActionList}
|
||||
|
||||
See the [Actions Reference](reference/actions.md).
|
||||
|
||||
## Cardinalities {#CardinalityList}
|
||||
|
||||
See the [`Times` clause](reference/mocking.md#EXPECT_CALL.Times) of
|
||||
`EXPECT_CALL` in the Mocking Reference.
|
||||
|
||||
## Expectation Order
|
||||
|
||||
By default, expectations can be matched in *any* order. If some or all
|
||||
expectations must be matched in a given order, you can use the
|
||||
[`After` clause](reference/mocking.md#EXPECT_CALL.After) or
|
||||
[`InSequence` clause](reference/mocking.md#EXPECT_CALL.InSequence) of
|
||||
`EXPECT_CALL`, or use an [`InSequence` object](reference/mocking.md#InSequence).
|
||||
|
||||
## Verifying and Resetting a Mock
|
||||
|
||||
gMock will verify the expectations on a mock object when it is destructed, or
|
||||
you can do it earlier:
|
||||
|
||||
```cpp
|
||||
using ::testing::Mock;
|
||||
...
|
||||
// Verifies and removes the expectations on mock_obj;
|
||||
// returns true if and only if successful.
|
||||
Mock::VerifyAndClearExpectations(&mock_obj);
|
||||
...
|
||||
// Verifies and removes the expectations on mock_obj;
|
||||
// also removes the default actions set by ON_CALL();
|
||||
// returns true if and only if successful.
|
||||
Mock::VerifyAndClear(&mock_obj);
|
||||
```
|
||||
|
||||
Do not set new expectations after verifying and clearing a mock after its use.
|
||||
Setting expectations after code that exercises the mock has undefined behavior.
|
||||
See [Using Mocks in Tests](gmock_for_dummies.md#using-mocks-in-tests) for more
|
||||
information.
|
||||
|
||||
You can also tell gMock that a mock object can be leaked and doesn't need to be
|
||||
verified:
|
||||
|
||||
```cpp
|
||||
Mock::AllowLeak(&mock_obj);
|
||||
```
|
||||
|
||||
## Mock Classes
|
||||
|
||||
gMock defines a convenient mock class template
|
||||
|
||||
```cpp
|
||||
class MockFunction<R(A1, ..., An)> {
|
||||
public:
|
||||
MOCK_METHOD(R, Call, (A1, ..., An));
|
||||
};
|
||||
```
|
||||
|
||||
See this [recipe](gmock_cook_book.md#UsingCheckPoints) for one application of
|
||||
it.
|
||||
|
||||
## Flags
|
||||
|
||||
| Flag | Description |
|
||||
| :----------------------------- | :---------------------------------------- |
|
||||
| `--gmock_catch_leaked_mocks=0` | Don't report leaked mock objects as failures. |
|
||||
| `--gmock_verbose=LEVEL` | Sets the default verbosity level (`info`, `warning`, or `error`) of Google Mock messages. |
|
||||
Vendored
+558
-416
File diff suppressed because it is too large
Load Diff
+7
-13
@@ -1,11 +1,9 @@
|
||||
## Legacy gMock FAQ {#GMockFaq}
|
||||
|
||||
<!-- GOOGLETEST_CM0021 DO NOT DELETE -->
|
||||
# Legacy gMock FAQ
|
||||
|
||||
### When I call a method on my mock object, the method for the real object is invoked instead. What's the problem?
|
||||
|
||||
In order for a method to be mocked, it must be *virtual*, unless you use the
|
||||
[high-perf dependency injection technique](#MockingNonVirtualMethods).
|
||||
[high-perf dependency injection technique](gmock_cook_book.md#MockingNonVirtualMethods).
|
||||
|
||||
### Can I mock a variadic function?
|
||||
|
||||
@@ -81,8 +79,6 @@ void Bar(int* p); // Neither p nor *p is const.
|
||||
void Bar(const int* p); // p is not const, but *p is.
|
||||
```
|
||||
|
||||
<!-- GOOGLETEST_CM0030 DO NOT DELETE -->
|
||||
|
||||
### I can't figure out why gMock thinks my expectations are not satisfied. What should I do?
|
||||
|
||||
You might want to run your test with `--gmock_verbose=info`. This flag lets
|
||||
@@ -91,7 +87,7 @@ trace, you'll gain insights on why the expectations you set are not met.
|
||||
|
||||
If you see the message "The mock function has no default action set, and its
|
||||
return type has no default value set.", then try
|
||||
[adding a default action](for_dummies.md#DefaultValue). Due to a known issue,
|
||||
[adding a default action](gmock_cheat_sheet.md#OnCall). Due to a known issue,
|
||||
unexpected calls on mocks without default actions don't print out a detailed
|
||||
comparison between the actual arguments and the expected arguments.
|
||||
|
||||
@@ -126,8 +122,6 @@ using ::testing::_;
|
||||
.Times(0);
|
||||
```
|
||||
|
||||
<!-- GOOGLETEST_CM0031 DO NOT DELETE -->
|
||||
|
||||
### I have a failed test where gMock tells me TWICE that a particular expectation is not satisfied. Isn't this redundant?
|
||||
|
||||
When gMock detects a failure, it prints relevant information (the mock function
|
||||
@@ -375,8 +369,8 @@ Usually, if your action is for a particular function type, defining it using
|
||||
different types (e.g. if you are defining `Return(*value*)`),
|
||||
`MakePolymorphicAction()` is easiest. Sometimes you want precise control on what
|
||||
types of functions the action can be used in, and implementing `ActionInterface`
|
||||
is the way to go here. See the implementation of `Return()` in
|
||||
`testing/base/public/gmock-actions.h` for an example.
|
||||
is the way to go here. See the implementation of `Return()` in `gmock-actions.h`
|
||||
for an example.
|
||||
|
||||
### I use SetArgPointee() in WillOnce(), but gcc complains about "conflicting return type specified". What does it mean?
|
||||
|
||||
@@ -386,8 +380,8 @@ doesn't say what the return value should be. You need `DoAll()` to chain a
|
||||
`SetArgPointee()` with a `Return()` that provides a value appropriate to the API
|
||||
being mocked.
|
||||
|
||||
See this [recipe](cook_book.md#mocking-side-effects) for more details and an
|
||||
example.
|
||||
See this [recipe](gmock_cook_book.md#mocking-side-effects) for more details and
|
||||
an example.
|
||||
|
||||
### I have a huge mock class, and Microsoft Visual C++ runs out of memory when compiling it. What can I do?
|
||||
|
||||
third_party/libgtest/googlemock/docs/for_dummies.md → third_party/libgtest/docs/gmock_for_dummies.md
Vendored
+59
-57
@@ -1,8 +1,6 @@
|
||||
## gMock for Dummies {#GMockForDummies}
|
||||
# gMock for Dummies
|
||||
|
||||
<!-- GOOGLETEST_CM0013 DO NOT DELETE -->
|
||||
|
||||
### What Is gMock?
|
||||
## What Is gMock?
|
||||
|
||||
When you write a prototype or test, often it's not feasible or wise to rely on
|
||||
real objects entirely. A **mock object** implements the same interface as a real
|
||||
@@ -10,9 +8,9 @@ object (so it can be used as one), but lets you specify at run time how it will
|
||||
be used and what it should do (which methods will be called? in which order? how
|
||||
many times? with what arguments? what will they return? etc).
|
||||
|
||||
**Note:** It is easy to confuse the term *fake objects* with mock objects. Fakes
|
||||
and mocks actually mean very different things in the Test-Driven Development
|
||||
(TDD) community:
|
||||
It is easy to confuse the term *fake objects* with mock objects. Fakes and mocks
|
||||
actually mean very different things in the Test-Driven Development (TDD)
|
||||
community:
|
||||
|
||||
* **Fake** objects have working implementations, but usually take some
|
||||
shortcut (perhaps to make the operations less expensive), which makes them
|
||||
@@ -39,7 +37,7 @@ When using gMock,
|
||||
3. then you exercise code that uses the mock objects. gMock will catch any
|
||||
violation to the expectations as soon as it arises.
|
||||
|
||||
### Why gMock?
|
||||
## Why gMock?
|
||||
|
||||
While mock objects help you remove unnecessary dependencies in tests and make
|
||||
them fast and reliable, using mocks manually in C++ is *hard*:
|
||||
@@ -53,9 +51,9 @@ them fast and reliable, using mocks manually in C++ is *hard*:
|
||||
one.
|
||||
|
||||
In contrast, Java and Python programmers have some fine mock frameworks (jMock,
|
||||
EasyMock, [Mox](http://wtf/mox), etc), which automate the creation of mocks. As
|
||||
a result, mocking is a proven effective technique and widely adopted practice in
|
||||
those communities. Having the right tool absolutely makes the difference.
|
||||
EasyMock, etc), which automate the creation of mocks. As a result, mocking is a
|
||||
proven effective technique and widely adopted practice in those communities.
|
||||
Having the right tool absolutely makes the difference.
|
||||
|
||||
gMock was built to help C++ programmers. It was inspired by jMock and EasyMock,
|
||||
but designed with C++'s specifics in mind. It is your friend if any of the
|
||||
@@ -85,28 +83,28 @@ We encourage you to use gMock as
|
||||
* a *testing* tool to cut your tests' outbound dependencies and probe the
|
||||
interaction between your module and its collaborators.
|
||||
|
||||
### Getting Started
|
||||
## Getting Started
|
||||
|
||||
gMock is bundled with googletest.
|
||||
|
||||
### A Case for Mock Turtles
|
||||
## A Case for Mock Turtles
|
||||
|
||||
Let's look at an example. Suppose you are developing a graphics program that
|
||||
relies on a [LOGO](http://en.wikipedia.org/wiki/Logo_programming_language)-like
|
||||
relies on a [LOGO](https://en.wikipedia.org/wiki/Logo_programming_language)-like
|
||||
API for drawing. How would you test that it does the right thing? Well, you can
|
||||
run it and compare the screen with a golden screen snapshot, but let's admit it:
|
||||
tests like this are expensive to run and fragile (What if you just upgraded to a
|
||||
shiny new graphics card that has better anti-aliasing? Suddenly you have to
|
||||
update all your golden images.). It would be too painful if all your tests are
|
||||
like this. Fortunately, you learned about
|
||||
[Dependency Injection](http://en.wikipedia.org/wiki/Dependency_injection) and know the right thing
|
||||
[Dependency Injection](https://en.wikipedia.org/wiki/Dependency_injection) and know the right thing
|
||||
to do: instead of having your application talk to the system API directly, wrap
|
||||
the API in an interface (say, `Turtle`) and code to that interface:
|
||||
|
||||
```cpp
|
||||
class Turtle {
|
||||
...
|
||||
virtual ~Turtle() {};
|
||||
virtual ~Turtle() {}
|
||||
virtual void PenUp() = 0;
|
||||
virtual void PenDown() = 0;
|
||||
virtual void Forward(int distance) = 0;
|
||||
@@ -135,20 +133,20 @@ because your new machine does anti-aliasing differently), easier to read and
|
||||
maintain (the intent of a test is expressed in the code, not in some binary
|
||||
images), and run *much, much faster*.
|
||||
|
||||
### Writing the Mock Class
|
||||
## Writing the Mock Class
|
||||
|
||||
If you are lucky, the mocks you need to use have already been implemented by
|
||||
some nice people. If, however, you find yourself in the position to write a mock
|
||||
class, relax - gMock turns this task into a fun game! (Well, almost.)
|
||||
|
||||
#### How to Define It
|
||||
### How to Define It
|
||||
|
||||
Using the `Turtle` interface as example, here are the simple steps you need to
|
||||
follow:
|
||||
|
||||
* Derive a class `MockTurtle` from `Turtle`.
|
||||
* Take a *virtual* function of `Turtle` (while it's possible to
|
||||
[mock non-virtual methods using templates](cook_book.md#MockingNonVirtualMethods),
|
||||
[mock non-virtual methods using templates](gmock_cook_book.md#MockingNonVirtualMethods),
|
||||
it's much more involved).
|
||||
* In the `public:` section of the child class, write `MOCK_METHOD();`
|
||||
* Now comes the fun part: you take the function signature, cut-and-paste it
|
||||
@@ -166,7 +164,7 @@ follow:
|
||||
After the process, you should have something like:
|
||||
|
||||
```cpp
|
||||
#include "gmock/gmock.h" // Brings in gMock.
|
||||
#include <gmock/gmock.h> // Brings in gMock.
|
||||
|
||||
class MockTurtle : public Turtle {
|
||||
public:
|
||||
@@ -184,7 +182,7 @@ class MockTurtle : public Turtle {
|
||||
You don't need to define these mock methods somewhere else - the `MOCK_METHOD`
|
||||
macro will generate the definitions for you. It's that simple!
|
||||
|
||||
#### Where to Put It
|
||||
### Where to Put It
|
||||
|
||||
When you define a mock class, you need to decide where to put its definition.
|
||||
Some people put it in a `_test.cc`. This is fine when the interface being mocked
|
||||
@@ -192,10 +190,10 @@ Some people put it in a `_test.cc`. This is fine when the interface being mocked
|
||||
`Foo` changes it, your test could break. (You can't really expect `Foo`'s
|
||||
maintainer to fix every test that uses `Foo`, can you?)
|
||||
|
||||
So, the rule of thumb is: if you need to mock `Foo` and it's owned by others,
|
||||
define the mock class in `Foo`'s package (better, in a `testing` sub-package
|
||||
such that you can clearly separate production code and testing utilities), put
|
||||
it in a `.h` and a `cc_library`. Then everyone can reference them from their
|
||||
Generally, you should not mock classes you don't own. If you must mock such a
|
||||
class owned by others, define the mock class in `Foo`'s Bazel package (usually
|
||||
the same directory or a `testing` sub-directory), and put it in a `.h` and a
|
||||
`cc_library` with `testonly=True`. Then everyone can reference them from their
|
||||
tests. If `Foo` ever changes, there is only one copy of `MockFoo` to change, and
|
||||
only tests that depend on the changed methods need to be fixed.
|
||||
|
||||
@@ -206,14 +204,12 @@ choosing the adaptor interface can make your code easier to write and more
|
||||
readable (a net win in the long run), as you can choose `FooAdaptor` to fit your
|
||||
specific domain much better than `Foo` does.
|
||||
|
||||
<!-- GOOGLETEST_CM0029 DO NOT DELETE -->
|
||||
|
||||
### Using Mocks in Tests
|
||||
## Using Mocks in Tests
|
||||
|
||||
Once you have a mock class, using it is easy. The typical work flow is:
|
||||
|
||||
1. Import the gMock names from the `testing` namespace such that you can use
|
||||
them unqualified (You only have to do it once per file. Remember that
|
||||
them unqualified (You only have to do it once per file). Remember that
|
||||
namespaces are a good idea.
|
||||
2. Create some mock objects.
|
||||
3. Specify your expectations on them (How many times will a method be called?
|
||||
@@ -228,8 +224,8 @@ Here's an example:
|
||||
|
||||
```cpp
|
||||
#include "path/to/mock-turtle.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using ::testing::AtLeast; // #1
|
||||
|
||||
@@ -257,17 +253,20 @@ Stack trace:
|
||||
...
|
||||
```
|
||||
|
||||
**Tip 1:** If you run the test from an Emacs buffer, you can hit <Enter> on the
|
||||
line number to jump right to the failed expectation.
|
||||
**Tip 1:** If you run the test from an Emacs buffer, you can hit `<Enter>` on
|
||||
the line number to jump right to the failed expectation.
|
||||
|
||||
**Tip 2:** If your mock objects are never deleted, the final verification won't
|
||||
happen. Therefore it's a good idea to turn on the heap checker in your tests
|
||||
when you allocate mocks on the heap. You get that automatically if you use the
|
||||
`gtest_main` library already.
|
||||
|
||||
###### Expectation Ordering
|
||||
|
||||
**Important note:** gMock requires expectations to be set **before** the mock
|
||||
functions are called, otherwise the behavior is **undefined**. In particular,
|
||||
you mustn't interleave `EXPECT_CALL()s` and calls to the mock functions.
|
||||
functions are called, otherwise the behavior is **undefined**. Do not alternate
|
||||
between calls to `EXPECT_CALL()` and calls to the mock functions, and do not set
|
||||
any expectations on a mock after passing the mock to an API.
|
||||
|
||||
This means `EXPECT_CALL()` should be read as expecting that a call will occur
|
||||
*in the future*, not that a call has occurred. Why does gMock work like that?
|
||||
@@ -279,7 +278,7 @@ Admittedly, this test is contrived and doesn't do much. You can easily achieve
|
||||
the same effect without using gMock. However, as we shall reveal soon, gMock
|
||||
allows you to do *so much more* with the mocks.
|
||||
|
||||
### Setting Expectations
|
||||
## Setting Expectations
|
||||
|
||||
The key to using a mock object successfully is to set the *right expectations*
|
||||
on it. If you set the expectations too strict, your test will fail as the result
|
||||
@@ -288,7 +287,7 @@ to do it just right such that your test can catch exactly the kind of bugs you
|
||||
intend it to catch. gMock provides the necessary means for you to do it "just
|
||||
right."
|
||||
|
||||
#### General Syntax
|
||||
### General Syntax
|
||||
|
||||
In gMock we use the `EXPECT_CALL()` macro to set an expectation on a mock
|
||||
method. The general syntax is:
|
||||
@@ -314,8 +313,8 @@ EXPECT_CALL(mock_object, non-overloaded-method)
|
||||
|
||||
This syntax allows the test writer to specify "called with any arguments"
|
||||
without explicitly specifying the number or types of arguments. To avoid
|
||||
unintended ambiguity, this syntax may only be used for methods which are not
|
||||
overloaded
|
||||
unintended ambiguity, this syntax may only be used for methods that are not
|
||||
overloaded.
|
||||
|
||||
Either form of the macro can be followed by some optional *clauses* that provide
|
||||
more information about the expectation. We'll discuss how each clause works in
|
||||
@@ -338,12 +337,13 @@ says that the `turtle` object's `GetX()` method will be called five times, it
|
||||
will return 100 the first time, 150 the second time, and then 200 every time.
|
||||
Some people like to call this style of syntax a Domain-Specific Language (DSL).
|
||||
|
||||
{: .callout .note}
|
||||
**Note:** Why do we use a macro to do this? Well it serves two purposes: first
|
||||
it makes expectations easily identifiable (either by `gsearch` or by a human
|
||||
it makes expectations easily identifiable (either by `grep` or by a human
|
||||
reader), and second it allows gMock to include the source file location of a
|
||||
failed expectation in messages, making debugging easier.
|
||||
|
||||
#### Matchers: What Arguments Do We Expect?
|
||||
### Matchers: What Arguments Do We Expect?
|
||||
|
||||
When a mock function takes arguments, we may specify what arguments we are
|
||||
expecting, for example:
|
||||
@@ -374,8 +374,8 @@ convenient way of saying "any value".
|
||||
In the above examples, `100` and `50` are also matchers; implicitly, they are
|
||||
the same as `Eq(100)` and `Eq(50)`, which specify that the argument must be
|
||||
equal (using `operator==`) to the matcher argument. There are many
|
||||
[built-in matchers](#MatcherList) for common types (as well as
|
||||
[custom matchers](cook_book.md#NewMatchers)); for example:
|
||||
[built-in matchers](reference/matchers.md) for common types (as well as
|
||||
[custom matchers](gmock_cook_book.md#NewMatchers)); for example:
|
||||
|
||||
```cpp
|
||||
using ::testing::Ge;
|
||||
@@ -397,9 +397,9 @@ EXPECT_CALL(turtle, GoTo);
|
||||
This works for all non-overloaded methods; if a method is overloaded, you need
|
||||
to help gMock resolve which overload is expected by specifying the number of
|
||||
arguments and possibly also the
|
||||
[types of the arguments](cook_book.md#SelectOverload).
|
||||
[types of the arguments](gmock_cook_book.md#SelectOverload).
|
||||
|
||||
#### Cardinalities: How Many Times Will It Be Called?
|
||||
### Cardinalities: How Many Times Will It Be Called?
|
||||
|
||||
The first clause we can specify following an `EXPECT_CALL()` is `Times()`. We
|
||||
call its argument a **cardinality** as it tells *how many times* the call should
|
||||
@@ -414,7 +414,7 @@ called.
|
||||
|
||||
We've seen `AtLeast(n)` as an example of fuzzy cardinalities earlier. For the
|
||||
list of built-in cardinalities you can use, see
|
||||
[here](cheat_sheet.md#CardinalityList).
|
||||
[here](gmock_cheat_sheet.md#CardinalityList).
|
||||
|
||||
The `Times()` clause can be omitted. **If you omit `Times()`, gMock will infer
|
||||
the cardinality for you.** The rules are easy to remember:
|
||||
@@ -429,7 +429,7 @@ the cardinality for you.** The rules are easy to remember:
|
||||
**Quick quiz:** what do you think will happen if a function is expected to be
|
||||
called twice but actually called four times?
|
||||
|
||||
#### Actions: What Should It Do?
|
||||
### Actions: What Should It Do?
|
||||
|
||||
Remember that a mock object doesn't really have a working implementation? We as
|
||||
users have to tell it what to do when a method is invoked. This is easy in
|
||||
@@ -482,8 +482,8 @@ the *default* action for the function every time (unless, of course, you have a
|
||||
`WillRepeatedly()`.).
|
||||
|
||||
What can we do inside `WillOnce()` besides `Return()`? You can return a
|
||||
reference using `ReturnRef(*variable*)`, or invoke a pre-defined function, among
|
||||
[others](cook_book.md#using-actions).
|
||||
reference using `ReturnRef(`*`variable`*`)`, or invoke a pre-defined function,
|
||||
among [others](gmock_cook_book.md#using-actions).
|
||||
|
||||
**Important note:** The `EXPECT_CALL()` statement evaluates the action clause
|
||||
only once, even though the action may be performed many times. Therefore you
|
||||
@@ -503,7 +503,7 @@ always return 100 as `n++` is only evaluated once. Similarly, `Return(new Foo)`
|
||||
will create a new `Foo` object when the `EXPECT_CALL()` is executed, and will
|
||||
return the same pointer every time. If you want the side effect to happen every
|
||||
time, you need to define a custom action, which we'll teach in the
|
||||
[cook book](http://<!-- GOOGLETEST_CM0012 DO NOT DELETE -->).
|
||||
[cook book](gmock_cook_book.md).
|
||||
|
||||
Time for another quiz! What do you think the following means?
|
||||
|
||||
@@ -522,7 +522,7 @@ will be taken afterwards. So the right answer is that `turtle.GetY()` will
|
||||
return 100 the first time, but **return 0 from the second time on**, as
|
||||
returning 0 is the default action for `int` functions.
|
||||
|
||||
#### Using Multiple Expectations {#MultiExpectations}
|
||||
### Using Multiple Expectations {#MultiExpectations}
|
||||
|
||||
So far we've only shown examples where you have a single expectation. More
|
||||
realistically, you'll specify expectations on multiple mock methods which may be
|
||||
@@ -547,6 +547,7 @@ error, as the last matching expectation (#2) has been saturated. If, however,
|
||||
the third `Forward(10)` call is replaced by `Forward(20)`, then it would be OK,
|
||||
as now #1 will be the matching expectation.
|
||||
|
||||
{: .callout .note}
|
||||
**Note:** Why does gMock search for a match in the *reverse* order of the
|
||||
expectations? The reason is that this allows a user to set up the default
|
||||
expectations in a mock object's constructor or the test fixture's set-up phase
|
||||
@@ -555,15 +556,16 @@ body. So, if you have two expectations on the same method, you want to put the
|
||||
one with more specific matchers **after** the other, or the more specific rule
|
||||
would be shadowed by the more general one that comes after it.
|
||||
|
||||
{: .callout .tip}
|
||||
**Tip:** It is very common to start with a catch-all expectation for a method
|
||||
and `Times(AnyNumber())` (omitting arguments, or with `_` for all arguments, if
|
||||
overloaded). This makes any calls to the method expected. This is not necessary
|
||||
for methods that are not mentioned at all (these are "uninteresting"), but is
|
||||
useful for methods that have some expectations, but for which other calls are
|
||||
ok. See
|
||||
[Understanding Uninteresting vs Unexpected Calls](cook_book.md#uninteresting-vs-unexpected).
|
||||
[Understanding Uninteresting vs Unexpected Calls](gmock_cook_book.md#uninteresting-vs-unexpected).
|
||||
|
||||
#### Ordered vs Unordered Calls {#OrderedCalls}
|
||||
### Ordered vs Unordered Calls {#OrderedCalls}
|
||||
|
||||
By default, an expectation can match a call even though an earlier expectation
|
||||
hasn't been satisfied. In other words, the calls don't have to occur in the
|
||||
@@ -598,9 +600,9 @@ order as written. If a call is made out-of-order, it will be an error.
|
||||
|
||||
(What if you care about the relative order of some of the calls, but not all of
|
||||
them? Can you specify an arbitrary partial order? The answer is ... yes! The
|
||||
details can be found [here](cook_book.md#OrderedCalls).)
|
||||
details can be found [here](gmock_cook_book.md#OrderedCalls).)
|
||||
|
||||
#### All Expectations Are Sticky (Unless Said Otherwise) {#StickyExpectations}
|
||||
### All Expectations Are Sticky (Unless Said Otherwise) {#StickyExpectations}
|
||||
|
||||
Now let's do a quick quiz to see how well you can use this mock stuff already.
|
||||
How would you test that the turtle is asked to go to the origin *exactly twice*
|
||||
@@ -688,7 +690,7 @@ it's in a sequence - as soon as another expectation that comes after it in the
|
||||
sequence has been used, it automatically retires (and will never be used to
|
||||
match any call).
|
||||
|
||||
#### Uninteresting Calls
|
||||
### Uninteresting Calls
|
||||
|
||||
A mock object may have many methods, and not all of them are that interesting.
|
||||
For example, in some tests we may not care about how many times `GetX()` and
|
||||
@@ -697,4 +699,4 @@ For example, in some tests we may not care about how many times `GetX()` and
|
||||
In gMock, if you are not interested in a method, just don't say anything about
|
||||
it. If a call to this method occurs, you'll see a warning in the test output,
|
||||
but it won't be a failure. This is called "naggy" behavior; to change, see
|
||||
[The Nice, the Strict, and the Naggy](cook_book.md#NiceStrictNaggy).
|
||||
[The Nice, the Strict, and the Naggy](gmock_cook_book.md#NiceStrictNaggy).
|
||||
Vendored
+22
@@ -0,0 +1,22 @@
|
||||
# GoogleTest User's Guide
|
||||
|
||||
## Welcome to GoogleTest!
|
||||
|
||||
GoogleTest is Google's C++ testing and mocking framework. This user's guide has
|
||||
the following contents:
|
||||
|
||||
* [GoogleTest Primer](primer.md) - Teaches you how to write simple tests using
|
||||
GoogleTest. Read this first if you are new to GoogleTest.
|
||||
* [GoogleTest Advanced](advanced.md) - Read this when you've finished the
|
||||
Primer and want to utilize GoogleTest to its full potential.
|
||||
* [GoogleTest Samples](samples.md) - Describes some GoogleTest samples.
|
||||
* [GoogleTest FAQ](faq.md) - Have a question? Want some tips? Check here
|
||||
first.
|
||||
* [Mocking for Dummies](gmock_for_dummies.md) - Teaches you how to create mock
|
||||
objects and use them in tests.
|
||||
* [Mocking Cookbook](gmock_cook_book.md) - Includes tips and approaches to
|
||||
common mocking use cases.
|
||||
* [Mocking Cheat Sheet](gmock_cheat_sheet.md) - A handy reference for
|
||||
matchers, actions, invariants, and more.
|
||||
* [Mocking FAQ](gmock_faq.md) - Contains answers to some mocking-specific
|
||||
questions.
|
||||
+144
@@ -0,0 +1,144 @@
|
||||
## Using GoogleTest from various build systems
|
||||
|
||||
GoogleTest comes with pkg-config files that can be used to determine all
|
||||
necessary flags for compiling and linking to GoogleTest (and GoogleMock).
|
||||
Pkg-config is a standardised plain-text format containing
|
||||
|
||||
* the includedir (-I) path
|
||||
* necessary macro (-D) definitions
|
||||
* further required flags (-pthread)
|
||||
* the library (-L) path
|
||||
* the library (-l) to link to
|
||||
|
||||
All current build systems support pkg-config in one way or another. For all
|
||||
examples here we assume you want to compile the sample
|
||||
`samples/sample3_unittest.cc`.
|
||||
|
||||
### CMake
|
||||
|
||||
Using `pkg-config` in CMake is fairly easy:
|
||||
|
||||
```cmake
|
||||
find_package(PkgConfig)
|
||||
pkg_search_module(GTEST REQUIRED gtest_main)
|
||||
|
||||
add_executable(testapp)
|
||||
target_sources(testapp PRIVATE samples/sample3_unittest.cc)
|
||||
target_link_libraries(testapp PRIVATE ${GTEST_LDFLAGS})
|
||||
target_compile_options(testapp PRIVATE ${GTEST_CFLAGS})
|
||||
|
||||
enable_testing()
|
||||
add_test(first_and_only_test testapp)
|
||||
```
|
||||
|
||||
It is generally recommended that you use `target_compile_options` + `_CFLAGS`
|
||||
over `target_include_directories` + `_INCLUDE_DIRS` as the former includes not
|
||||
just -I flags (GoogleTest might require a macro indicating to internal headers
|
||||
that all libraries have been compiled with threading enabled. In addition,
|
||||
GoogleTest might also require `-pthread` in the compiling step, and as such
|
||||
splitting the pkg-config `Cflags` variable into include dirs and macros for
|
||||
`target_compile_definitions()` might still miss this). The same recommendation
|
||||
goes for using `_LDFLAGS` over the more commonplace `_LIBRARIES`, which happens
|
||||
to discard `-L` flags and `-pthread`.
|
||||
|
||||
### Help! pkg-config can't find GoogleTest!
|
||||
|
||||
Let's say you have a `CMakeLists.txt` along the lines of the one in this
|
||||
tutorial and you try to run `cmake`. It is very possible that you get a failure
|
||||
along the lines of:
|
||||
|
||||
```
|
||||
-- Checking for one of the modules 'gtest_main'
|
||||
CMake Error at /usr/share/cmake/Modules/FindPkgConfig.cmake:640 (message):
|
||||
None of the required 'gtest_main' found
|
||||
```
|
||||
|
||||
These failures are common if you installed GoogleTest yourself and have not
|
||||
sourced it from a distro or other package manager. If so, you need to tell
|
||||
pkg-config where it can find the `.pc` files containing the information. Say you
|
||||
installed GoogleTest to `/usr/local`, then it might be that the `.pc` files are
|
||||
installed under `/usr/local/lib64/pkgconfig`. If you set
|
||||
|
||||
```
|
||||
export PKG_CONFIG_PATH=/usr/local/lib64/pkgconfig
|
||||
```
|
||||
|
||||
pkg-config will also try to look in `PKG_CONFIG_PATH` to find `gtest_main.pc`.
|
||||
|
||||
### Using pkg-config in a cross-compilation setting
|
||||
|
||||
Pkg-config can be used in a cross-compilation setting too. To do this, let's
|
||||
assume the final prefix of the cross-compiled installation will be `/usr`, and
|
||||
your sysroot is `/home/MYUSER/sysroot`. Configure and install GTest using
|
||||
|
||||
```
|
||||
mkdir build && cmake -DCMAKE_INSTALL_PREFIX=/usr ..
|
||||
```
|
||||
|
||||
Install into the sysroot using `DESTDIR`:
|
||||
|
||||
```
|
||||
make -j install DESTDIR=/home/MYUSER/sysroot
|
||||
```
|
||||
|
||||
Before we continue, it is recommended to **always** define the following two
|
||||
variables for pkg-config in a cross-compilation setting:
|
||||
|
||||
```
|
||||
export PKG_CONFIG_ALLOW_SYSTEM_CFLAGS=yes
|
||||
export PKG_CONFIG_ALLOW_SYSTEM_LIBS=yes
|
||||
```
|
||||
|
||||
otherwise `pkg-config` will filter `-I` and `-L` flags against standard prefixes
|
||||
such as `/usr` (see https://bugs.freedesktop.org/show_bug.cgi?id=28264#c3 for
|
||||
reasons why this stripping needs to occur usually).
|
||||
|
||||
If you look at the generated pkg-config file, it will look something like
|
||||
|
||||
```
|
||||
libdir=/usr/lib64
|
||||
includedir=/usr/include
|
||||
|
||||
Name: gtest
|
||||
Description: GoogleTest (without main() function)
|
||||
Version: 1.11.0
|
||||
URL: https://github.com/google/googletest
|
||||
Libs: -L${libdir} -lgtest -lpthread
|
||||
Cflags: -I${includedir} -DGTEST_HAS_PTHREAD=1 -lpthread
|
||||
```
|
||||
|
||||
Notice that the sysroot is not included in `libdir` and `includedir`! If you try
|
||||
to run `pkg-config` with the correct
|
||||
`PKG_CONFIG_LIBDIR=/home/MYUSER/sysroot/usr/lib64/pkgconfig` against this `.pc`
|
||||
file, you will get
|
||||
|
||||
```
|
||||
$ pkg-config --cflags gtest
|
||||
-DGTEST_HAS_PTHREAD=1 -lpthread -I/usr/include
|
||||
$ pkg-config --libs gtest
|
||||
-L/usr/lib64 -lgtest -lpthread
|
||||
```
|
||||
|
||||
which is obviously wrong and points to the `CBUILD` and not `CHOST` root. In
|
||||
order to use this in a cross-compilation setting, we need to tell pkg-config to
|
||||
inject the actual sysroot into `-I` and `-L` variables. Let us now tell
|
||||
pkg-config about the actual sysroot
|
||||
|
||||
```
|
||||
export PKG_CONFIG_DIR=
|
||||
export PKG_CONFIG_SYSROOT_DIR=/home/MYUSER/sysroot
|
||||
export PKG_CONFIG_LIBDIR=${PKG_CONFIG_SYSROOT_DIR}/usr/lib64/pkgconfig
|
||||
```
|
||||
|
||||
and running `pkg-config` again we get
|
||||
|
||||
```
|
||||
$ pkg-config --cflags gtest
|
||||
-DGTEST_HAS_PTHREAD=1 -lpthread -I/home/MYUSER/sysroot/usr/include
|
||||
$ pkg-config --libs gtest
|
||||
-L/home/MYUSER/sysroot/usr/lib64 -lgtest -lpthread
|
||||
```
|
||||
|
||||
which contains the correct sysroot now. For a more comprehensive guide to also
|
||||
including `${CHOST}` in build system calls, see the excellent tutorial by Diego
|
||||
Elio Pettenò: <https://autotools.io/pkgconfig/cross-compiling.html>
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
# Supported Platforms
|
||||
|
||||
GoogleTest follows Google's
|
||||
[Foundational C++ Support Policy](https://opensource.google/documentation/policies/cplusplus-support).
|
||||
See
|
||||
[this table](https://github.com/google/oss-policies-info/blob/main/foundational-cxx-support-matrix.md)
|
||||
for a list of currently supported versions compilers, platforms, and build
|
||||
tools.
|
||||
+104
-189
@@ -1,85 +1,84 @@
|
||||
# Googletest Primer
|
||||
# GoogleTest Primer
|
||||
|
||||
## Introduction: Why googletest?
|
||||
## Introduction: Why GoogleTest?
|
||||
|
||||
*googletest* helps you write better C++ tests.
|
||||
*GoogleTest* helps you write better C++ tests.
|
||||
|
||||
googletest is a testing framework developed by the Testing Technology team with
|
||||
GoogleTest is a testing framework developed by the Testing Technology team with
|
||||
Google's specific requirements and constraints in mind. Whether you work on
|
||||
Linux, Windows, or a Mac, if you write C++ code, googletest can help you. And it
|
||||
Linux, Windows, or a Mac, if you write C++ code, GoogleTest can help you. And it
|
||||
supports *any* kind of tests, not just unit tests.
|
||||
|
||||
So what makes a good test, and how does googletest fit in? We believe:
|
||||
So what makes a good test, and how does GoogleTest fit in? We believe:
|
||||
|
||||
1. Tests should be *independent* and *repeatable*. It's a pain to debug a test
|
||||
that succeeds or fails as a result of other tests. googletest isolates the
|
||||
that succeeds or fails as a result of other tests. GoogleTest isolates the
|
||||
tests by running each of them on a different object. When a test fails,
|
||||
googletest allows you to run it in isolation for quick debugging.
|
||||
GoogleTest allows you to run it in isolation for quick debugging.
|
||||
2. Tests should be well *organized* and reflect the structure of the tested
|
||||
code. googletest groups related tests into test suites that can share data
|
||||
code. GoogleTest groups related tests into test suites that can share data
|
||||
and subroutines. This common pattern is easy to recognize and makes tests
|
||||
easy to maintain. Such consistency is especially helpful when people switch
|
||||
projects and start to work on a new code base.
|
||||
3. Tests should be *portable* and *reusable*. Google has a lot of code that is
|
||||
platform-neutral; its tests should also be platform-neutral. googletest
|
||||
platform-neutral; its tests should also be platform-neutral. GoogleTest
|
||||
works on different OSes, with different compilers, with or without
|
||||
exceptions, so googletest tests can work with a variety of configurations.
|
||||
exceptions, so GoogleTest tests can work with a variety of configurations.
|
||||
4. When tests fail, they should provide as much *information* about the problem
|
||||
as possible. googletest doesn't stop at the first test failure. Instead, it
|
||||
as possible. GoogleTest doesn't stop at the first test failure. Instead, it
|
||||
only stops the current test and continues with the next. You can also set up
|
||||
tests that report non-fatal failures after which the current test continues.
|
||||
Thus, you can detect and fix multiple bugs in a single run-edit-compile
|
||||
cycle.
|
||||
5. The testing framework should liberate test writers from housekeeping chores
|
||||
and let them focus on the test *content*. googletest automatically keeps
|
||||
and let them focus on the test *content*. GoogleTest automatically keeps
|
||||
track of all tests defined, and doesn't require the user to enumerate them
|
||||
in order to run them.
|
||||
6. Tests should be *fast*. With googletest, you can reuse shared resources
|
||||
6. Tests should be *fast*. With GoogleTest, you can reuse shared resources
|
||||
across tests and pay for the set-up/tear-down only once, without making
|
||||
tests depend on each other.
|
||||
|
||||
Since googletest is based on the popular xUnit architecture, you'll feel right
|
||||
Since GoogleTest is based on the popular xUnit architecture, you'll feel right
|
||||
at home if you've used JUnit or PyUnit before. If not, it will take you about 10
|
||||
minutes to learn the basics and get started. So let's go!
|
||||
|
||||
## Beware of the nomenclature
|
||||
## Beware of the Nomenclature
|
||||
|
||||
_Note:_ There might be some confusion arising from different definitions of the
|
||||
terms _Test_, _Test Case_ and _Test Suite_, so beware of misunderstanding these.
|
||||
{: .callout .note}
|
||||
*Note:* There might be some confusion arising from different definitions of the
|
||||
terms *Test*, *Test Case* and *Test Suite*, so beware of misunderstanding these.
|
||||
|
||||
Historically, googletest started to use the term _Test Case_ for grouping
|
||||
Historically, GoogleTest started to use the term *Test Case* for grouping
|
||||
related tests, whereas current publications, including International Software
|
||||
Testing Qualifications Board ([ISTQB](http://www.istqb.org/)) materials and
|
||||
Testing Qualifications Board ([ISTQB](https://www.istqb.org/)) materials and
|
||||
various textbooks on software quality, use the term
|
||||
_[Test Suite][istqb test suite]_ for this.
|
||||
*[Test Suite][istqb test suite]* for this.
|
||||
|
||||
The related term _Test_, as it is used in googletest, corresponds to the term
|
||||
_[Test Case][istqb test case]_ of ISTQB and others.
|
||||
The related term *Test*, as it is used in GoogleTest, corresponds to the term
|
||||
*[Test Case][istqb test case]* of ISTQB and others.
|
||||
|
||||
The term _Test_ is commonly of broad enough sense, including ISTQB's definition
|
||||
of _Test Case_, so it's not much of a problem here. But the term _Test Case_ as
|
||||
The term *Test* is commonly of broad enough sense, including ISTQB's definition
|
||||
of *Test Case*, so it's not much of a problem here. But the term *Test Case* as
|
||||
was used in Google Test is of contradictory sense and thus confusing.
|
||||
|
||||
googletest recently started replacing the term _Test Case_ with _Test Suite_.
|
||||
GoogleTest recently started replacing the term *Test Case* with *Test Suite*.
|
||||
The preferred API is *TestSuite*. The older TestCase API is being slowly
|
||||
deprecated and refactored away.
|
||||
|
||||
So please be aware of the different definitions of the terms:
|
||||
|
||||
<!-- mdformat off(github rendering does not support multiline tables) -->
|
||||
|
||||
Meaning | googletest Term | [ISTQB](http://www.istqb.org/) Term
|
||||
Meaning | GoogleTest Term | [ISTQB](https://www.istqb.org/) Term
|
||||
:----------------------------------------------------------------------------------- | :---------------------- | :----------------------------------
|
||||
Exercise a particular program path with specific input values and verify the results | [TEST()](#simple-tests) | [Test Case][istqb test case]
|
||||
|
||||
<!-- mdformat on -->
|
||||
|
||||
[istqb test case]: http://glossary.istqb.org/en/search/test%20case
|
||||
[istqb test suite]: http://glossary.istqb.org/en/search/test%20suite
|
||||
[istqb test case]: https://glossary.istqb.org/en_US/term/test-case
|
||||
[istqb test suite]: https://glossary.istqb.org/en_US/term/test-suite
|
||||
|
||||
## Basic Concepts
|
||||
|
||||
When using googletest, you start by writing *assertions*, which are statements
|
||||
When using GoogleTest, you start by writing *assertions*, which are statements
|
||||
that check whether a condition is true. An assertion's result can be *success*,
|
||||
*nonfatal failure*, or *fatal failure*. If a fatal failure occurs, it aborts the
|
||||
current function; otherwise the program continues normally.
|
||||
@@ -99,11 +98,11 @@ assertion level and building up to tests and test suites.
|
||||
|
||||
## Assertions
|
||||
|
||||
googletest assertions are macros that resemble function calls. You test a class
|
||||
GoogleTest assertions are macros that resemble function calls. You test a class
|
||||
or function by making assertions about its behavior. When an assertion fails,
|
||||
googletest prints the assertion's source file and line number location, along
|
||||
GoogleTest prints the assertion's source file and line number location, along
|
||||
with a failure message. You may also supply a custom failure message which will
|
||||
be appended to googletest's message.
|
||||
be appended to GoogleTest's message.
|
||||
|
||||
The assertions come in pairs that test the same thing but have different effects
|
||||
on the current function. `ASSERT_*` versions generate fatal failures when they
|
||||
@@ -119,7 +118,9 @@ Depending on the nature of the leak, it may or may not be worth fixing - so keep
|
||||
this in mind if you get a heap checker error in addition to assertion errors.
|
||||
|
||||
To provide a custom failure message, simply stream it into the macro using the
|
||||
`<<` operator or a sequence of such operators. An example:
|
||||
`<<` operator or a sequence of such operators. See the following example, using
|
||||
the [`ASSERT_EQ` and `EXPECT_EQ`](reference/assertions.md#EXPECT_EQ) macros to
|
||||
verify value equality:
|
||||
|
||||
```c++
|
||||
ASSERT_EQ(x.size(), y.size()) << "Vectors x and y are of unequal length";
|
||||
@@ -134,112 +135,12 @@ macro--in particular, C strings and `string` objects. If a wide string
|
||||
(`wchar_t*`, `TCHAR*` in `UNICODE` mode on Windows, or `std::wstring`) is
|
||||
streamed to an assertion, it will be translated to UTF-8 when printed.
|
||||
|
||||
### Basic Assertions
|
||||
|
||||
These assertions do basic true/false condition testing.
|
||||
|
||||
Fatal assertion | Nonfatal assertion | Verifies
|
||||
-------------------------- | -------------------------- | --------------------
|
||||
`ASSERT_TRUE(condition);` | `EXPECT_TRUE(condition);` | `condition` is true
|
||||
`ASSERT_FALSE(condition);` | `EXPECT_FALSE(condition);` | `condition` is false
|
||||
|
||||
Remember, when they fail, `ASSERT_*` yields a fatal failure and returns from the
|
||||
current function, while `EXPECT_*` yields a nonfatal failure, allowing the
|
||||
function to continue running. In either case, an assertion failure means its
|
||||
containing test fails.
|
||||
|
||||
**Availability**: Linux, Windows, Mac.
|
||||
|
||||
### Binary Comparison
|
||||
|
||||
This section describes assertions that compare two values.
|
||||
|
||||
Fatal assertion | Nonfatal assertion | Verifies
|
||||
------------------------ | ------------------------ | --------------
|
||||
`ASSERT_EQ(val1, val2);` | `EXPECT_EQ(val1, val2);` | `val1 == val2`
|
||||
`ASSERT_NE(val1, val2);` | `EXPECT_NE(val1, val2);` | `val1 != val2`
|
||||
`ASSERT_LT(val1, val2);` | `EXPECT_LT(val1, val2);` | `val1 < val2`
|
||||
`ASSERT_LE(val1, val2);` | `EXPECT_LE(val1, val2);` | `val1 <= val2`
|
||||
`ASSERT_GT(val1, val2);` | `EXPECT_GT(val1, val2);` | `val1 > val2`
|
||||
`ASSERT_GE(val1, val2);` | `EXPECT_GE(val1, val2);` | `val1 >= val2`
|
||||
|
||||
Value arguments must be comparable by the assertion's comparison operator or
|
||||
you'll get a compiler error. We used to require the arguments to support the
|
||||
`<<` operator for streaming to an `ostream`, but this is no longer necessary. If
|
||||
`<<` is supported, it will be called to print the arguments when the assertion
|
||||
fails; otherwise googletest will attempt to print them in the best way it can.
|
||||
For more details and how to customize the printing of the arguments, see the
|
||||
[documentation](../../googlemock/docs/cook_book.md#teaching-gmock-how-to-print-your-values).
|
||||
|
||||
These assertions can work with a user-defined type, but only if you define the
|
||||
corresponding comparison operator (e.g., `==` or `<`). Since this is discouraged
|
||||
by the Google
|
||||
[C++ Style Guide](https://google.github.io/styleguide/cppguide.html#Operator_Overloading),
|
||||
you may need to use `ASSERT_TRUE()` or `EXPECT_TRUE()` to assert the equality of
|
||||
two objects of a user-defined type.
|
||||
|
||||
However, when possible, `ASSERT_EQ(actual, expected)` is preferred to
|
||||
`ASSERT_TRUE(actual == expected)`, since it tells you `actual` and `expected`'s
|
||||
values on failure.
|
||||
|
||||
Arguments are always evaluated exactly once. Therefore, it's OK for the
|
||||
arguments to have side effects. However, as with any ordinary C/C++ function,
|
||||
the arguments' evaluation order is undefined (i.e., the compiler is free to
|
||||
choose any order), and your code should not depend on any particular argument
|
||||
evaluation order.
|
||||
|
||||
`ASSERT_EQ()` does pointer equality on pointers. If used on two C strings, it
|
||||
tests if they are in the same memory location, not if they have the same value.
|
||||
Therefore, if you want to compare C strings (e.g. `const char*`) by value, use
|
||||
`ASSERT_STREQ()`, which will be described later on. In particular, to assert
|
||||
that a C string is `NULL`, use `ASSERT_STREQ(c_string, NULL)`. Consider using
|
||||
`ASSERT_EQ(c_string, nullptr)` if c++11 is supported. To compare two `string`
|
||||
objects, you should use `ASSERT_EQ`.
|
||||
|
||||
When doing pointer comparisons use `*_EQ(ptr, nullptr)` and `*_NE(ptr, nullptr)`
|
||||
instead of `*_EQ(ptr, NULL)` and `*_NE(ptr, NULL)`. This is because `nullptr` is
|
||||
typed, while `NULL` is not. See the [FAQ](faq.md) for more details.
|
||||
|
||||
If you're working with floating point numbers, you may want to use the floating
|
||||
point variations of some of these macros in order to avoid problems caused by
|
||||
rounding. See [Advanced googletest Topics](advanced.md) for details.
|
||||
|
||||
Macros in this section work with both narrow and wide string objects (`string`
|
||||
and `wstring`).
|
||||
|
||||
**Availability**: Linux, Windows, Mac.
|
||||
|
||||
**Historical note**: Before February 2016 `*_EQ` had a convention of calling it
|
||||
as `ASSERT_EQ(expected, actual)`, so lots of existing code uses this order. Now
|
||||
`*_EQ` treats both parameters in the same way.
|
||||
|
||||
### String Comparison
|
||||
|
||||
The assertions in this group compare two **C strings**. If you want to compare
|
||||
two `string` objects, use `EXPECT_EQ`, `EXPECT_NE`, and etc instead.
|
||||
|
||||
<!-- mdformat off(github rendering does not support multiline tables) -->
|
||||
|
||||
| Fatal assertion | Nonfatal assertion | Verifies |
|
||||
| -------------------------- | ------------------------------ | -------------------------------------------------------- |
|
||||
| `ASSERT_STREQ(str1,str2);` | `EXPECT_STREQ(str1,str2);` | the two C strings have the same content |
|
||||
| `ASSERT_STRNE(str1,str2);` | `EXPECT_STRNE(str1,str2);` | the two C strings have different contents |
|
||||
| `ASSERT_STRCASEEQ(str1,str2);` | `EXPECT_STRCASEEQ(str1,str2);` | the two C strings have the same content, ignoring case |
|
||||
| `ASSERT_STRCASENE(str1,str2);` | `EXPECT_STRCASENE(str1,str2);` | the two C strings have different contents, ignoring case |
|
||||
|
||||
<!-- mdformat on-->
|
||||
|
||||
Note that "CASE" in an assertion name means that case is ignored. A `NULL`
|
||||
pointer and an empty string are considered *different*.
|
||||
|
||||
`*STREQ*` and `*STRNE*` also accept wide C strings (`wchar_t*`). If a comparison
|
||||
of two wide strings fails, their values will be printed as UTF-8 narrow strings.
|
||||
|
||||
**Availability**: Linux, Windows, Mac.
|
||||
|
||||
**See also**: For more string comparison tricks (substring, prefix, suffix, and
|
||||
regular expression matching, for example), see [this](advanced.md) in the
|
||||
Advanced googletest Guide.
|
||||
GoogleTest provides a collection of assertions for verifying the behavior of
|
||||
your code in various ways. You can check Boolean conditions, compare values
|
||||
based on relational operators, verify string values, floating-point values, and
|
||||
much more. There are even assertions that enable you to verify more complex
|
||||
states by providing custom predicates. For the complete list of assertions
|
||||
provided by GoogleTest, see the [Assertions Reference](reference/assertions.md).
|
||||
|
||||
## Simple Tests
|
||||
|
||||
@@ -248,7 +149,7 @@ To create a test:
|
||||
1. Use the `TEST()` macro to define and name a test function. These are
|
||||
ordinary C++ functions that don't return a value.
|
||||
2. In this function, along with any valid C++ statements you want to include,
|
||||
use the various googletest assertions to check values.
|
||||
use the various GoogleTest assertions to check values.
|
||||
3. The test's result is determined by the assertions; if any assertion in the
|
||||
test fails (either fatally or non-fatally), or if the test crashes, the
|
||||
entire test fails. Otherwise, it succeeds.
|
||||
@@ -261,9 +162,9 @@ TEST(TestSuiteName, TestName) {
|
||||
|
||||
`TEST()` arguments go from general to specific. The *first* argument is the name
|
||||
of the test suite, and the *second* argument is the test's name within the test
|
||||
case. Both names must be valid C++ identifiers, and they should not contain
|
||||
any underscores (`_`). A test's *full name* consists of its containing test suite and
|
||||
its individual name. Tests from different test suites can have the same
|
||||
suite. Both names must be valid C++ identifiers, and they should not contain any
|
||||
underscores (`_`). A test's *full name* consists of its containing test suite
|
||||
and its individual name. Tests from different test suites can have the same
|
||||
individual name.
|
||||
|
||||
For example, let's take a simple integer function:
|
||||
@@ -289,7 +190,7 @@ TEST(FactorialTest, HandlesPositiveInput) {
|
||||
}
|
||||
```
|
||||
|
||||
googletest groups the test results by test suites, so logically related tests
|
||||
GoogleTest groups the test results by test suites, so logically related tests
|
||||
should be in the same test suite; in other words, the first argument to their
|
||||
`TEST()` should be the same. In the above example, we have two tests,
|
||||
`HandlesZeroInput` and `HandlesPositiveInput`, that belong to the same test
|
||||
@@ -309,7 +210,7 @@ objects for several different tests.
|
||||
|
||||
To create a fixture:
|
||||
|
||||
1. Derive a class from `::testing::Test` . Start its body with `protected:`, as
|
||||
1. Derive a class from `testing::Test` . Start its body with `protected:`, as
|
||||
we'll want to access fixture members from sub-classes.
|
||||
2. Inside the class, declare any objects you plan to use.
|
||||
3. If necessary, write a default constructor or `SetUp()` function to prepare
|
||||
@@ -326,14 +227,14 @@ When using a fixture, use `TEST_F()` instead of `TEST()` as it allows you to
|
||||
access objects and subroutines in the test fixture:
|
||||
|
||||
```c++
|
||||
TEST_F(TestFixtureName, TestName) {
|
||||
TEST_F(TestFixtureClassName, TestName) {
|
||||
... test body ...
|
||||
}
|
||||
```
|
||||
|
||||
Like `TEST()`, the first argument is the test suite name, but for `TEST_F()`
|
||||
this must be the name of the test fixture class. You've probably guessed: `_F`
|
||||
is for fixture.
|
||||
Unlike `TEST()`, in `TEST_F()` the first argument must be the name of the test
|
||||
fixture class. (`_F` stands for "Fixture"). No test suite name is specified for
|
||||
this macro.
|
||||
|
||||
Unfortunately, the C++ macro system does not allow us to create a single macro
|
||||
that can handle both types of tests. Using the wrong macro causes a compiler
|
||||
@@ -343,12 +244,12 @@ Also, you must first define a test fixture class before using it in a
|
||||
`TEST_F()`, or you'll get the compiler error "`virtual outside class
|
||||
declaration`".
|
||||
|
||||
For each test defined with `TEST_F()`, googletest will create a *fresh* test
|
||||
fixture at runtime, immediately initialize it via `SetUp()`, run the test,
|
||||
clean up by calling `TearDown()`, and then delete the test fixture. Note that
|
||||
For each test defined with `TEST_F()`, GoogleTest will create a *fresh* test
|
||||
fixture at runtime, immediately initialize it via `SetUp()`, run the test, clean
|
||||
up by calling `TearDown()`, and then delete the test fixture. Note that
|
||||
different tests in the same test suite have different test fixture objects, and
|
||||
googletest always deletes a test fixture before it creates the next one.
|
||||
googletest does **not** reuse the same test fixture for multiple tests. Any
|
||||
GoogleTest always deletes a test fixture before it creates the next one.
|
||||
GoogleTest does **not** reuse the same test fixture for multiple tests. Any
|
||||
changes one test makes to the fixture do not affect other tests.
|
||||
|
||||
As an example, let's write tests for a FIFO queue class named `Queue`, which has
|
||||
@@ -370,15 +271,16 @@ First, define a fixture class. By convention, you should give it the name
|
||||
`FooTest` where `Foo` is the class being tested.
|
||||
|
||||
```c++
|
||||
class QueueTest : public ::testing::Test {
|
||||
class QueueTest : public testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
QueueTest() {
|
||||
// q0_ remains empty
|
||||
q1_.Enqueue(1);
|
||||
q2_.Enqueue(2);
|
||||
q2_.Enqueue(3);
|
||||
}
|
||||
|
||||
// void TearDown() override {}
|
||||
// ~QueueTest() override = default;
|
||||
|
||||
Queue<int> q0_;
|
||||
Queue<int> q1_;
|
||||
@@ -386,8 +288,9 @@ class QueueTest : public ::testing::Test {
|
||||
};
|
||||
```
|
||||
|
||||
In this case, `TearDown()` is not needed since we don't have to clean up after
|
||||
each test, other than what's already done by the destructor.
|
||||
In this case, we don't need to define a destructor or a `TearDown()` method,
|
||||
because the implicit destructor generated by the compiler will perform all of
|
||||
the necessary cleanup.
|
||||
|
||||
Now we'll write tests using `TEST_F()` and this fixture.
|
||||
|
||||
@@ -418,35 +321,33 @@ The above uses both `ASSERT_*` and `EXPECT_*` assertions. The rule of thumb is
|
||||
to use `EXPECT_*` when you want the test to continue to reveal more errors after
|
||||
the assertion failure, and use `ASSERT_*` when continuing after failure doesn't
|
||||
make sense. For example, the second assertion in the `Dequeue` test is
|
||||
`ASSERT_NE(nullptr, n)`, as we need to dereference the pointer `n` later, which
|
||||
`ASSERT_NE(n, nullptr)`, as we need to dereference the pointer `n` later, which
|
||||
would lead to a segfault when `n` is `NULL`.
|
||||
|
||||
When these tests run, the following happens:
|
||||
|
||||
1. googletest constructs a `QueueTest` object (let's call it `t1`).
|
||||
2. `t1.SetUp()` initializes `t1`.
|
||||
3. The first test (`IsEmptyInitially`) runs on `t1`.
|
||||
4. `t1.TearDown()` cleans up after the test finishes.
|
||||
5. `t1` is destructed.
|
||||
6. The above steps are repeated on another `QueueTest` object, this time
|
||||
1. GoogleTest constructs a `QueueTest` object (let's call it `t1`).
|
||||
2. The first test (`IsEmptyInitially`) runs on `t1`.
|
||||
3. `t1` is destructed.
|
||||
4. The above steps are repeated on another `QueueTest` object, this time
|
||||
running the `DequeueWorks` test.
|
||||
|
||||
**Availability**: Linux, Windows, Mac.
|
||||
|
||||
## Invoking the Tests
|
||||
|
||||
`TEST()` and `TEST_F()` implicitly register their tests with googletest. So,
|
||||
`TEST()` and `TEST_F()` implicitly register their tests with GoogleTest. So,
|
||||
unlike with many other C++ testing frameworks, you don't have to re-list all
|
||||
your defined tests in order to run them.
|
||||
|
||||
After defining your tests, you can run them with `RUN_ALL_TESTS()`, which
|
||||
returns `0` if all the tests are successful, or `1` otherwise. Note that
|
||||
`RUN_ALL_TESTS()` runs *all tests* in your link unit--they can be from
|
||||
different test suites, or even different source files.
|
||||
`RUN_ALL_TESTS()` runs *all tests* in your link unit--they can be from different
|
||||
test suites, or even different source files.
|
||||
|
||||
When invoked, the `RUN_ALL_TESTS()` macro:
|
||||
|
||||
* Saves the state of all googletest flags.
|
||||
* Saves the state of all GoogleTest flags.
|
||||
|
||||
* Creates a test fixture object for the first test.
|
||||
|
||||
@@ -458,12 +359,13 @@ When invoked, the `RUN_ALL_TESTS()` macro:
|
||||
|
||||
* Deletes the fixture.
|
||||
|
||||
* Restores the state of all googletest flags.
|
||||
* Restores the state of all GoogleTest flags.
|
||||
|
||||
* Repeats the above steps for the next test, until all tests have run.
|
||||
|
||||
If a fatal failure happens the subsequent steps will be skipped.
|
||||
|
||||
{: .callout .important}
|
||||
> IMPORTANT: You must **not** ignore the return value of `RUN_ALL_TESTS()`, or
|
||||
> you will get a compiler error. The rationale for this design is that the
|
||||
> automated testing service determines whether a test has passed based on its
|
||||
@@ -471,29 +373,38 @@ If a fatal failure happens the subsequent steps will be skipped.
|
||||
> return the value of `RUN_ALL_TESTS()`.
|
||||
>
|
||||
> Also, you should call `RUN_ALL_TESTS()` only **once**. Calling it more than
|
||||
> once conflicts with some advanced googletest features (e.g., thread-safe
|
||||
> once conflicts with some advanced GoogleTest features (e.g., thread-safe
|
||||
> [death tests](advanced.md#death-tests)) and thus is not supported.
|
||||
|
||||
**Availability**: Linux, Windows, Mac.
|
||||
|
||||
## Writing the main() Function
|
||||
|
||||
Write your own main() function, which should return the value of
|
||||
Most users should *not* need to write their own `main` function and instead link
|
||||
with `gtest_main` (as opposed to with `gtest`), which defines a suitable entry
|
||||
point. See the end of this section for details. The remainder of this section
|
||||
should only apply when you need to do something custom before the tests run that
|
||||
cannot be expressed within the framework of fixtures and test suites.
|
||||
|
||||
If you write your own `main` function, it should return the value of
|
||||
`RUN_ALL_TESTS()`.
|
||||
|
||||
You can start from this boilerplate:
|
||||
|
||||
```c++
|
||||
#include "this/package/foo.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
namespace my {
|
||||
namespace project {
|
||||
namespace {
|
||||
|
||||
// The fixture for testing class Foo.
|
||||
class FooTest : public ::testing::Test {
|
||||
class FooTest : public testing::Test {
|
||||
protected:
|
||||
// You can remove any or all of the following functions if its body
|
||||
// is empty.
|
||||
// You can remove any or all of the following functions if their bodies would
|
||||
// be empty.
|
||||
|
||||
FooTest() {
|
||||
// You can do set-up work for each test here.
|
||||
@@ -516,7 +427,8 @@ class FooTest : public ::testing::Test {
|
||||
// before the destructor).
|
||||
}
|
||||
|
||||
// Objects declared here can be used by all tests in the test suite for Foo.
|
||||
// Class members declared here can be used by all tests in the test suite
|
||||
// for Foo.
|
||||
};
|
||||
|
||||
// Tests that the Foo::Bar() method does Abc.
|
||||
@@ -533,34 +445,37 @@ TEST_F(FooTest, DoesXyz) {
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace project
|
||||
} // namespace my
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
```
|
||||
|
||||
The `::testing::InitGoogleTest()` function parses the command line for
|
||||
googletest flags, and removes all recognized flags. This allows the user to
|
||||
control a test program's behavior via various flags, which we'll cover in
|
||||
the [AdvancedGuide](advanced.md). You **must** call this function before calling
|
||||
The `testing::InitGoogleTest()` function parses the command line for GoogleTest
|
||||
flags, and removes all recognized flags. This allows the user to control a test
|
||||
program's behavior via various flags, which we'll cover in the
|
||||
[AdvancedGuide](advanced.md). You **must** call this function before calling
|
||||
`RUN_ALL_TESTS()`, or the flags won't be properly initialized.
|
||||
|
||||
On Windows, `InitGoogleTest()` also works with wide strings, so it can be used
|
||||
in programs compiled in `UNICODE` mode as well.
|
||||
|
||||
But maybe you think that writing all those main() functions is too much work? We
|
||||
But maybe you think that writing all those `main` functions is too much work? We
|
||||
agree with you completely, and that's why Google Test provides a basic
|
||||
implementation of main(). If it fits your needs, then just link your test with
|
||||
gtest\_main library and you are good to go.
|
||||
the `gtest_main` library and you are good to go.
|
||||
|
||||
{: .callout .note}
|
||||
NOTE: `ParseGUnitFlags()` is deprecated in favor of `InitGoogleTest()`.
|
||||
|
||||
## Known Limitations
|
||||
|
||||
* Google Test is designed to be thread-safe. The implementation is thread-safe
|
||||
on systems where the `pthreads` library is available. It is currently
|
||||
_unsafe_ to use Google Test assertions from two threads concurrently on
|
||||
*unsafe* to use Google Test assertions from two threads concurrently on
|
||||
other systems (e.g. Windows). In most tests this is not an issue as usually
|
||||
the assertions are done in the main thread. If you want to help, you can
|
||||
volunteer to implement the necessary synchronization primitives in
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
# Quickstart: Building with Bazel
|
||||
|
||||
This tutorial aims to get you up and running with GoogleTest using the Bazel
|
||||
build system. If you're using GoogleTest for the first time or need a refresher,
|
||||
we recommend this tutorial as a starting point.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
To complete this tutorial, you'll need:
|
||||
|
||||
* A compatible operating system (e.g. Linux, macOS, Windows).
|
||||
* A compatible C++ compiler that supports at least C++14.
|
||||
* [Bazel](https://bazel.build/) 7.0 or higher, the preferred build system used
|
||||
by the GoogleTest team.
|
||||
|
||||
See [Supported Platforms](platforms.md) for more information about platforms
|
||||
compatible with GoogleTest.
|
||||
|
||||
If you don't already have Bazel installed, see the
|
||||
[Bazel installation guide](https://bazel.build/install).
|
||||
|
||||
{: .callout .note} Note: The terminal commands in this tutorial show a Unix
|
||||
shell prompt, but the commands work on the Windows command line as well.
|
||||
|
||||
## Set up a Bazel workspace
|
||||
|
||||
A
|
||||
[Bazel workspace](https://docs.bazel.build/versions/main/build-ref.html#workspace)
|
||||
is a directory on your filesystem that you use to manage source files for the
|
||||
software you want to build. Each workspace directory has a text file named
|
||||
`MODULE.bazel` which may be empty, or may contain references to external
|
||||
dependencies required to build the outputs.
|
||||
|
||||
First, create a directory for your workspace:
|
||||
|
||||
```
|
||||
$ mkdir my_workspace && cd my_workspace
|
||||
```
|
||||
|
||||
Next, you’ll create the `MODULE.bazel` file to specify dependencies. As of Bazel
|
||||
7.0, the recommended way to consume GoogleTest is through the
|
||||
[Bazel Central Registry](https://registry.bazel.build/modules/googletest). To do
|
||||
this, create a `MODULE.bazel` file in the root directory of your Bazel workspace
|
||||
with the following content:
|
||||
|
||||
```
|
||||
# MODULE.bazel
|
||||
|
||||
# Choose the most recent version available at
|
||||
# https://registry.bazel.build/modules/googletest
|
||||
bazel_dep(name = "googletest", version = "1.15.2")
|
||||
```
|
||||
|
||||
Now you're ready to build C++ code that uses GoogleTest.
|
||||
|
||||
## Create and run a binary
|
||||
|
||||
With your Bazel workspace set up, you can now use GoogleTest code within your
|
||||
own project.
|
||||
|
||||
As an example, create a file named `hello_test.cc` in your `my_workspace`
|
||||
directory with the following contents:
|
||||
|
||||
```cpp
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
// Demonstrate some basic assertions.
|
||||
TEST(HelloTest, BasicAssertions) {
|
||||
// Expect two strings not to be equal.
|
||||
EXPECT_STRNE("hello", "world");
|
||||
// Expect equality.
|
||||
EXPECT_EQ(7 * 6, 42);
|
||||
}
|
||||
```
|
||||
|
||||
GoogleTest provides [assertions](primer.md#assertions) that you use to test the
|
||||
behavior of your code. The above sample includes the main GoogleTest header file
|
||||
and demonstrates some basic assertions.
|
||||
|
||||
To build the code, create a file named `BUILD` in the same directory with the
|
||||
following contents:
|
||||
|
||||
```
|
||||
cc_test(
|
||||
name = "hello_test",
|
||||
size = "small",
|
||||
srcs = ["hello_test.cc"],
|
||||
deps = [
|
||||
"@googletest//:gtest",
|
||||
"@googletest//:gtest_main",
|
||||
],
|
||||
)
|
||||
```
|
||||
|
||||
This `cc_test` rule declares the C++ test binary you want to build, and links to
|
||||
the GoogleTest library (`@googletest//:gtest"`) and the GoogleTest `main()`
|
||||
function (`@googletest//:gtest_main`). For more information about Bazel `BUILD`
|
||||
files, see the
|
||||
[Bazel C++ Tutorial](https://docs.bazel.build/versions/main/tutorial/cpp.html).
|
||||
|
||||
{: .callout .note}
|
||||
NOTE: In the example below, we assume Clang or GCC and set `--cxxopt=-std=c++14`
|
||||
to ensure that GoogleTest is compiled as C++14 instead of the compiler's default
|
||||
setting (which could be C++11). For MSVC, the equivalent would be
|
||||
`--cxxopt=/std:c++14`. See [Supported Platforms](platforms.md) for more details
|
||||
on supported language versions.
|
||||
|
||||
Now you can build and run your test:
|
||||
|
||||
<pre>
|
||||
<strong>$ bazel test --cxxopt=-std=c++14 --test_output=all //:hello_test</strong>
|
||||
INFO: Analyzed target //:hello_test (26 packages loaded, 362 targets configured).
|
||||
INFO: Found 1 test target...
|
||||
INFO: From Testing //:hello_test:
|
||||
==================== Test output for //:hello_test:
|
||||
Running main() from gmock_main.cc
|
||||
[==========] Running 1 test from 1 test suite.
|
||||
[----------] Global test environment set-up.
|
||||
[----------] 1 test from HelloTest
|
||||
[ RUN ] HelloTest.BasicAssertions
|
||||
[ OK ] HelloTest.BasicAssertions (0 ms)
|
||||
[----------] 1 test from HelloTest (0 ms total)
|
||||
|
||||
[----------] Global test environment tear-down
|
||||
[==========] 1 test from 1 test suite ran. (0 ms total)
|
||||
[ PASSED ] 1 test.
|
||||
================================================================================
|
||||
Target //:hello_test up-to-date:
|
||||
bazel-bin/hello_test
|
||||
INFO: Elapsed time: 4.190s, Critical Path: 3.05s
|
||||
INFO: 27 processes: 8 internal, 19 linux-sandbox.
|
||||
INFO: Build completed successfully, 27 total actions
|
||||
//:hello_test PASSED in 0.1s
|
||||
|
||||
INFO: Build completed successfully, 27 total actions
|
||||
</pre>
|
||||
|
||||
Congratulations! You've successfully built and run a test binary using
|
||||
GoogleTest.
|
||||
|
||||
## Next steps
|
||||
|
||||
* [Check out the Primer](primer.md) to start learning how to write simple
|
||||
tests.
|
||||
* [See the code samples](samples.md) for more examples showing how to use a
|
||||
variety of GoogleTest features.
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
# Quickstart: Building with CMake
|
||||
|
||||
This tutorial aims to get you up and running with GoogleTest using CMake. If
|
||||
you're using GoogleTest for the first time or need a refresher, we recommend
|
||||
this tutorial as a starting point. If your project uses Bazel, see the
|
||||
[Quickstart for Bazel](quickstart-bazel.md) instead.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
To complete this tutorial, you'll need:
|
||||
|
||||
* A compatible operating system (e.g. Linux, macOS, Windows).
|
||||
* A compatible C++ compiler that supports at least C++14.
|
||||
* [CMake](https://cmake.org/) and a compatible build tool for building the
|
||||
project.
|
||||
* Compatible build tools include
|
||||
[Make](https://www.gnu.org/software/make/),
|
||||
[Ninja](https://ninja-build.org/), and others - see
|
||||
[CMake Generators](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html)
|
||||
for more information.
|
||||
|
||||
See [Supported Platforms](platforms.md) for more information about platforms
|
||||
compatible with GoogleTest.
|
||||
|
||||
If you don't already have CMake installed, see the
|
||||
[CMake installation guide](https://cmake.org/install).
|
||||
|
||||
{: .callout .note}
|
||||
Note: The terminal commands in this tutorial show a Unix shell prompt, but the
|
||||
commands work on the Windows command line as well.
|
||||
|
||||
## Set up a project
|
||||
|
||||
CMake uses a file named `CMakeLists.txt` to configure the build system for a
|
||||
project. You'll use this file to set up your project and declare a dependency on
|
||||
GoogleTest.
|
||||
|
||||
First, create a directory for your project:
|
||||
|
||||
```
|
||||
$ mkdir my_project && cd my_project
|
||||
```
|
||||
|
||||
Next, you'll create the `CMakeLists.txt` file and declare a dependency on
|
||||
GoogleTest. There are many ways to express dependencies in the CMake ecosystem;
|
||||
in this quickstart, you'll use the
|
||||
[`FetchContent` CMake module](https://cmake.org/cmake/help/latest/module/FetchContent.html).
|
||||
To do this, in your project directory (`my_project`), create a file named
|
||||
`CMakeLists.txt` with the following contents:
|
||||
|
||||
```cmake
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
project(my_project)
|
||||
|
||||
# GoogleTest requires at least C++14
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
googletest
|
||||
URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip
|
||||
)
|
||||
# For Windows: Prevent overriding the parent project's compiler/linker settings
|
||||
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
||||
FetchContent_MakeAvailable(googletest)
|
||||
```
|
||||
|
||||
The above configuration declares a dependency on GoogleTest which is downloaded
|
||||
from GitHub. In the above example, `03597a01ee50ed33e9dfd640b249b4be3799d395` is
|
||||
the Git commit hash of the GoogleTest version to use; we recommend updating the
|
||||
hash often to point to the latest version.
|
||||
|
||||
For more information about how to create `CMakeLists.txt` files, see the
|
||||
[CMake Tutorial](https://cmake.org/cmake/help/latest/guide/tutorial/index.html).
|
||||
|
||||
## Create and run a binary
|
||||
|
||||
With GoogleTest declared as a dependency, you can use GoogleTest code within
|
||||
your own project.
|
||||
|
||||
As an example, create a file named `hello_test.cc` in your `my_project`
|
||||
directory with the following contents:
|
||||
|
||||
```cpp
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
// Demonstrate some basic assertions.
|
||||
TEST(HelloTest, BasicAssertions) {
|
||||
// Expect two strings not to be equal.
|
||||
EXPECT_STRNE("hello", "world");
|
||||
// Expect equality.
|
||||
EXPECT_EQ(7 * 6, 42);
|
||||
}
|
||||
```
|
||||
|
||||
GoogleTest provides [assertions](primer.md#assertions) that you use to test the
|
||||
behavior of your code. The above sample includes the main GoogleTest header file
|
||||
and demonstrates some basic assertions.
|
||||
|
||||
To build the code, add the following to the end of your `CMakeLists.txt` file:
|
||||
|
||||
```cmake
|
||||
enable_testing()
|
||||
|
||||
add_executable(
|
||||
hello_test
|
||||
hello_test.cc
|
||||
)
|
||||
target_link_libraries(
|
||||
hello_test
|
||||
GTest::gtest_main
|
||||
)
|
||||
|
||||
include(GoogleTest)
|
||||
gtest_discover_tests(hello_test)
|
||||
```
|
||||
|
||||
The above configuration enables testing in CMake, declares the C++ test binary
|
||||
you want to build (`hello_test`), and links it to GoogleTest (`gtest_main`). The
|
||||
last two lines enable CMake's test runner to discover the tests included in the
|
||||
binary, using the
|
||||
[`GoogleTest` CMake module](https://cmake.org/cmake/help/git-stage/module/GoogleTest.html).
|
||||
|
||||
Now you can build and run your test:
|
||||
|
||||
<pre>
|
||||
<strong>my_project$ cmake -S . -B build</strong>
|
||||
-- The C compiler identification is GNU 10.2.1
|
||||
-- The CXX compiler identification is GNU 10.2.1
|
||||
...
|
||||
-- Build files have been written to: .../my_project/build
|
||||
|
||||
<strong>my_project$ cmake --build build</strong>
|
||||
Scanning dependencies of target gtest
|
||||
...
|
||||
[100%] Built target gmock_main
|
||||
|
||||
<strong>my_project$ cd build && ctest</strong>
|
||||
Test project .../my_project/build
|
||||
Start 1: HelloTest.BasicAssertions
|
||||
1/1 Test #1: HelloTest.BasicAssertions ........ Passed 0.00 sec
|
||||
|
||||
100% tests passed, 0 tests failed out of 1
|
||||
|
||||
Total Test time (real) = 0.01 sec
|
||||
</pre>
|
||||
|
||||
Congratulations! You've successfully built and run a test binary using
|
||||
GoogleTest.
|
||||
|
||||
## Next steps
|
||||
|
||||
* [Check out the Primer](primer.md) to start learning how to write simple
|
||||
tests.
|
||||
* [See the code samples](samples.md) for more examples showing how to use a
|
||||
variety of GoogleTest features.
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
# Actions Reference
|
||||
|
||||
[**Actions**](../gmock_for_dummies.md#actions-what-should-it-do) specify what a
|
||||
mock function should do when invoked. This page lists the built-in actions
|
||||
provided by GoogleTest. All actions are defined in the `::testing` namespace.
|
||||
|
||||
## Returning a Value
|
||||
|
||||
| Action | Description |
|
||||
| :-------------------------------- | :-------------------------------------------- |
|
||||
| `Return()` | Return from a `void` mock function. |
|
||||
| `Return(value)` | Return `value`. If the type of `value` is different to the mock function's return type, `value` is converted to the latter type <i>at the time the expectation is set</i>, not when the action is executed. |
|
||||
| `ReturnArg<N>()` | Return the `N`-th (0-based) argument. |
|
||||
| `ReturnNew<T>(a1, ..., ak)` | Return `new T(a1, ..., ak)`; a different object is created each time. |
|
||||
| `ReturnNull()` | Return a null pointer. |
|
||||
| `ReturnPointee(ptr)` | Return the value pointed to by `ptr`. |
|
||||
| `ReturnRef(variable)` | Return a reference to `variable`. |
|
||||
| `ReturnRefOfCopy(value)` | Return a reference to a copy of `value`; the copy lives as long as the action. |
|
||||
| `ReturnRoundRobin({a1, ..., ak})` | Each call will return the next `ai` in the list, starting at the beginning when the end of the list is reached. |
|
||||
|
||||
## Side Effects
|
||||
|
||||
| Action | Description |
|
||||
| :--------------------------------- | :-------------------------------------- |
|
||||
| `Assign(&variable, value)` | Assign `value` to variable. |
|
||||
| `DeleteArg<N>()` | Delete the `N`-th (0-based) argument, which must be a pointer. |
|
||||
| `SaveArg<N>(pointer)` | Save the `N`-th (0-based) argument to `*pointer` by copy-assignment. |
|
||||
| `SaveArgByMove<N>(pointer)` | Save the `N`-th (0-based) argument to `*pointer` by move-assignment. |
|
||||
| `SaveArgPointee<N>(pointer)` | Save the value pointed to by the `N`-th (0-based) argument to `*pointer`. |
|
||||
| `SetArgReferee<N>(value)` | Assign `value` to the variable referenced by the `N`-th (0-based) argument. |
|
||||
| `SetArgPointee<N>(value)` | Assign `value` to the variable pointed by the `N`-th (0-based) argument. |
|
||||
| `SetArgumentPointee<N>(value)` | Same as `SetArgPointee<N>(value)`. Deprecated. Will be removed in v1.7.0. |
|
||||
| `SetArrayArgument<N>(first, last)` | Copies the elements in source range [`first`, `last`) to the array pointed to by the `N`-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. |
|
||||
| `SetErrnoAndReturn(error, value)` | Set `errno` to `error` and return `value`. |
|
||||
| `Throw(exception)` | Throws the given exception, which can be any copyable value. Available since v1.1.0. |
|
||||
|
||||
## Using a Function, Functor, or Lambda as an Action
|
||||
|
||||
In the following, by "callable" we mean a free function, `std::function`,
|
||||
functor, or lambda.
|
||||
|
||||
| Action | Description |
|
||||
| :---------------------------------- | :------------------------------------- |
|
||||
| `f` | Invoke `f` with the arguments passed to the mock function, where `f` is a callable. |
|
||||
| `Invoke(f)` | Invoke `f` with the arguments passed to the mock function, where `f` can be a global/static function or a functor. |
|
||||
| `Invoke(object_pointer, &class::method)` | Invoke the method on the object with the arguments passed to the mock function. |
|
||||
| `InvokeWithoutArgs(f)` | Invoke `f`, which can be a global/static function or a functor. `f` must take no arguments. |
|
||||
| `InvokeWithoutArgs(object_pointer, &class::method)` | Invoke the method on the object, which takes no arguments. |
|
||||
| `InvokeArgument<N>(arg1, arg2, ..., argk)` | Invoke the mock function's `N`-th (0-based) argument, which must be a function or a functor, with the `k` arguments. |
|
||||
|
||||
The return value of the invoked function is used as the return value of the
|
||||
action.
|
||||
|
||||
When defining a callable to be used with `Invoke*()`, you can declare any unused
|
||||
parameters as `Unused`:
|
||||
|
||||
```cpp
|
||||
using ::testing::Invoke;
|
||||
double Distance(Unused, double x, double y) { return sqrt(x*x + y*y); }
|
||||
...
|
||||
EXPECT_CALL(mock, Foo("Hi", _, _)).WillOnce(Invoke(Distance));
|
||||
```
|
||||
|
||||
`Invoke(callback)` and `InvokeWithoutArgs(callback)` take ownership of
|
||||
`callback`, which must be permanent. The type of `callback` must be a base
|
||||
callback type instead of a derived one, e.g.
|
||||
|
||||
```cpp
|
||||
BlockingClosure* done = new BlockingClosure;
|
||||
... Invoke(done) ...; // This won't compile!
|
||||
|
||||
Closure* done2 = new BlockingClosure;
|
||||
... Invoke(done2) ...; // This works.
|
||||
```
|
||||
|
||||
In `InvokeArgument<N>(...)`, if an argument needs to be passed by reference,
|
||||
wrap it inside `std::ref()`. For example,
|
||||
|
||||
```cpp
|
||||
using ::testing::InvokeArgument;
|
||||
...
|
||||
InvokeArgument<2>(5, string("Hi"), std::ref(foo))
|
||||
```
|
||||
|
||||
calls the mock function's #2 argument, passing to it `5` and `string("Hi")` by
|
||||
value, and `foo` by reference.
|
||||
|
||||
## Default Action
|
||||
|
||||
| Action | Description |
|
||||
| :------------ | :----------------------------------------------------- |
|
||||
| `DoDefault()` | Do the default action (specified by `ON_CALL()` or the built-in one). |
|
||||
|
||||
{: .callout .note}
|
||||
**Note:** due to technical reasons, `DoDefault()` cannot be used inside a
|
||||
composite action - trying to do so will result in a run-time error.
|
||||
|
||||
## Composite Actions
|
||||
|
||||
| Action | Description |
|
||||
| :----------------------------- | :------------------------------------------ |
|
||||
| `DoAll(a1, a2, ..., an)` | Do all actions `a1` to `an` and return the result of `an` in each invocation. The first `n - 1` sub-actions must return void and will receive a readonly view of the arguments. |
|
||||
| `IgnoreResult(a)` | Perform action `a` and ignore its result. `a` must not return void. |
|
||||
| `WithArg<N>(a)` | Pass the `N`-th (0-based) argument of the mock function to action `a` and perform it. |
|
||||
| `WithArgs<N1, N2, ..., Nk>(a)` | Pass the selected (0-based) arguments of the mock function to action `a` and perform it. |
|
||||
| `WithoutArgs(a)` | Perform action `a` without any arguments. |
|
||||
|
||||
## Defining Actions
|
||||
|
||||
| Macro | Description |
|
||||
| :--------------------------------- | :-------------------------------------- |
|
||||
| `ACTION(Sum) { return arg0 + arg1; }` | Defines an action `Sum()` to return the sum of the mock function's argument #0 and #1. |
|
||||
| `ACTION_P(Plus, n) { return arg0 + n; }` | Defines an action `Plus(n)` to return the sum of the mock function's argument #0 and `n`. |
|
||||
| `ACTION_Pk(Foo, p1, ..., pk) { statements; }` | Defines a parameterized action `Foo(p1, ..., pk)` to execute the given `statements`. |
|
||||
|
||||
The `ACTION*` macros cannot be used inside a function or class.
|
||||
+640
@@ -0,0 +1,640 @@
|
||||
# Assertions Reference
|
||||
|
||||
This page lists the assertion macros provided by GoogleTest for verifying code
|
||||
behavior. To use them, add `#include <gtest/gtest.h>`.
|
||||
|
||||
The majority of the macros listed below come as a pair with an `EXPECT_` variant
|
||||
and an `ASSERT_` variant. Upon failure, `EXPECT_` macros generate nonfatal
|
||||
failures and allow the current function to continue running, while `ASSERT_`
|
||||
macros generate fatal failures and abort the current function.
|
||||
|
||||
All assertion macros support streaming a custom failure message into them with
|
||||
the `<<` operator, for example:
|
||||
|
||||
```cpp
|
||||
EXPECT_TRUE(my_condition) << "My condition is not true";
|
||||
```
|
||||
|
||||
Anything that can be streamed to an `ostream` can be streamed to an assertion
|
||||
macro—in particular, C strings and string objects. If a wide string (`wchar_t*`,
|
||||
`TCHAR*` in `UNICODE` mode on Windows, or `std::wstring`) is streamed to an
|
||||
assertion, it will be translated to UTF-8 when printed.
|
||||
|
||||
## Explicit Success and Failure {#success-failure}
|
||||
|
||||
The assertions in this section generate a success or failure directly instead of
|
||||
testing a value or expression. These are useful when control flow, rather than a
|
||||
Boolean expression, determines the test's success or failure, as shown by the
|
||||
following example:
|
||||
|
||||
```c++
|
||||
switch(expression) {
|
||||
case 1:
|
||||
... some checks ...
|
||||
case 2:
|
||||
... some other checks ...
|
||||
default:
|
||||
FAIL() << "We shouldn't get here.";
|
||||
}
|
||||
```
|
||||
|
||||
### SUCCEED {#SUCCEED}
|
||||
|
||||
`SUCCEED()`
|
||||
|
||||
Generates a success. This *does not* make the overall test succeed. A test is
|
||||
considered successful only if none of its assertions fail during its execution.
|
||||
|
||||
The `SUCCEED` assertion is purely documentary and currently doesn't generate any
|
||||
user-visible output. However, we may add `SUCCEED` messages to GoogleTest output
|
||||
in the future.
|
||||
|
||||
### FAIL {#FAIL}
|
||||
|
||||
`FAIL()`
|
||||
|
||||
Generates a fatal failure, which returns from the current function.
|
||||
|
||||
Can only be used in functions that return `void`. See
|
||||
[Assertion Placement](../advanced.md#assertion-placement) for more information.
|
||||
|
||||
### ADD_FAILURE {#ADD_FAILURE}
|
||||
|
||||
`ADD_FAILURE()`
|
||||
|
||||
Generates a nonfatal failure, which allows the current function to continue
|
||||
running.
|
||||
|
||||
### ADD_FAILURE_AT {#ADD_FAILURE_AT}
|
||||
|
||||
`ADD_FAILURE_AT(`*`file_path`*`,`*`line_number`*`)`
|
||||
|
||||
Generates a nonfatal failure at the file and line number specified.
|
||||
|
||||
## Generalized Assertion {#generalized}
|
||||
|
||||
The following assertion allows [matchers](matchers.md) to be used to verify
|
||||
values.
|
||||
|
||||
### EXPECT_THAT {#EXPECT_THAT}
|
||||
|
||||
`EXPECT_THAT(`*`value`*`,`*`matcher`*`)` \
|
||||
`ASSERT_THAT(`*`value`*`,`*`matcher`*`)`
|
||||
|
||||
Verifies that *`value`* matches the [matcher](matchers.md) *`matcher`*.
|
||||
|
||||
For example, the following code verifies that the string `value1` starts with
|
||||
`"Hello"`, `value2` matches a regular expression, and `value3` is between 5 and
|
||||
10:
|
||||
|
||||
```cpp
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
using ::testing::AllOf;
|
||||
using ::testing::Gt;
|
||||
using ::testing::Lt;
|
||||
using ::testing::MatchesRegex;
|
||||
using ::testing::StartsWith;
|
||||
|
||||
...
|
||||
EXPECT_THAT(value1, StartsWith("Hello"));
|
||||
EXPECT_THAT(value2, MatchesRegex("Line \\d+"));
|
||||
ASSERT_THAT(value3, AllOf(Gt(5), Lt(10)));
|
||||
```
|
||||
|
||||
Matchers enable assertions of this form to read like English and generate
|
||||
informative failure messages. For example, if the above assertion on `value1`
|
||||
fails, the resulting message will be similar to the following:
|
||||
|
||||
```
|
||||
Value of: value1
|
||||
Actual: "Hi, world!"
|
||||
Expected: starts with "Hello"
|
||||
```
|
||||
|
||||
GoogleTest provides a built-in library of matchers—see the
|
||||
[Matchers Reference](matchers.md). It is also possible to write your own
|
||||
matchers—see [Writing New Matchers Quickly](../gmock_cook_book.md#NewMatchers).
|
||||
The use of matchers makes `EXPECT_THAT` a powerful, extensible assertion.
|
||||
|
||||
*The idea for this assertion was borrowed from Joe Walnes' Hamcrest project,
|
||||
which adds `assertThat()` to JUnit.*
|
||||
|
||||
## Boolean Conditions {#boolean}
|
||||
|
||||
The following assertions test Boolean conditions.
|
||||
|
||||
### EXPECT_TRUE {#EXPECT_TRUE}
|
||||
|
||||
`EXPECT_TRUE(`*`condition`*`)` \
|
||||
`ASSERT_TRUE(`*`condition`*`)`
|
||||
|
||||
Verifies that *`condition`* is true.
|
||||
|
||||
### EXPECT_FALSE {#EXPECT_FALSE}
|
||||
|
||||
`EXPECT_FALSE(`*`condition`*`)` \
|
||||
`ASSERT_FALSE(`*`condition`*`)`
|
||||
|
||||
Verifies that *`condition`* is false.
|
||||
|
||||
## Binary Comparison {#binary-comparison}
|
||||
|
||||
The following assertions compare two values. The value arguments must be
|
||||
comparable by the assertion's comparison operator, otherwise a compiler error
|
||||
will result.
|
||||
|
||||
If an argument supports the `<<` operator, it will be called to print the
|
||||
argument when the assertion fails. Otherwise, GoogleTest will attempt to print
|
||||
them in the best way it can—see
|
||||
[Teaching GoogleTest How to Print Your Values](../advanced.md#teaching-googletest-how-to-print-your-values).
|
||||
|
||||
Arguments are always evaluated exactly once, so it's OK for the arguments to
|
||||
have side effects. However, the argument evaluation order is undefined and
|
||||
programs should not depend on any particular argument evaluation order.
|
||||
|
||||
These assertions work with both narrow and wide string objects (`string` and
|
||||
`wstring`).
|
||||
|
||||
See also the [Floating-Point Comparison](#floating-point) assertions to compare
|
||||
floating-point numbers and avoid problems caused by rounding.
|
||||
|
||||
### EXPECT_EQ {#EXPECT_EQ}
|
||||
|
||||
`EXPECT_EQ(`*`val1`*`,`*`val2`*`)` \
|
||||
`ASSERT_EQ(`*`val1`*`,`*`val2`*`)`
|
||||
|
||||
Verifies that *`val1`*`==`*`val2`*.
|
||||
|
||||
Does pointer equality on pointers. If used on two C strings, it tests if they
|
||||
are in the same memory location, not if they have the same value. Use
|
||||
[`EXPECT_STREQ`](#EXPECT_STREQ) to compare C strings (e.g. `const char*`) by
|
||||
value.
|
||||
|
||||
When comparing a pointer to `NULL`, use `EXPECT_EQ(`*`ptr`*`, nullptr)` instead
|
||||
of `EXPECT_EQ(`*`ptr`*`, NULL)`.
|
||||
|
||||
### EXPECT_NE {#EXPECT_NE}
|
||||
|
||||
`EXPECT_NE(`*`val1`*`,`*`val2`*`)` \
|
||||
`ASSERT_NE(`*`val1`*`,`*`val2`*`)`
|
||||
|
||||
Verifies that *`val1`*`!=`*`val2`*.
|
||||
|
||||
Does pointer equality on pointers. If used on two C strings, it tests if they
|
||||
are in different memory locations, not if they have different values. Use
|
||||
[`EXPECT_STRNE`](#EXPECT_STRNE) to compare C strings (e.g. `const char*`) by
|
||||
value.
|
||||
|
||||
When comparing a pointer to `NULL`, use `EXPECT_NE(`*`ptr`*`, nullptr)` instead
|
||||
of `EXPECT_NE(`*`ptr`*`, NULL)`.
|
||||
|
||||
### EXPECT_LT {#EXPECT_LT}
|
||||
|
||||
`EXPECT_LT(`*`val1`*`,`*`val2`*`)` \
|
||||
`ASSERT_LT(`*`val1`*`,`*`val2`*`)`
|
||||
|
||||
Verifies that *`val1`*`<`*`val2`*.
|
||||
|
||||
### EXPECT_LE {#EXPECT_LE}
|
||||
|
||||
`EXPECT_LE(`*`val1`*`,`*`val2`*`)` \
|
||||
`ASSERT_LE(`*`val1`*`,`*`val2`*`)`
|
||||
|
||||
Verifies that *`val1`*`<=`*`val2`*.
|
||||
|
||||
### EXPECT_GT {#EXPECT_GT}
|
||||
|
||||
`EXPECT_GT(`*`val1`*`,`*`val2`*`)` \
|
||||
`ASSERT_GT(`*`val1`*`,`*`val2`*`)`
|
||||
|
||||
Verifies that *`val1`*`>`*`val2`*.
|
||||
|
||||
### EXPECT_GE {#EXPECT_GE}
|
||||
|
||||
`EXPECT_GE(`*`val1`*`,`*`val2`*`)` \
|
||||
`ASSERT_GE(`*`val1`*`,`*`val2`*`)`
|
||||
|
||||
Verifies that *`val1`*`>=`*`val2`*.
|
||||
|
||||
## String Comparison {#c-strings}
|
||||
|
||||
The following assertions compare two **C strings**. To compare two `string`
|
||||
objects, use [`EXPECT_EQ`](#EXPECT_EQ) or [`EXPECT_NE`](#EXPECT_NE) instead.
|
||||
|
||||
These assertions also accept wide C strings (`wchar_t*`). If a comparison of two
|
||||
wide strings fails, their values will be printed as UTF-8 narrow strings.
|
||||
|
||||
To compare a C string with `NULL`, use `EXPECT_EQ(`*`c_string`*`, nullptr)` or
|
||||
`EXPECT_NE(`*`c_string`*`, nullptr)`.
|
||||
|
||||
### EXPECT_STREQ {#EXPECT_STREQ}
|
||||
|
||||
`EXPECT_STREQ(`*`str1`*`,`*`str2`*`)` \
|
||||
`ASSERT_STREQ(`*`str1`*`,`*`str2`*`)`
|
||||
|
||||
Verifies that the two C strings *`str1`* and *`str2`* have the same contents.
|
||||
|
||||
### EXPECT_STRNE {#EXPECT_STRNE}
|
||||
|
||||
`EXPECT_STRNE(`*`str1`*`,`*`str2`*`)` \
|
||||
`ASSERT_STRNE(`*`str1`*`,`*`str2`*`)`
|
||||
|
||||
Verifies that the two C strings *`str1`* and *`str2`* have different contents.
|
||||
|
||||
### EXPECT_STRCASEEQ {#EXPECT_STRCASEEQ}
|
||||
|
||||
`EXPECT_STRCASEEQ(`*`str1`*`,`*`str2`*`)` \
|
||||
`ASSERT_STRCASEEQ(`*`str1`*`,`*`str2`*`)`
|
||||
|
||||
Verifies that the two C strings *`str1`* and *`str2`* have the same contents,
|
||||
ignoring case.
|
||||
|
||||
### EXPECT_STRCASENE {#EXPECT_STRCASENE}
|
||||
|
||||
`EXPECT_STRCASENE(`*`str1`*`,`*`str2`*`)` \
|
||||
`ASSERT_STRCASENE(`*`str1`*`,`*`str2`*`)`
|
||||
|
||||
Verifies that the two C strings *`str1`* and *`str2`* have different contents,
|
||||
ignoring case.
|
||||
|
||||
## Floating-Point Comparison {#floating-point}
|
||||
|
||||
The following assertions compare two floating-point values.
|
||||
|
||||
Due to rounding errors, it is very unlikely that two floating-point values will
|
||||
match exactly, so `EXPECT_EQ` is not suitable. In general, for floating-point
|
||||
comparison to make sense, the user needs to carefully choose the error bound.
|
||||
|
||||
GoogleTest also provides assertions that use a default error bound based on
|
||||
Units in the Last Place (ULPs). To learn more about ULPs, see the article
|
||||
[Comparing Floating Point Numbers](https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/).
|
||||
|
||||
### EXPECT_FLOAT_EQ {#EXPECT_FLOAT_EQ}
|
||||
|
||||
`EXPECT_FLOAT_EQ(`*`val1`*`,`*`val2`*`)` \
|
||||
`ASSERT_FLOAT_EQ(`*`val1`*`,`*`val2`*`)`
|
||||
|
||||
Verifies that the two `float` values *`val1`* and *`val2`* are approximately
|
||||
equal, to within 4 ULPs from each other. Infinity and the largest finite float
|
||||
value are considered to be one ULP apart.
|
||||
|
||||
### EXPECT_DOUBLE_EQ {#EXPECT_DOUBLE_EQ}
|
||||
|
||||
`EXPECT_DOUBLE_EQ(`*`val1`*`,`*`val2`*`)` \
|
||||
`ASSERT_DOUBLE_EQ(`*`val1`*`,`*`val2`*`)`
|
||||
|
||||
Verifies that the two `double` values *`val1`* and *`val2`* are approximately
|
||||
equal, to within 4 ULPs from each other. Infinity and the largest finite double
|
||||
value are considered to be one ULP apart.
|
||||
|
||||
### EXPECT_NEAR {#EXPECT_NEAR}
|
||||
|
||||
`EXPECT_NEAR(`*`val1`*`,`*`val2`*`,`*`abs_error`*`)` \
|
||||
`ASSERT_NEAR(`*`val1`*`,`*`val2`*`,`*`abs_error`*`)`
|
||||
|
||||
Verifies that the difference between *`val1`* and *`val2`* does not exceed the
|
||||
absolute error bound *`abs_error`*.
|
||||
|
||||
If *`val`* and *`val2`* are both infinity of the same sign, the difference is
|
||||
considered to be 0. Otherwise, if either value is infinity, the difference is
|
||||
considered to be infinity. All non-NaN values (including infinity) are
|
||||
considered to not exceed an *`abs_error`* of infinity.
|
||||
|
||||
## Exception Assertions {#exceptions}
|
||||
|
||||
The following assertions verify that a piece of code throws, or does not throw,
|
||||
an exception. Usage requires exceptions to be enabled in the build environment.
|
||||
|
||||
Note that the piece of code under test can be a compound statement, for example:
|
||||
|
||||
```cpp
|
||||
EXPECT_NO_THROW({
|
||||
int n = 5;
|
||||
DoSomething(&n);
|
||||
});
|
||||
```
|
||||
|
||||
### EXPECT_THROW {#EXPECT_THROW}
|
||||
|
||||
`EXPECT_THROW(`*`statement`*`,`*`exception_type`*`)` \
|
||||
`ASSERT_THROW(`*`statement`*`,`*`exception_type`*`)`
|
||||
|
||||
Verifies that *`statement`* throws an exception of type *`exception_type`*.
|
||||
|
||||
### EXPECT_ANY_THROW {#EXPECT_ANY_THROW}
|
||||
|
||||
`EXPECT_ANY_THROW(`*`statement`*`)` \
|
||||
`ASSERT_ANY_THROW(`*`statement`*`)`
|
||||
|
||||
Verifies that *`statement`* throws an exception of any type.
|
||||
|
||||
### EXPECT_NO_THROW {#EXPECT_NO_THROW}
|
||||
|
||||
`EXPECT_NO_THROW(`*`statement`*`)` \
|
||||
`ASSERT_NO_THROW(`*`statement`*`)`
|
||||
|
||||
Verifies that *`statement`* does not throw any exception.
|
||||
|
||||
## Predicate Assertions {#predicates}
|
||||
|
||||
The following assertions enable more complex predicates to be verified while
|
||||
printing a more clear failure message than if `EXPECT_TRUE` were used alone.
|
||||
|
||||
### EXPECT_PRED* {#EXPECT_PRED}
|
||||
|
||||
`EXPECT_PRED1(`*`pred`*`,`*`val1`*`)` \
|
||||
`EXPECT_PRED2(`*`pred`*`,`*`val1`*`,`*`val2`*`)` \
|
||||
`EXPECT_PRED3(`*`pred`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`)` \
|
||||
`EXPECT_PRED4(`*`pred`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`,`*`val4`*`)` \
|
||||
`EXPECT_PRED5(`*`pred`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`,`*`val4`*`,`*`val5`*`)`
|
||||
|
||||
`ASSERT_PRED1(`*`pred`*`,`*`val1`*`)` \
|
||||
`ASSERT_PRED2(`*`pred`*`,`*`val1`*`,`*`val2`*`)` \
|
||||
`ASSERT_PRED3(`*`pred`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`)` \
|
||||
`ASSERT_PRED4(`*`pred`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`,`*`val4`*`)` \
|
||||
`ASSERT_PRED5(`*`pred`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`,`*`val4`*`,`*`val5`*`)`
|
||||
|
||||
Verifies that the predicate *`pred`* returns `true` when passed the given values
|
||||
as arguments.
|
||||
|
||||
The parameter *`pred`* is a function or functor that accepts as many arguments
|
||||
as the corresponding macro accepts values. If *`pred`* returns `true` for the
|
||||
given arguments, the assertion succeeds, otherwise the assertion fails.
|
||||
|
||||
When the assertion fails, it prints the value of each argument. Arguments are
|
||||
always evaluated exactly once.
|
||||
|
||||
As an example, see the following code:
|
||||
|
||||
```cpp
|
||||
// Returns true if m and n have no common divisors except 1.
|
||||
bool MutuallyPrime(int m, int n) { ... }
|
||||
...
|
||||
const int a = 3;
|
||||
const int b = 4;
|
||||
const int c = 10;
|
||||
...
|
||||
EXPECT_PRED2(MutuallyPrime, a, b); // Succeeds
|
||||
EXPECT_PRED2(MutuallyPrime, b, c); // Fails
|
||||
```
|
||||
|
||||
In the above example, the first assertion succeeds, and the second fails with
|
||||
the following message:
|
||||
|
||||
```
|
||||
MutuallyPrime(b, c) is false, where
|
||||
b is 4
|
||||
c is 10
|
||||
```
|
||||
|
||||
Note that if the given predicate is an overloaded function or a function
|
||||
template, the assertion macro might not be able to determine which version to
|
||||
use, and it might be necessary to explicitly specify the type of the function.
|
||||
For example, for a Boolean function `IsPositive()` overloaded to take either a
|
||||
single `int` or `double` argument, it would be necessary to write one of the
|
||||
following:
|
||||
|
||||
```cpp
|
||||
EXPECT_PRED1(static_cast<bool (*)(int)>(IsPositive), 5);
|
||||
EXPECT_PRED1(static_cast<bool (*)(double)>(IsPositive), 3.14);
|
||||
```
|
||||
|
||||
Writing simply `EXPECT_PRED1(IsPositive, 5);` would result in a compiler error.
|
||||
Similarly, to use a template function, specify the template arguments:
|
||||
|
||||
```cpp
|
||||
template <typename T>
|
||||
bool IsNegative(T x) {
|
||||
return x < 0;
|
||||
}
|
||||
...
|
||||
EXPECT_PRED1(IsNegative<int>, -5); // Must specify type for IsNegative
|
||||
```
|
||||
|
||||
If a template has multiple parameters, wrap the predicate in parentheses so the
|
||||
macro arguments are parsed correctly:
|
||||
|
||||
```cpp
|
||||
ASSERT_PRED2((MyPredicate<int, int>), 5, 0);
|
||||
```
|
||||
|
||||
### EXPECT_PRED_FORMAT* {#EXPECT_PRED_FORMAT}
|
||||
|
||||
`EXPECT_PRED_FORMAT1(`*`pred_formatter`*`,`*`val1`*`)` \
|
||||
`EXPECT_PRED_FORMAT2(`*`pred_formatter`*`,`*`val1`*`,`*`val2`*`)` \
|
||||
`EXPECT_PRED_FORMAT3(`*`pred_formatter`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`)` \
|
||||
`EXPECT_PRED_FORMAT4(`*`pred_formatter`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`,`*`val4`*`)`
|
||||
\
|
||||
`EXPECT_PRED_FORMAT5(`*`pred_formatter`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`,`*`val4`*`,`*`val5`*`)`
|
||||
|
||||
`ASSERT_PRED_FORMAT1(`*`pred_formatter`*`,`*`val1`*`)` \
|
||||
`ASSERT_PRED_FORMAT2(`*`pred_formatter`*`,`*`val1`*`,`*`val2`*`)` \
|
||||
`ASSERT_PRED_FORMAT3(`*`pred_formatter`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`)` \
|
||||
`ASSERT_PRED_FORMAT4(`*`pred_formatter`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`,`*`val4`*`)`
|
||||
\
|
||||
`ASSERT_PRED_FORMAT5(`*`pred_formatter`*`,`*`val1`*`,`*`val2`*`,`*`val3`*`,`*`val4`*`,`*`val5`*`)`
|
||||
|
||||
Verifies that the predicate *`pred_formatter`* succeeds when passed the given
|
||||
values as arguments.
|
||||
|
||||
The parameter *`pred_formatter`* is a *predicate-formatter*, which is a function
|
||||
or functor with the signature:
|
||||
|
||||
```cpp
|
||||
testing::AssertionResult PredicateFormatter(const char* expr1,
|
||||
const char* expr2,
|
||||
...
|
||||
const char* exprn,
|
||||
T1 val1,
|
||||
T2 val2,
|
||||
...
|
||||
Tn valn);
|
||||
```
|
||||
|
||||
where *`val1`*, *`val2`*, ..., *`valn`* are the values of the predicate
|
||||
arguments, and *`expr1`*, *`expr2`*, ..., *`exprn`* are the corresponding
|
||||
expressions as they appear in the source code. The types `T1`, `T2`, ..., `Tn`
|
||||
can be either value types or reference types; if an argument has type `T`, it
|
||||
can be declared as either `T` or `const T&`, whichever is appropriate. For more
|
||||
about the return type `testing::AssertionResult`, see
|
||||
[Using a Function That Returns an AssertionResult](../advanced.md#using-a-function-that-returns-an-assertionresult).
|
||||
|
||||
As an example, see the following code:
|
||||
|
||||
```cpp
|
||||
// Returns the smallest prime common divisor of m and n,
|
||||
// or 1 when m and n are mutually prime.
|
||||
int SmallestPrimeCommonDivisor(int m, int n) { ... }
|
||||
|
||||
// Returns true if m and n have no common divisors except 1.
|
||||
bool MutuallyPrime(int m, int n) { ... }
|
||||
|
||||
// A predicate-formatter for asserting that two integers are mutually prime.
|
||||
testing::AssertionResult AssertMutuallyPrime(const char* m_expr,
|
||||
const char* n_expr,
|
||||
int m,
|
||||
int n) {
|
||||
if (MutuallyPrime(m, n)) return testing::AssertionSuccess();
|
||||
|
||||
return testing::AssertionFailure() << m_expr << " and " << n_expr
|
||||
<< " (" << m << " and " << n << ") are not mutually prime, "
|
||||
<< "as they have a common divisor " << SmallestPrimeCommonDivisor(m, n);
|
||||
}
|
||||
|
||||
...
|
||||
const int a = 3;
|
||||
const int b = 4;
|
||||
const int c = 10;
|
||||
...
|
||||
EXPECT_PRED_FORMAT2(AssertMutuallyPrime, a, b); // Succeeds
|
||||
EXPECT_PRED_FORMAT2(AssertMutuallyPrime, b, c); // Fails
|
||||
```
|
||||
|
||||
In the above example, the final assertion fails and the predicate-formatter
|
||||
produces the following failure message:
|
||||
|
||||
```
|
||||
b and c (4 and 10) are not mutually prime, as they have a common divisor 2
|
||||
```
|
||||
|
||||
## Windows HRESULT Assertions {#HRESULT}
|
||||
|
||||
The following assertions test for `HRESULT` success or failure. For example:
|
||||
|
||||
```cpp
|
||||
CComPtr<IShellDispatch2> shell;
|
||||
ASSERT_HRESULT_SUCCEEDED(shell.CoCreateInstance(L"Shell.Application"));
|
||||
CComVariant empty;
|
||||
ASSERT_HRESULT_SUCCEEDED(shell->ShellExecute(CComBSTR(url), empty, empty, empty, empty));
|
||||
```
|
||||
|
||||
The generated output contains the human-readable error message associated with
|
||||
the returned `HRESULT` code.
|
||||
|
||||
### EXPECT_HRESULT_SUCCEEDED {#EXPECT_HRESULT_SUCCEEDED}
|
||||
|
||||
`EXPECT_HRESULT_SUCCEEDED(`*`expression`*`)` \
|
||||
`ASSERT_HRESULT_SUCCEEDED(`*`expression`*`)`
|
||||
|
||||
Verifies that *`expression`* is a success `HRESULT`.
|
||||
|
||||
### EXPECT_HRESULT_FAILED {#EXPECT_HRESULT_FAILED}
|
||||
|
||||
`EXPECT_HRESULT_FAILED(`*`expression`*`)` \
|
||||
`ASSERT_HRESULT_FAILED(`*`expression`*`)`
|
||||
|
||||
Verifies that *`expression`* is a failure `HRESULT`.
|
||||
|
||||
## Death Assertions {#death}
|
||||
|
||||
The following assertions verify that a piece of code causes the process to
|
||||
terminate. For context, see [Death Tests](../advanced.md#death-tests).
|
||||
|
||||
These assertions spawn a new process and execute the code under test in that
|
||||
process. How that happens depends on the platform and the variable
|
||||
`::testing::GTEST_FLAG(death_test_style)`, which is initialized from the
|
||||
command-line flag `--gtest_death_test_style`.
|
||||
|
||||
* On POSIX systems, `fork()` (or `clone()` on Linux) is used to spawn the
|
||||
child, after which:
|
||||
* If the variable's value is `"fast"`, the death test statement is
|
||||
immediately executed.
|
||||
* If the variable's value is `"threadsafe"`, the child process re-executes
|
||||
the unit test binary just as it was originally invoked, but with some
|
||||
extra flags to cause just the single death test under consideration to
|
||||
be run.
|
||||
* On Windows, the child is spawned using the `CreateProcess()` API, and
|
||||
re-executes the binary to cause just the single death test under
|
||||
consideration to be run - much like the `"threadsafe"` mode on POSIX.
|
||||
|
||||
Other values for the variable are illegal and will cause the death test to fail.
|
||||
Currently, the flag's default value is
|
||||
**`"fast"`**.
|
||||
|
||||
If the death test statement runs to completion without dying, the child process
|
||||
will nonetheless terminate, and the assertion fails.
|
||||
|
||||
Note that the piece of code under test can be a compound statement, for example:
|
||||
|
||||
```cpp
|
||||
EXPECT_DEATH({
|
||||
int n = 5;
|
||||
DoSomething(&n);
|
||||
}, "Error on line .* of DoSomething()");
|
||||
```
|
||||
|
||||
### EXPECT_DEATH {#EXPECT_DEATH}
|
||||
|
||||
`EXPECT_DEATH(`*`statement`*`,`*`matcher`*`)` \
|
||||
`ASSERT_DEATH(`*`statement`*`,`*`matcher`*`)`
|
||||
|
||||
Verifies that *`statement`* causes the process to terminate with a nonzero exit
|
||||
status and produces `stderr` output that matches *`matcher`*.
|
||||
|
||||
The parameter *`matcher`* is either a [matcher](matchers.md) for a `const
|
||||
std::string&`, or a regular expression (see
|
||||
[Regular Expression Syntax](../advanced.md#regular-expression-syntax))—a bare
|
||||
string *`s`* (with no matcher) is treated as
|
||||
[`ContainsRegex(s)`](matchers.md#string-matchers), **not**
|
||||
[`Eq(s)`](matchers.md#generic-comparison).
|
||||
|
||||
For example, the following code verifies that calling `DoSomething(42)` causes
|
||||
the process to die with an error message that contains the text `My error`:
|
||||
|
||||
```cpp
|
||||
EXPECT_DEATH(DoSomething(42), "My error");
|
||||
```
|
||||
|
||||
### EXPECT_DEATH_IF_SUPPORTED {#EXPECT_DEATH_IF_SUPPORTED}
|
||||
|
||||
`EXPECT_DEATH_IF_SUPPORTED(`*`statement`*`,`*`matcher`*`)` \
|
||||
`ASSERT_DEATH_IF_SUPPORTED(`*`statement`*`,`*`matcher`*`)`
|
||||
|
||||
If death tests are supported, behaves the same as
|
||||
[`EXPECT_DEATH`](#EXPECT_DEATH). Otherwise, verifies nothing.
|
||||
|
||||
### EXPECT_DEBUG_DEATH {#EXPECT_DEBUG_DEATH}
|
||||
|
||||
`EXPECT_DEBUG_DEATH(`*`statement`*`,`*`matcher`*`)` \
|
||||
`ASSERT_DEBUG_DEATH(`*`statement`*`,`*`matcher`*`)`
|
||||
|
||||
In debug mode, behaves the same as [`EXPECT_DEATH`](#EXPECT_DEATH). When not in
|
||||
debug mode (i.e. `NDEBUG` is defined), just executes *`statement`*.
|
||||
|
||||
### EXPECT_EXIT {#EXPECT_EXIT}
|
||||
|
||||
`EXPECT_EXIT(`*`statement`*`,`*`predicate`*`,`*`matcher`*`)` \
|
||||
`ASSERT_EXIT(`*`statement`*`,`*`predicate`*`,`*`matcher`*`)`
|
||||
|
||||
Verifies that *`statement`* causes the process to terminate with an exit status
|
||||
that satisfies *`predicate`*, and produces `stderr` output that matches
|
||||
*`matcher`*.
|
||||
|
||||
The parameter *`predicate`* is a function or functor that accepts an `int` exit
|
||||
status and returns a `bool`. GoogleTest provides two predicates to handle common
|
||||
cases:
|
||||
|
||||
```cpp
|
||||
// Returns true if the program exited normally with the given exit status code.
|
||||
::testing::ExitedWithCode(exit_code);
|
||||
|
||||
// Returns true if the program was killed by the given signal.
|
||||
// Not available on Windows.
|
||||
::testing::KilledBySignal(signal_number);
|
||||
```
|
||||
|
||||
The parameter *`matcher`* is either a [matcher](matchers.md) for a `const
|
||||
std::string&`, or a regular expression (see
|
||||
[Regular Expression Syntax](../advanced.md#regular-expression-syntax))—a bare
|
||||
string *`s`* (with no matcher) is treated as
|
||||
[`ContainsRegex(s)`](matchers.md#string-matchers), **not**
|
||||
[`Eq(s)`](matchers.md#generic-comparison).
|
||||
|
||||
For example, the following code verifies that calling `NormalExit()` causes the
|
||||
process to print a message containing the text `Success` to `stderr` and exit
|
||||
with exit status code 0:
|
||||
|
||||
```cpp
|
||||
EXPECT_EXIT(NormalExit(), testing::ExitedWithCode(0), "Success");
|
||||
```
|
||||
+305
@@ -0,0 +1,305 @@
|
||||
# Matchers Reference
|
||||
|
||||
A **matcher** matches a *single* argument. You can use it inside `ON_CALL()` or
|
||||
`EXPECT_CALL()`, or use it to validate a value directly using two macros:
|
||||
|
||||
| Macro | Description |
|
||||
| :----------------------------------- | :------------------------------------ |
|
||||
| `EXPECT_THAT(actual_value, matcher)` | Asserts that `actual_value` matches `matcher`. |
|
||||
| `ASSERT_THAT(actual_value, matcher)` | The same as `EXPECT_THAT(actual_value, matcher)`, except that it generates a **fatal** failure. |
|
||||
|
||||
{: .callout .warning}
|
||||
**WARNING:** Equality matching via `EXPECT_THAT(actual_value, expected_value)`
|
||||
is supported, however note that implicit conversions can cause surprising
|
||||
results. For example, `EXPECT_THAT(some_bool, "some string")` will compile and
|
||||
may pass unintentionally.
|
||||
|
||||
**BEST PRACTICE:** Prefer to make the comparison explicit via
|
||||
`EXPECT_THAT(actual_value, Eq(expected_value))` or `EXPECT_EQ(actual_value,
|
||||
expected_value)`.
|
||||
|
||||
Built-in matchers (where `argument` is the function argument, e.g.
|
||||
`actual_value` in the example above, or when used in the context of
|
||||
`EXPECT_CALL(mock_object, method(matchers))`, the arguments of `method`) are
|
||||
divided into several categories. All matchers are defined in the `::testing`
|
||||
namespace unless otherwise noted.
|
||||
|
||||
## Wildcard
|
||||
|
||||
Matcher | Description
|
||||
:-------------------------- | :-----------------------------------------------
|
||||
`_` | `argument` can be any value of the correct type.
|
||||
`A<type>()` or `An<type>()` | `argument` can be any value of type `type`.
|
||||
|
||||
## Generic Comparison
|
||||
|
||||
| Matcher | Description |
|
||||
| :--------------------- | :-------------------------------------------------- |
|
||||
| `Eq(value)` or `value` | `argument == value` |
|
||||
| `Ge(value)` | `argument >= value` |
|
||||
| `Gt(value)` | `argument > value` |
|
||||
| `Le(value)` | `argument <= value` |
|
||||
| `Lt(value)` | `argument < value` |
|
||||
| `Ne(value)` | `argument != value` |
|
||||
| `IsFalse()` | `argument` evaluates to `false` in a Boolean context. |
|
||||
| `DistanceFrom(target, m)` | The distance between `argument` and `target` (computed by `abs(argument - target)`) matches `m`. |
|
||||
| `DistanceFrom(target, get_distance, m)` | The distance between `argument` and `target` (computed by `get_distance(argument, target)`) matches `m`. |
|
||||
| `IsTrue()` | `argument` evaluates to `true` in a Boolean context. |
|
||||
| `IsNull()` | `argument` is a `NULL` pointer (raw or smart). |
|
||||
| `NotNull()` | `argument` is a non-null pointer (raw or smart). |
|
||||
| `Optional(m)` | `argument` is `optional<>` that contains a value matching `m`. (For testing whether an `optional<>` is set, check for equality with `nullopt`. You may need to use `Eq(nullopt)` if the inner type doesn't have `==`.)|
|
||||
| `VariantWith<T>(m)` | `argument` is `variant<>` that holds the alternative of type T with a value matching `m`. |
|
||||
| `Ref(variable)` | `argument` is a reference to `variable`. |
|
||||
| `TypedEq<type>(value)` | `argument` has type `type` and is equal to `value`. You may need to use this instead of `Eq(value)` when the mock function is overloaded. |
|
||||
|
||||
Except `Ref()`, these matchers make a *copy* of `value` in case it's modified or
|
||||
destructed later. If the compiler complains that `value` doesn't have a public
|
||||
copy constructor, try wrap it in `std::ref()`, e.g.
|
||||
`Eq(std::ref(non_copyable_value))`. If you do that, make sure
|
||||
`non_copyable_value` is not changed afterwards, or the meaning of your matcher
|
||||
will be changed.
|
||||
|
||||
`IsTrue` and `IsFalse` are useful when you need to use a matcher, or for types
|
||||
that can be explicitly converted to Boolean, but are not implicitly converted to
|
||||
Boolean. In other cases, you can use the basic
|
||||
[`EXPECT_TRUE` and `EXPECT_FALSE`](assertions.md#boolean) assertions.
|
||||
|
||||
## Floating-Point Matchers {#FpMatchers}
|
||||
|
||||
| Matcher | Description |
|
||||
| :------------------------------- | :--------------------------------- |
|
||||
| `DoubleEq(a_double)` | `argument` is a `double` value approximately equal to `a_double`, treating two NaNs as unequal. |
|
||||
| `FloatEq(a_float)` | `argument` is a `float` value approximately equal to `a_float`, treating two NaNs as unequal. |
|
||||
| `NanSensitiveDoubleEq(a_double)` | `argument` is a `double` value approximately equal to `a_double`, treating two NaNs as equal. |
|
||||
| `NanSensitiveFloatEq(a_float)` | `argument` is a `float` value approximately equal to `a_float`, treating two NaNs as equal. |
|
||||
| `IsNan()` | `argument` is any floating-point type with a NaN value. |
|
||||
|
||||
The above matchers use ULP-based comparison (the same as used in googletest).
|
||||
They automatically pick a reasonable error bound based on the absolute value of
|
||||
the expected value. `DoubleEq()` and `FloatEq()` conform to the IEEE standard,
|
||||
which requires comparing two NaNs for equality to return false. The
|
||||
`NanSensitive*` version instead treats two NaNs as equal, which is often what a
|
||||
user wants.
|
||||
|
||||
| Matcher | Description |
|
||||
| :------------------------------------------------ | :----------------------- |
|
||||
| `DoubleNear(a_double, max_abs_error)` | `argument` is a `double` value close to `a_double` (absolute error <= `max_abs_error`), treating two NaNs as unequal. |
|
||||
| `FloatNear(a_float, max_abs_error)` | `argument` is a `float` value close to `a_float` (absolute error <= `max_abs_error`), treating two NaNs as unequal. |
|
||||
| `NanSensitiveDoubleNear(a_double, max_abs_error)` | `argument` is a `double` value close to `a_double` (absolute error <= `max_abs_error`), treating two NaNs as equal. |
|
||||
| `NanSensitiveFloatNear(a_float, max_abs_error)` | `argument` is a `float` value close to `a_float` (absolute error <= `max_abs_error`), treating two NaNs as equal. |
|
||||
|
||||
## String Matchers
|
||||
|
||||
The `argument` can be either a C string or a C++ string object:
|
||||
|
||||
| Matcher | Description |
|
||||
| :---------------------- | :------------------------------------------------- |
|
||||
| `ContainsRegex(string)` | `argument` matches the given regular expression. |
|
||||
| `EndsWith(suffix)` | `argument` ends with string `suffix`. |
|
||||
| `HasSubstr(string)` | `argument` contains `string` as a sub-string. |
|
||||
| `IsEmpty()` | `argument` is an empty string. |
|
||||
| `MatchesRegex(string)` | `argument` matches the given regular expression with the match starting at the first character and ending at the last character. |
|
||||
| `StartsWith(prefix)` | `argument` starts with string `prefix`. |
|
||||
| `StrCaseEq(string)` | `argument` is equal to `string`, ignoring case. |
|
||||
| `StrCaseNe(string)` | `argument` is not equal to `string`, ignoring case. |
|
||||
| `StrEq(string)` | `argument` is equal to `string`. |
|
||||
| `StrNe(string)` | `argument` is not equal to `string`. |
|
||||
| `WhenBase64Unescaped(m)` | `argument` is a base-64 escaped string whose unescaped string matches `m`. The web-safe format from [RFC 4648](https://www.rfc-editor.org/rfc/rfc4648#section-5) is supported. |
|
||||
|
||||
`ContainsRegex()` and `MatchesRegex()` take ownership of the `RE` object. They
|
||||
use the regular expression syntax defined
|
||||
[here](../advanced.md#regular-expression-syntax). All of these matchers, except
|
||||
`ContainsRegex()` and `MatchesRegex()` work for wide strings as well.
|
||||
|
||||
## Container Matchers
|
||||
|
||||
Most STL-style containers support `==`, so you can use `Eq(expected_container)`
|
||||
or simply `expected_container` to match a container exactly. If you want to
|
||||
write the elements in-line, match them more flexibly, or get more informative
|
||||
messages, you can use:
|
||||
|
||||
| Matcher | Description |
|
||||
| :---------------------------------------- | :------------------------------- |
|
||||
| `BeginEndDistanceIs(m)` | `argument` is a container whose `begin()` and `end()` iterators are separated by a number of increments matching `m`. E.g. `BeginEndDistanceIs(2)` or `BeginEndDistanceIs(Lt(2))`. For containers that define a `size()` method, `SizeIs(m)` may be more efficient. |
|
||||
| `ContainerEq(container)` | The same as `Eq(container)` except that the failure message also includes which elements are in one container but not the other. |
|
||||
| `Contains(e)` | `argument` contains an element that matches `e`, which can be either a value or a matcher. |
|
||||
| `Contains(e).Times(n)` | `argument` contains elements that match `e`, which can be either a value or a matcher, and the number of matches is `n`, which can be either a value or a matcher. Unlike the plain `Contains` and `Each` this allows to check for arbitrary occurrences including testing for absence with `Contains(e).Times(0)`. |
|
||||
| `Each(e)` | `argument` is a container where *every* element matches `e`, which can be either a value or a matcher. |
|
||||
| `ElementsAre(e0, e1, ..., en)` | `argument` has `n + 1` elements, where the *i*-th element matches `ei`, which can be a value or a matcher. |
|
||||
| `ElementsAreArray({e0, e1, ..., en})`, `ElementsAreArray(a_container)`, `ElementsAreArray(begin, end)`, `ElementsAreArray(array)`, or `ElementsAreArray(array, count)` | The same as `ElementsAre()` except that the expected element values/matchers come from an initializer list, STL-style container, iterator range, or C-style array. |
|
||||
| `IsEmpty()` | `argument` is an empty container (`container.empty()`). |
|
||||
| `IsSubsetOf({e0, e1, ..., en})`, `IsSubsetOf(a_container)`, `IsSubsetOf(begin, end)`, `IsSubsetOf(array)`, or `IsSubsetOf(array, count)` | `argument` matches `UnorderedElementsAre(x0, x1, ..., xk)` for some subset `{x0, x1, ..., xk}` of the expected matchers. |
|
||||
| `IsSupersetOf({e0, e1, ..., en})`, `IsSupersetOf(a_container)`, `IsSupersetOf(begin, end)`, `IsSupersetOf(array)`, or `IsSupersetOf(array, count)` | Some subset of `argument` matches `UnorderedElementsAre(`expected matchers`)`. |
|
||||
| `Pointwise(m, container)`, `Pointwise(m, {e0, e1, ..., en})` | `argument` contains the same number of elements as in `container`, and for all i, (the i-th element in `argument`, the i-th element in `container`) match `m`, which is a matcher on 2-tuples. E.g. `Pointwise(Le(), upper_bounds)` verifies that each element in `argument` doesn't exceed the corresponding element in `upper_bounds`. See more detail below. |
|
||||
| `SizeIs(m)` | `argument` is a container whose size matches `m`. E.g. `SizeIs(2)` or `SizeIs(Lt(2))`. |
|
||||
| `UnorderedElementsAre(e0, e1, ..., en)` | `argument` has `n + 1` elements, and under *some* permutation of the elements, each element matches an `ei` (for a different `i`), which can be a value or a matcher. |
|
||||
| `UnorderedElementsAreArray({e0, e1, ..., en})`, `UnorderedElementsAreArray(a_container)`, `UnorderedElementsAreArray(begin, end)`, `UnorderedElementsAreArray(array)`, or `UnorderedElementsAreArray(array, count)` | The same as `UnorderedElementsAre()` except that the expected element values/matchers come from an initializer list, STL-style container, iterator range, or C-style array. |
|
||||
| `UnorderedPointwise(m, container)`, `UnorderedPointwise(m, {e0, e1, ..., en})` | Like `Pointwise(m, container)`, but ignores the order of elements. |
|
||||
| `WhenSorted(m)` | When `argument` is sorted using the `<` operator, it matches container matcher `m`. E.g. `WhenSorted(ElementsAre(1, 2, 3))` verifies that `argument` contains elements 1, 2, and 3, ignoring order. |
|
||||
| `WhenSortedBy(comparator, m)` | The same as `WhenSorted(m)`, except that the given comparator instead of `<` is used to sort `argument`. E.g. `WhenSortedBy(std::greater(), ElementsAre(3, 2, 1))`. |
|
||||
|
||||
**Notes:**
|
||||
|
||||
* These matchers can also match:
|
||||
1. a native array passed by reference (e.g. in `Foo(const int (&a)[5])`),
|
||||
and
|
||||
2. an array passed as a pointer and a count (e.g. in `Bar(const T* buffer,
|
||||
int len)` -- see [Multi-argument Matchers](#MultiArgMatchers)).
|
||||
* The array being matched may be multi-dimensional (i.e. its elements can be
|
||||
arrays).
|
||||
* `m` in `Pointwise(m, ...)` and `UnorderedPointwise(m, ...)` should be a
|
||||
matcher for `::std::tuple<T, U>` where `T` and `U` are the element type of
|
||||
the actual container and the expected container, respectively. For example,
|
||||
to compare two `Foo` containers where `Foo` doesn't support `operator==`,
|
||||
one might write:
|
||||
|
||||
```cpp
|
||||
MATCHER(FooEq, "") {
|
||||
return std::get<0>(arg).Equals(std::get<1>(arg));
|
||||
}
|
||||
...
|
||||
EXPECT_THAT(actual_foos, Pointwise(FooEq(), expected_foos));
|
||||
```
|
||||
|
||||
## Member Matchers
|
||||
|
||||
| Matcher | Description |
|
||||
| :------------------------------ | :----------------------------------------- |
|
||||
| `Field(&class::field, m)` | `argument.field` (or `argument->field` when `argument` is a plain pointer) matches matcher `m`, where `argument` is an object of type _class_. |
|
||||
| `Field(field_name, &class::field, m)` | The same as the two-parameter version, but provides a better error message. |
|
||||
| `Key(e)` | `argument.first` matches `e`, which can be either a value or a matcher. E.g. `Contains(Key(Le(5)))` can verify that a `map` contains a key `<= 5`. |
|
||||
| `Pair(m1, m2)` | `argument` is an `std::pair` whose `first` field matches `m1` and `second` field matches `m2`. |
|
||||
| `FieldsAre(m...)` | `argument` is a compatible object where each field matches piecewise with the matchers `m...`. A compatible object is any that supports the `std::tuple_size<Obj>`+`get<I>(obj)` protocol. In C++17 and up this also supports types compatible with structured bindings, like aggregates. |
|
||||
| `Property(&class::property, m)` | `argument.property()` (or `argument->property()` when `argument` is a plain pointer) matches matcher `m`, where `argument` is an object of type _class_. The method `property()` must take no argument and be declared as `const`. |
|
||||
| `Property(property_name, &class::property, m)` | The same as the two-parameter version, but provides a better error message.
|
||||
|
||||
{: .callout .warning}
|
||||
Warning: Don't use `Property()` against member functions that you do not own,
|
||||
because taking addresses of functions is fragile and generally not part of the
|
||||
contract of the function.
|
||||
|
||||
**Notes:**
|
||||
|
||||
* You can use `FieldsAre()` to match any type that supports structured
|
||||
bindings, such as `std::tuple`, `std::pair`, `std::array`, and aggregate
|
||||
types. For example:
|
||||
|
||||
```cpp
|
||||
std::tuple<int, std::string> my_tuple{7, "hello world"};
|
||||
EXPECT_THAT(my_tuple, FieldsAre(Ge(0), HasSubstr("hello")));
|
||||
|
||||
struct MyStruct {
|
||||
int value = 42;
|
||||
std::string greeting = "aloha";
|
||||
};
|
||||
MyStruct s;
|
||||
EXPECT_THAT(s, FieldsAre(42, "aloha"));
|
||||
```
|
||||
|
||||
## Matching the Result of a Function, Functor, or Callback
|
||||
|
||||
| Matcher | Description |
|
||||
| :--------------- | :------------------------------------------------ |
|
||||
| `ResultOf(f, m)` | `f(argument)` matches matcher `m`, where `f` is a function or functor. |
|
||||
| `ResultOf(result_description, f, m)` | The same as the two-parameter version, but provides a better error message.
|
||||
|
||||
## Pointer Matchers
|
||||
|
||||
| Matcher | Description |
|
||||
| :------------------------ | :---------------------------------------------- |
|
||||
| `Address(m)` | the result of `std::addressof(argument)` matches `m`. |
|
||||
| `Pointee(m)` | `argument` (either a smart pointer or a raw pointer) points to a value that matches matcher `m`. |
|
||||
| `Pointer(m)` | `argument` (either a smart pointer or a raw pointer) contains a pointer that matches `m`. `m` will match against the raw pointer regardless of the type of `argument`. |
|
||||
| `WhenDynamicCastTo<T>(m)` | when `argument` is passed through `dynamic_cast<T>()`, it matches matcher `m`. |
|
||||
|
||||
## Multi-argument Matchers {#MultiArgMatchers}
|
||||
|
||||
Technically, all matchers match a *single* value. A "multi-argument" matcher is
|
||||
just one that matches a *tuple*. The following matchers can be used to match a
|
||||
tuple `(x, y)`:
|
||||
|
||||
Matcher | Description
|
||||
:------ | :----------
|
||||
`Eq()` | `x == y`
|
||||
`Ge()` | `x >= y`
|
||||
`Gt()` | `x > y`
|
||||
`Le()` | `x <= y`
|
||||
`Lt()` | `x < y`
|
||||
`Ne()` | `x != y`
|
||||
|
||||
You can use the following selectors to pick a subset of the arguments (or
|
||||
reorder them) to participate in the matching:
|
||||
|
||||
| Matcher | Description |
|
||||
| :------------------------- | :---------------------------------------------- |
|
||||
| `AllArgs(m)` | Equivalent to `m`. Useful as syntactic sugar in `.With(AllArgs(m))`. |
|
||||
| `Args<N1, N2, ..., Nk>(m)` | The tuple of the `k` selected (using 0-based indices) arguments matches `m`, e.g. `Args<1, 2>(Eq())`. |
|
||||
|
||||
## Composite Matchers
|
||||
|
||||
You can make a matcher from one or more other matchers:
|
||||
|
||||
| Matcher | Description |
|
||||
| :------------------------------- | :-------------------------------------- |
|
||||
| `AllOf(m1, m2, ..., mn)` | `argument` matches all of the matchers `m1` to `mn`. |
|
||||
| `AllOfArray({m0, m1, ..., mn})`, `AllOfArray(a_container)`, `AllOfArray(begin, end)`, `AllOfArray(array)`, or `AllOfArray(array, count)` | The same as `AllOf()` except that the matchers come from an initializer list, STL-style container, iterator range, or C-style array. |
|
||||
| `AnyOf(m1, m2, ..., mn)` | `argument` matches at least one of the matchers `m1` to `mn`. |
|
||||
| `AnyOfArray({m0, m1, ..., mn})`, `AnyOfArray(a_container)`, `AnyOfArray(begin, end)`, `AnyOfArray(array)`, or `AnyOfArray(array, count)` | The same as `AnyOf()` except that the matchers come from an initializer list, STL-style container, iterator range, or C-style array. |
|
||||
| `Not(m)` | `argument` doesn't match matcher `m`. |
|
||||
| `Conditional(cond, m1, m2)` | Matches matcher `m1` if `cond` evaluates to true, else matches `m2`.|
|
||||
|
||||
## Adapters for Matchers
|
||||
|
||||
| Matcher | Description |
|
||||
| :---------------------- | :------------------------------------ |
|
||||
| `MatcherCast<T>(m)` | casts matcher `m` to type `Matcher<T>`. |
|
||||
| `SafeMatcherCast<T>(m)` | [safely casts](../gmock_cook_book.md#SafeMatcherCast) matcher `m` to type `Matcher<T>`. |
|
||||
| `Truly(predicate)` | `predicate(argument)` returns something considered by C++ to be true, where `predicate` is a function or functor. |
|
||||
|
||||
`AddressSatisfies(callback)` and `Truly(callback)` take ownership of `callback`,
|
||||
which must be a permanent callback.
|
||||
|
||||
## Using Matchers as Predicates {#MatchersAsPredicatesCheat}
|
||||
|
||||
| Matcher | Description |
|
||||
| :---------------------------- | :------------------------------------------ |
|
||||
| `Matches(m)(value)` | evaluates to `true` if `value` matches `m`. You can use `Matches(m)` alone as a unary functor. |
|
||||
| `ExplainMatchResult(m, value, result_listener)` | evaluates to `true` if `value` matches `m`, explaining the result to `result_listener`. |
|
||||
| `Value(value, m)` | evaluates to `true` if `value` matches `m`. |
|
||||
|
||||
## Defining Matchers
|
||||
|
||||
| Macro | Description |
|
||||
| :----------------------------------- | :------------------------------------ |
|
||||
| `MATCHER(IsEven, "") { return (arg % 2) == 0; }` | Defines a matcher `IsEven()` to match an even number. |
|
||||
| `MATCHER_P(IsDivisibleBy, n, "") { *result_listener << "where the remainder is " << (arg % n); return (arg % n) == 0; }` | Defines a matcher `IsDivisibleBy(n)` to match a number divisible by `n`. |
|
||||
| `MATCHER_P2(IsBetween, a, b, absl::StrCat(negation ? "isn't" : "is", " between ", PrintToString(a), " and ", PrintToString(b))) { return a <= arg && arg <= b; }` | Defines a matcher `IsBetween(a, b)` to match a value in the range [`a`, `b`]. |
|
||||
|
||||
**Notes:**
|
||||
|
||||
1. The `MATCHER*` macros cannot be used inside a function or class.
|
||||
2. The matcher body must be *purely functional* (i.e. it cannot have any side
|
||||
effect, and the result must not depend on anything other than the value
|
||||
being matched and the matcher parameters).
|
||||
3. You can use `PrintToString(x)` to convert a value `x` of any type to a
|
||||
string.
|
||||
4. You can use `ExplainMatchResult()` in a custom matcher to wrap another
|
||||
matcher, for example:
|
||||
|
||||
```cpp
|
||||
MATCHER_P(NestedPropertyMatches, matcher, "") {
|
||||
return ExplainMatchResult(matcher, arg.nested().property(), result_listener);
|
||||
}
|
||||
```
|
||||
|
||||
5. You can use `DescribeMatcher<>` to describe another matcher. For example:
|
||||
|
||||
```cpp
|
||||
MATCHER_P(XAndYThat, matcher,
|
||||
"X that " + DescribeMatcher<int>(matcher, negation) +
|
||||
(negation ? " or" : " and") + " Y that " +
|
||||
DescribeMatcher<double>(matcher, negation)) {
|
||||
return ExplainMatchResult(matcher, arg.x(), result_listener) &&
|
||||
ExplainMatchResult(matcher, arg.y(), result_listener);
|
||||
}
|
||||
```
|
||||
+588
@@ -0,0 +1,588 @@
|
||||
# Mocking Reference
|
||||
|
||||
This page lists the facilities provided by GoogleTest for creating and working
|
||||
with mock objects. To use them, add `#include <gmock/gmock.h>`.
|
||||
|
||||
## Macros {#macros}
|
||||
|
||||
GoogleTest defines the following macros for working with mocks.
|
||||
|
||||
### MOCK_METHOD {#MOCK_METHOD}
|
||||
|
||||
`MOCK_METHOD(`*`return_type`*`,`*`method_name`*`, (`*`args...`*`));` \
|
||||
`MOCK_METHOD(`*`return_type`*`,`*`method_name`*`, (`*`args...`*`),
|
||||
(`*`specs...`*`));`
|
||||
|
||||
Defines a mock method *`method_name`* with arguments `(`*`args...`*`)` and
|
||||
return type *`return_type`* within a mock class.
|
||||
|
||||
The parameters of `MOCK_METHOD` mirror the method declaration. The optional
|
||||
fourth parameter *`specs...`* is a comma-separated list of qualifiers. The
|
||||
following qualifiers are accepted:
|
||||
|
||||
| Qualifier | Meaning |
|
||||
| -------------------------- | -------------------------------------------- |
|
||||
| `const` | Makes the mocked method a `const` method. Required if overriding a `const` method. |
|
||||
| `override` | Marks the method with `override`. Recommended if overriding a `virtual` method. |
|
||||
| `noexcept` | Marks the method with `noexcept`. Required if overriding a `noexcept` method. |
|
||||
| `Calltype(`*`calltype`*`)` | Sets the call type for the method, for example `Calltype(STDMETHODCALLTYPE)`. Useful on Windows. |
|
||||
| `ref(`*`qualifier`*`)` | Marks the method with the given reference qualifier, for example `ref(&)` or `ref(&&)`. Required if overriding a method that has a reference qualifier. |
|
||||
|
||||
Note that commas in arguments prevent `MOCK_METHOD` from parsing the arguments
|
||||
correctly if they are not appropriately surrounded by parentheses. See the
|
||||
following example:
|
||||
|
||||
```cpp
|
||||
class MyMock {
|
||||
public:
|
||||
// The following 2 lines will not compile due to commas in the arguments:
|
||||
MOCK_METHOD(std::pair<bool, int>, GetPair, ()); // Error!
|
||||
MOCK_METHOD(bool, CheckMap, (std::map<int, double>, bool)); // Error!
|
||||
|
||||
// One solution - wrap arguments that contain commas in parentheses:
|
||||
MOCK_METHOD((std::pair<bool, int>), GetPair, ());
|
||||
MOCK_METHOD(bool, CheckMap, ((std::map<int, double>), bool));
|
||||
|
||||
// Another solution - use type aliases:
|
||||
using BoolAndInt = std::pair<bool, int>;
|
||||
MOCK_METHOD(BoolAndInt, GetPair, ());
|
||||
using MapIntDouble = std::map<int, double>;
|
||||
MOCK_METHOD(bool, CheckMap, (MapIntDouble, bool));
|
||||
};
|
||||
```
|
||||
|
||||
`MOCK_METHOD` must be used in the `public:` section of a mock class definition,
|
||||
regardless of whether the method being mocked is `public`, `protected`, or
|
||||
`private` in the base class.
|
||||
|
||||
### EXPECT_CALL {#EXPECT_CALL}
|
||||
|
||||
`EXPECT_CALL(`*`mock_object`*`,`*`method_name`*`(`*`matchers...`*`))`
|
||||
|
||||
Creates an [expectation](../gmock_for_dummies.md#setting-expectations) that the
|
||||
method *`method_name`* of the object *`mock_object`* is called with arguments
|
||||
that match the given matchers *`matchers...`*. `EXPECT_CALL` must precede any
|
||||
code that exercises the mock object.
|
||||
|
||||
The parameter *`matchers...`* is a comma-separated list of
|
||||
[matchers](../gmock_for_dummies.md#matchers-what-arguments-do-we-expect) that
|
||||
correspond to each argument of the method *`method_name`*. The expectation will
|
||||
apply only to calls of *`method_name`* whose arguments match all of the
|
||||
matchers. If `(`*`matchers...`*`)` is omitted, the expectation behaves as if
|
||||
each argument's matcher were a [wildcard matcher (`_`)](matchers.md#wildcard).
|
||||
See the [Matchers Reference](matchers.md) for a list of all built-in matchers.
|
||||
|
||||
The following chainable clauses can be used to modify the expectation, and they
|
||||
must be used in the following order:
|
||||
|
||||
```cpp
|
||||
EXPECT_CALL(mock_object, method_name(matchers...))
|
||||
.With(multi_argument_matcher) // Can be used at most once
|
||||
.Times(cardinality) // Can be used at most once
|
||||
.InSequence(sequences...) // Can be used any number of times
|
||||
.After(expectations...) // Can be used any number of times
|
||||
.WillOnce(action) // Can be used any number of times
|
||||
.WillRepeatedly(action) // Can be used at most once
|
||||
.RetiresOnSaturation(); // Can be used at most once
|
||||
```
|
||||
|
||||
See details for each modifier clause below.
|
||||
|
||||
#### With {#EXPECT_CALL.With}
|
||||
|
||||
`.With(`*`multi_argument_matcher`*`)`
|
||||
|
||||
Restricts the expectation to apply only to mock function calls whose arguments
|
||||
as a whole match the multi-argument matcher *`multi_argument_matcher`*.
|
||||
|
||||
GoogleTest passes all of the arguments as one tuple into the matcher. The
|
||||
parameter *`multi_argument_matcher`* must thus be a matcher of type
|
||||
`Matcher<std::tuple<A1, ..., An>>`, where `A1, ..., An` are the types of the
|
||||
function arguments.
|
||||
|
||||
For example, the following code sets the expectation that
|
||||
`my_mock.SetPosition()` is called with any two arguments, the first argument
|
||||
being less than the second:
|
||||
|
||||
```cpp
|
||||
using ::testing::_;
|
||||
using ::testing::Lt;
|
||||
...
|
||||
EXPECT_CALL(my_mock, SetPosition(_, _))
|
||||
.With(Lt());
|
||||
```
|
||||
|
||||
GoogleTest provides some built-in matchers for 2-tuples, including the `Lt()`
|
||||
matcher above. See [Multi-argument Matchers](matchers.md#MultiArgMatchers).
|
||||
|
||||
The `With` clause can be used at most once on an expectation and must be the
|
||||
first clause.
|
||||
|
||||
#### Times {#EXPECT_CALL.Times}
|
||||
|
||||
`.Times(`*`cardinality`*`)`
|
||||
|
||||
Specifies how many times the mock function call is expected.
|
||||
|
||||
The parameter *`cardinality`* represents the number of expected calls and can be
|
||||
one of the following, all defined in the `::testing` namespace:
|
||||
|
||||
| Cardinality | Meaning |
|
||||
| ------------------- | --------------------------------------------------- |
|
||||
| `AnyNumber()` | The function can be called any number of times. |
|
||||
| `AtLeast(n)` | The function call is expected at least *n* times. |
|
||||
| `AtMost(n)` | The function call is expected at most *n* times. |
|
||||
| `Between(m, n)` | The function call is expected between *m* and *n* times, inclusive. |
|
||||
| `Exactly(n)` or `n` | The function call is expected exactly *n* times. If *n* is 0, the call should never happen. |
|
||||
|
||||
If the `Times` clause is omitted, GoogleTest infers the cardinality as follows:
|
||||
|
||||
* If neither [`WillOnce`](#EXPECT_CALL.WillOnce) nor
|
||||
[`WillRepeatedly`](#EXPECT_CALL.WillRepeatedly) are specified, the inferred
|
||||
cardinality is `Times(1)`.
|
||||
* If there are *n* `WillOnce` clauses and no `WillRepeatedly` clause, where
|
||||
*n* >= 1, the inferred cardinality is `Times(n)`.
|
||||
* If there are *n* `WillOnce` clauses and one `WillRepeatedly` clause, where
|
||||
*n* >= 0, the inferred cardinality is `Times(AtLeast(n))`.
|
||||
|
||||
The `Times` clause can be used at most once on an expectation.
|
||||
|
||||
#### InSequence {#EXPECT_CALL.InSequence}
|
||||
|
||||
`.InSequence(`*`sequences...`*`)`
|
||||
|
||||
Specifies that the mock function call is expected in a certain sequence.
|
||||
|
||||
The parameter *`sequences...`* is any number of [`Sequence`](#Sequence) objects.
|
||||
Expected calls assigned to the same sequence are expected to occur in the order
|
||||
the expectations are declared.
|
||||
|
||||
For example, the following code sets the expectation that the `Reset()` method
|
||||
of `my_mock` is called before both `GetSize()` and `Describe()`, and `GetSize()`
|
||||
and `Describe()` can occur in any order relative to each other:
|
||||
|
||||
```cpp
|
||||
using ::testing::Sequence;
|
||||
Sequence s1, s2;
|
||||
...
|
||||
EXPECT_CALL(my_mock, Reset())
|
||||
.InSequence(s1, s2);
|
||||
EXPECT_CALL(my_mock, GetSize())
|
||||
.InSequence(s1);
|
||||
EXPECT_CALL(my_mock, Describe())
|
||||
.InSequence(s2);
|
||||
```
|
||||
|
||||
The `InSequence` clause can be used any number of times on an expectation.
|
||||
|
||||
See also the [`InSequence` class](#InSequence).
|
||||
|
||||
#### After {#EXPECT_CALL.After}
|
||||
|
||||
`.After(`*`expectations...`*`)`
|
||||
|
||||
Specifies that the mock function call is expected to occur after one or more
|
||||
other calls.
|
||||
|
||||
The parameter *`expectations...`* can be up to five
|
||||
[`Expectation`](#Expectation) or [`ExpectationSet`](#ExpectationSet) objects.
|
||||
The mock function call is expected to occur after all of the given expectations.
|
||||
|
||||
For example, the following code sets the expectation that the `Describe()`
|
||||
method of `my_mock` is called only after both `InitX()` and `InitY()` have been
|
||||
called.
|
||||
|
||||
```cpp
|
||||
using ::testing::Expectation;
|
||||
...
|
||||
Expectation init_x = EXPECT_CALL(my_mock, InitX());
|
||||
Expectation init_y = EXPECT_CALL(my_mock, InitY());
|
||||
EXPECT_CALL(my_mock, Describe())
|
||||
.After(init_x, init_y);
|
||||
```
|
||||
|
||||
The `ExpectationSet` object is helpful when the number of prerequisites for an
|
||||
expectation is large or variable, for example:
|
||||
|
||||
```cpp
|
||||
using ::testing::ExpectationSet;
|
||||
...
|
||||
ExpectationSet all_inits;
|
||||
// Collect all expectations of InitElement() calls
|
||||
for (int i = 0; i < element_count; i++) {
|
||||
all_inits += EXPECT_CALL(my_mock, InitElement(i));
|
||||
}
|
||||
EXPECT_CALL(my_mock, Describe())
|
||||
.After(all_inits); // Expect Describe() call after all InitElement() calls
|
||||
```
|
||||
|
||||
The `After` clause can be used any number of times on an expectation.
|
||||
|
||||
#### WillOnce {#EXPECT_CALL.WillOnce}
|
||||
|
||||
`.WillOnce(`*`action`*`)`
|
||||
|
||||
Specifies the mock function's actual behavior when invoked, for a single
|
||||
matching function call.
|
||||
|
||||
The parameter *`action`* represents the
|
||||
[action](../gmock_for_dummies.md#actions-what-should-it-do) that the function
|
||||
call will perform. See the [Actions Reference](actions.md) for a list of
|
||||
built-in actions.
|
||||
|
||||
The use of `WillOnce` implicitly sets a cardinality on the expectation when
|
||||
`Times` is not specified. See [`Times`](#EXPECT_CALL.Times).
|
||||
|
||||
Each matching function call will perform the next action in the order declared.
|
||||
For example, the following code specifies that `my_mock.GetNumber()` is expected
|
||||
to be called exactly 3 times and will return `1`, `2`, and `3` respectively on
|
||||
the first, second, and third calls:
|
||||
|
||||
```cpp
|
||||
using ::testing::Return;
|
||||
...
|
||||
EXPECT_CALL(my_mock, GetNumber())
|
||||
.WillOnce(Return(1))
|
||||
.WillOnce(Return(2))
|
||||
.WillOnce(Return(3));
|
||||
```
|
||||
|
||||
The `WillOnce` clause can be used any number of times on an expectation. Unlike
|
||||
`WillRepeatedly`, the action fed to each `WillOnce` call will be called at most
|
||||
once, so may be a move-only type and/or have an `&&`-qualified call operator.
|
||||
|
||||
#### WillRepeatedly {#EXPECT_CALL.WillRepeatedly}
|
||||
|
||||
`.WillRepeatedly(`*`action`*`)`
|
||||
|
||||
Specifies the mock function's actual behavior when invoked, for all subsequent
|
||||
matching function calls. Takes effect after the actions specified in the
|
||||
[`WillOnce`](#EXPECT_CALL.WillOnce) clauses, if any, have been performed.
|
||||
|
||||
The parameter *`action`* represents the
|
||||
[action](../gmock_for_dummies.md#actions-what-should-it-do) that the function
|
||||
call will perform. See the [Actions Reference](actions.md) for a list of
|
||||
built-in actions.
|
||||
|
||||
The use of `WillRepeatedly` implicitly sets a cardinality on the expectation
|
||||
when `Times` is not specified. See [`Times`](#EXPECT_CALL.Times).
|
||||
|
||||
If any `WillOnce` clauses have been specified, matching function calls will
|
||||
perform those actions before the action specified by `WillRepeatedly`. See the
|
||||
following example:
|
||||
|
||||
```cpp
|
||||
using ::testing::Return;
|
||||
...
|
||||
EXPECT_CALL(my_mock, GetName())
|
||||
.WillRepeatedly(Return("John Doe")); // Return "John Doe" on all calls
|
||||
|
||||
EXPECT_CALL(my_mock, GetNumber())
|
||||
.WillOnce(Return(42)) // Return 42 on the first call
|
||||
.WillRepeatedly(Return(7)); // Return 7 on all subsequent calls
|
||||
```
|
||||
|
||||
The `WillRepeatedly` clause can be used at most once on an expectation.
|
||||
|
||||
#### RetiresOnSaturation {#EXPECT_CALL.RetiresOnSaturation}
|
||||
|
||||
`.RetiresOnSaturation()`
|
||||
|
||||
Indicates that the expectation will no longer be active after the expected
|
||||
number of matching function calls has been reached.
|
||||
|
||||
The `RetiresOnSaturation` clause is only meaningful for expectations with an
|
||||
upper-bounded cardinality. The expectation will *retire* (no longer match any
|
||||
function calls) after it has been *saturated* (the upper bound has been
|
||||
reached). See the following example:
|
||||
|
||||
```cpp
|
||||
using ::testing::_;
|
||||
using ::testing::AnyNumber;
|
||||
...
|
||||
EXPECT_CALL(my_mock, SetNumber(_)) // Expectation 1
|
||||
.Times(AnyNumber());
|
||||
EXPECT_CALL(my_mock, SetNumber(7)) // Expectation 2
|
||||
.Times(2)
|
||||
.RetiresOnSaturation();
|
||||
```
|
||||
|
||||
In the above example, the first two calls to `my_mock.SetNumber(7)` match
|
||||
expectation 2, which then becomes inactive and no longer matches any calls. A
|
||||
third call to `my_mock.SetNumber(7)` would then match expectation 1. Without
|
||||
`RetiresOnSaturation()` on expectation 2, a third call to `my_mock.SetNumber(7)`
|
||||
would match expectation 2 again, producing a failure since the limit of 2 calls
|
||||
was exceeded.
|
||||
|
||||
The `RetiresOnSaturation` clause can be used at most once on an expectation and
|
||||
must be the last clause.
|
||||
|
||||
### ON_CALL {#ON_CALL}
|
||||
|
||||
`ON_CALL(`*`mock_object`*`,`*`method_name`*`(`*`matchers...`*`))`
|
||||
|
||||
Defines what happens when the method *`method_name`* of the object
|
||||
*`mock_object`* is called with arguments that match the given matchers
|
||||
*`matchers...`*. Requires a modifier clause to specify the method's behavior.
|
||||
*Does not* set any expectations that the method will be called.
|
||||
|
||||
The parameter *`matchers...`* is a comma-separated list of
|
||||
[matchers](../gmock_for_dummies.md#matchers-what-arguments-do-we-expect) that
|
||||
correspond to each argument of the method *`method_name`*. The `ON_CALL`
|
||||
specification will apply only to calls of *`method_name`* whose arguments match
|
||||
all of the matchers. If `(`*`matchers...`*`)` is omitted, the behavior is as if
|
||||
each argument's matcher were a [wildcard matcher (`_`)](matchers.md#wildcard).
|
||||
See the [Matchers Reference](matchers.md) for a list of all built-in matchers.
|
||||
|
||||
The following chainable clauses can be used to set the method's behavior, and
|
||||
they must be used in the following order:
|
||||
|
||||
```cpp
|
||||
ON_CALL(mock_object, method_name(matchers...))
|
||||
.With(multi_argument_matcher) // Can be used at most once
|
||||
.WillByDefault(action); // Required
|
||||
```
|
||||
|
||||
See details for each modifier clause below.
|
||||
|
||||
#### With {#ON_CALL.With}
|
||||
|
||||
`.With(`*`multi_argument_matcher`*`)`
|
||||
|
||||
Restricts the specification to only mock function calls whose arguments as a
|
||||
whole match the multi-argument matcher *`multi_argument_matcher`*.
|
||||
|
||||
GoogleTest passes all of the arguments as one tuple into the matcher. The
|
||||
parameter *`multi_argument_matcher`* must thus be a matcher of type
|
||||
`Matcher<std::tuple<A1, ..., An>>`, where `A1, ..., An` are the types of the
|
||||
function arguments.
|
||||
|
||||
For example, the following code sets the default behavior when
|
||||
`my_mock.SetPosition()` is called with any two arguments, the first argument
|
||||
being less than the second:
|
||||
|
||||
```cpp
|
||||
using ::testing::_;
|
||||
using ::testing::Lt;
|
||||
using ::testing::Return;
|
||||
...
|
||||
ON_CALL(my_mock, SetPosition(_, _))
|
||||
.With(Lt())
|
||||
.WillByDefault(Return(true));
|
||||
```
|
||||
|
||||
GoogleTest provides some built-in matchers for 2-tuples, including the `Lt()`
|
||||
matcher above. See [Multi-argument Matchers](matchers.md#MultiArgMatchers).
|
||||
|
||||
The `With` clause can be used at most once with each `ON_CALL` statement.
|
||||
|
||||
#### WillByDefault {#ON_CALL.WillByDefault}
|
||||
|
||||
`.WillByDefault(`*`action`*`)`
|
||||
|
||||
Specifies the default behavior of a matching mock function call.
|
||||
|
||||
The parameter *`action`* represents the
|
||||
[action](../gmock_for_dummies.md#actions-what-should-it-do) that the function
|
||||
call will perform. See the [Actions Reference](actions.md) for a list of
|
||||
built-in actions.
|
||||
|
||||
For example, the following code specifies that by default, a call to
|
||||
`my_mock.Greet()` will return `"hello"`:
|
||||
|
||||
```cpp
|
||||
using ::testing::Return;
|
||||
...
|
||||
ON_CALL(my_mock, Greet())
|
||||
.WillByDefault(Return("hello"));
|
||||
```
|
||||
|
||||
The action specified by `WillByDefault` is superseded by the actions specified
|
||||
on a matching `EXPECT_CALL` statement, if any. See the
|
||||
[`WillOnce`](#EXPECT_CALL.WillOnce) and
|
||||
[`WillRepeatedly`](#EXPECT_CALL.WillRepeatedly) clauses of `EXPECT_CALL`.
|
||||
|
||||
The `WillByDefault` clause must be used exactly once with each `ON_CALL`
|
||||
statement.
|
||||
|
||||
## Classes {#classes}
|
||||
|
||||
GoogleTest defines the following classes for working with mocks.
|
||||
|
||||
### DefaultValue {#DefaultValue}
|
||||
|
||||
`::testing::DefaultValue<T>`
|
||||
|
||||
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). For mock functions with a return type of `T`, this default value is
|
||||
returned from function calls that do not specify an action.
|
||||
|
||||
Provides the static methods `Set()`, `SetFactory()`, and `Clear()` to manage the
|
||||
default value:
|
||||
|
||||
```cpp
|
||||
// Sets the default value to be returned. T must be copy constructible.
|
||||
DefaultValue<T>::Set(value);
|
||||
|
||||
// Sets a factory. Will be invoked on demand. T must be move constructible.
|
||||
T MakeT();
|
||||
DefaultValue<T>::SetFactory(&MakeT);
|
||||
|
||||
// Unsets the default value.
|
||||
DefaultValue<T>::Clear();
|
||||
```
|
||||
|
||||
### NiceMock {#NiceMock}
|
||||
|
||||
`::testing::NiceMock<T>`
|
||||
|
||||
Represents a mock object that suppresses warnings on
|
||||
[uninteresting calls](../gmock_cook_book.md#uninteresting-vs-unexpected). The
|
||||
template parameter `T` is any mock class, except for another `NiceMock`,
|
||||
`NaggyMock`, or `StrictMock`.
|
||||
|
||||
Usage of `NiceMock<T>` is analogous to usage of `T`. `NiceMock<T>` is a subclass
|
||||
of `T`, so it can be used wherever an object of type `T` is accepted. In
|
||||
addition, `NiceMock<T>` can be constructed with any arguments that a constructor
|
||||
of `T` accepts.
|
||||
|
||||
For example, the following code suppresses warnings on the mock `my_mock` of
|
||||
type `MockClass` if a method other than `DoSomething()` is called:
|
||||
|
||||
```cpp
|
||||
using ::testing::NiceMock;
|
||||
...
|
||||
NiceMock<MockClass> my_mock("some", "args");
|
||||
EXPECT_CALL(my_mock, DoSomething());
|
||||
... code that uses my_mock ...
|
||||
```
|
||||
|
||||
`NiceMock<T>` only works for mock methods defined using the `MOCK_METHOD` macro
|
||||
directly in the definition of class `T`. If a mock method is defined in a base
|
||||
class of `T`, a warning might still be generated.
|
||||
|
||||
`NiceMock<T>` might not work correctly if the destructor of `T` is not virtual.
|
||||
|
||||
### NaggyMock {#NaggyMock}
|
||||
|
||||
`::testing::NaggyMock<T>`
|
||||
|
||||
Represents a mock object that generates warnings on
|
||||
[uninteresting calls](../gmock_cook_book.md#uninteresting-vs-unexpected). The
|
||||
template parameter `T` is any mock class, except for another `NiceMock`,
|
||||
`NaggyMock`, or `StrictMock`.
|
||||
|
||||
Usage of `NaggyMock<T>` is analogous to usage of `T`. `NaggyMock<T>` is a
|
||||
subclass of `T`, so it can be used wherever an object of type `T` is accepted.
|
||||
In addition, `NaggyMock<T>` can be constructed with any arguments that a
|
||||
constructor of `T` accepts.
|
||||
|
||||
For example, the following code generates warnings on the mock `my_mock` of type
|
||||
`MockClass` if a method other than `DoSomething()` is called:
|
||||
|
||||
```cpp
|
||||
using ::testing::NaggyMock;
|
||||
...
|
||||
NaggyMock<MockClass> my_mock("some", "args");
|
||||
EXPECT_CALL(my_mock, DoSomething());
|
||||
... code that uses my_mock ...
|
||||
```
|
||||
|
||||
Mock objects of type `T` by default behave the same way as `NaggyMock<T>`.
|
||||
|
||||
### StrictMock {#StrictMock}
|
||||
|
||||
`::testing::StrictMock<T>`
|
||||
|
||||
Represents a mock object that generates test failures on
|
||||
[uninteresting calls](../gmock_cook_book.md#uninteresting-vs-unexpected). The
|
||||
template parameter `T` is any mock class, except for another `NiceMock`,
|
||||
`NaggyMock`, or `StrictMock`.
|
||||
|
||||
Usage of `StrictMock<T>` is analogous to usage of `T`. `StrictMock<T>` is a
|
||||
subclass of `T`, so it can be used wherever an object of type `T` is accepted.
|
||||
In addition, `StrictMock<T>` can be constructed with any arguments that a
|
||||
constructor of `T` accepts.
|
||||
|
||||
For example, the following code generates a test failure on the mock `my_mock`
|
||||
of type `MockClass` if a method other than `DoSomething()` is called:
|
||||
|
||||
```cpp
|
||||
using ::testing::StrictMock;
|
||||
...
|
||||
StrictMock<MockClass> my_mock("some", "args");
|
||||
EXPECT_CALL(my_mock, DoSomething());
|
||||
... code that uses my_mock ...
|
||||
```
|
||||
|
||||
`StrictMock<T>` only works for mock methods defined using the `MOCK_METHOD`
|
||||
macro directly in the definition of class `T`. If a mock method is defined in a
|
||||
base class of `T`, a failure might not be generated.
|
||||
|
||||
`StrictMock<T>` might not work correctly if the destructor of `T` is not
|
||||
virtual.
|
||||
|
||||
### Sequence {#Sequence}
|
||||
|
||||
`::testing::Sequence`
|
||||
|
||||
Represents a chronological sequence of expectations. See the
|
||||
[`InSequence`](#EXPECT_CALL.InSequence) clause of `EXPECT_CALL` for usage.
|
||||
|
||||
### InSequence {#InSequence}
|
||||
|
||||
`::testing::InSequence`
|
||||
|
||||
An object of this type causes all expectations encountered in its scope to be
|
||||
put in an anonymous sequence.
|
||||
|
||||
This allows more convenient expression of multiple expectations in a single
|
||||
sequence:
|
||||
|
||||
```cpp
|
||||
using ::testing::InSequence;
|
||||
{
|
||||
InSequence seq;
|
||||
|
||||
// The following are expected to occur in the order declared.
|
||||
EXPECT_CALL(...);
|
||||
EXPECT_CALL(...);
|
||||
...
|
||||
EXPECT_CALL(...);
|
||||
}
|
||||
```
|
||||
|
||||
The name of the `InSequence` object does not matter.
|
||||
|
||||
### Expectation {#Expectation}
|
||||
|
||||
`::testing::Expectation`
|
||||
|
||||
Represents a mock function call expectation as created by
|
||||
[`EXPECT_CALL`](#EXPECT_CALL):
|
||||
|
||||
```cpp
|
||||
using ::testing::Expectation;
|
||||
Expectation my_expectation = EXPECT_CALL(...);
|
||||
```
|
||||
|
||||
Useful for specifying sequences of expectations; see the
|
||||
[`After`](#EXPECT_CALL.After) clause of `EXPECT_CALL`.
|
||||
|
||||
### ExpectationSet {#ExpectationSet}
|
||||
|
||||
`::testing::ExpectationSet`
|
||||
|
||||
Represents a set of mock function call expectations.
|
||||
|
||||
Use the `+=` operator to add [`Expectation`](#Expectation) objects to the set:
|
||||
|
||||
```cpp
|
||||
using ::testing::ExpectationSet;
|
||||
ExpectationSet my_expectations;
|
||||
my_expectations += EXPECT_CALL(...);
|
||||
```
|
||||
|
||||
Useful for specifying sequences of expectations; see the
|
||||
[`After`](#EXPECT_CALL.After) clause of `EXPECT_CALL`.
|
||||
+1550
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -1,7 +1,7 @@
|
||||
# Googletest Samples {#samples}
|
||||
# Googletest Samples
|
||||
|
||||
If you're like us, you'd like to look at
|
||||
[googletest samples.](https://github.com/google/googletest/tree/master/googletest/samples)
|
||||
[googletest samples.](https://github.com/google/googletest/blob/main/googletest/samples)
|
||||
The sample directory has a number of well-commented samples showing how to use a
|
||||
variety of googletest features.
|
||||
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
"""Provides a fake @fuchsia_sdk implementation that's used when the real one isn't available.
|
||||
|
||||
GoogleTest can be used with the [Fuchsia](https://fuchsia.dev/) SDK. However,
|
||||
because the Fuchsia SDK does not yet support bzlmod, GoogleTest's `MODULE.bazel`
|
||||
file by default provides a "fake" Fuchsia SDK.
|
||||
|
||||
To override this and use the real Fuchsia SDK, you can add the following to your
|
||||
project's `MODULE.bazel` file:
|
||||
|
||||
fake_fuchsia_sdk_extension =
|
||||
use_extension("@com_google_googletest//:fake_fuchsia_sdk.bzl", "fuchsia_sdk")
|
||||
override_repo(fake_fuchsia_sdk_extension, "fuchsia_sdk")
|
||||
|
||||
NOTE: The `override_repo` built-in is only available in Bazel 8.0 and higher.
|
||||
|
||||
See https://github.com/google/googletest/issues/4472 for more details of why the
|
||||
fake Fuchsia SDK is needed.
|
||||
"""
|
||||
|
||||
def _fake_fuchsia_sdk_impl(repo_ctx):
|
||||
for stub_target in repo_ctx.attr._stub_build_targets:
|
||||
stub_package = stub_target
|
||||
stub_target_name = stub_target.split("/")[-1]
|
||||
repo_ctx.file("%s/BUILD.bazel" % stub_package, """
|
||||
filegroup(
|
||||
name = "%s",
|
||||
)
|
||||
""" % stub_target_name)
|
||||
|
||||
fake_fuchsia_sdk = repository_rule(
|
||||
doc = "Used to create a fake @fuchsia_sdk repository with stub build targets.",
|
||||
implementation = _fake_fuchsia_sdk_impl,
|
||||
attrs = {
|
||||
"_stub_build_targets": attr.string_list(
|
||||
doc = "The stub build targets to initialize.",
|
||||
default = [
|
||||
"pkg/fdio",
|
||||
"pkg/syslog",
|
||||
"pkg/zx",
|
||||
],
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
_create_fake = tag_class()
|
||||
|
||||
def _fuchsia_sdk_impl(module_ctx):
|
||||
create_fake_sdk = False
|
||||
for mod in module_ctx.modules:
|
||||
for _ in mod.tags.create_fake:
|
||||
create_fake_sdk = True
|
||||
|
||||
if create_fake_sdk:
|
||||
fake_fuchsia_sdk(name = "fuchsia_sdk")
|
||||
|
||||
return module_ctx.extension_metadata(reproducible = True)
|
||||
|
||||
fuchsia_sdk = module_extension(
|
||||
implementation = _fuchsia_sdk_impl,
|
||||
tag_classes = {"create_fake": _create_fake},
|
||||
)
|
||||
+31
-54
@@ -5,7 +5,7 @@
|
||||
# CMake build script for Google Mock.
|
||||
#
|
||||
# To run the tests for Google Mock itself on Linux, use 'make test' or
|
||||
# ctest. You can select which tests to run using 'ctest -R regex'.
|
||||
# ctest. You can select which tests to run using 'ctest -R regex'.
|
||||
# For more options, run 'ctest --help'.
|
||||
|
||||
option(gmock_build_tests "Build all of Google Mock's own tests." OFF)
|
||||
@@ -36,20 +36,15 @@ endif()
|
||||
# as ${gmock_SOURCE_DIR} and to the root binary directory as
|
||||
# ${gmock_BINARY_DIR}.
|
||||
# Language "C" is required for find_package(Threads).
|
||||
if (CMAKE_VERSION VERSION_LESS 3.0)
|
||||
project(gmock CXX C)
|
||||
else()
|
||||
cmake_policy(SET CMP0048 NEW)
|
||||
project(gmock VERSION ${GOOGLETEST_VERSION} LANGUAGES CXX C)
|
||||
endif()
|
||||
cmake_minimum_required(VERSION 2.6.4)
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
project(gmock VERSION ${GOOGLETEST_VERSION} LANGUAGES CXX C)
|
||||
|
||||
if (COMMAND set_up_hermetic_build)
|
||||
set_up_hermetic_build()
|
||||
endif()
|
||||
|
||||
# Instructs CMake to process Google Test's CMakeLists.txt and add its
|
||||
# targets to the current scope. We are placing Google Test's binary
|
||||
# targets to the current scope. We are placing Google Test's binary
|
||||
# directory in a subdirectory of our own as VC compilation may break
|
||||
# if they are the same (the default).
|
||||
add_subdirectory("${gtest_dir}" "${gmock_BINARY_DIR}/${gtest_dir}")
|
||||
@@ -65,25 +60,26 @@ else()
|
||||
endif()
|
||||
|
||||
# Although Google Test's CMakeLists.txt calls this function, the
|
||||
# changes there don't affect the current scope. Therefore we have to
|
||||
# changes there don't affect the current scope. Therefore we have to
|
||||
# call it again here.
|
||||
config_compiler_and_linker() # from ${gtest_dir}/cmake/internal_utils.cmake
|
||||
config_compiler_and_linker() # from ${gtest_dir}/cmake/internal_utils.cmake
|
||||
|
||||
# Adds Google Mock's and Google Test's header directories to the search path.
|
||||
# Get Google Test's include dirs from the target, gtest_SOURCE_DIR is broken
|
||||
# when using fetch-content with the name "GTest".
|
||||
get_target_property(gtest_include_dirs gtest INCLUDE_DIRECTORIES)
|
||||
set(gmock_build_include_dirs
|
||||
"${gmock_SOURCE_DIR}/include"
|
||||
"${gmock_SOURCE_DIR}"
|
||||
"${gtest_SOURCE_DIR}/include"
|
||||
# This directory is needed to build directly from Google Test sources.
|
||||
"${gtest_SOURCE_DIR}")
|
||||
"${gtest_include_dirs}")
|
||||
include_directories(${gmock_build_include_dirs})
|
||||
|
||||
########################################################################
|
||||
#
|
||||
# Defines the gmock & gmock_main libraries. User tests should link
|
||||
# Defines the gmock & gmock_main libraries. User tests should link
|
||||
# with one of them.
|
||||
|
||||
# Google Mock libraries. We build them using more strict warnings than what
|
||||
# Google Mock libraries. We build them using more strict warnings than what
|
||||
# are used for other targets, to ensure that Google Mock can be compiled by
|
||||
# a user aggressive about warnings.
|
||||
if (MSVC)
|
||||
@@ -100,24 +96,23 @@ if (MSVC)
|
||||
else()
|
||||
cxx_library(gmock "${cxx_strict}" src/gmock-all.cc)
|
||||
target_link_libraries(gmock PUBLIC gtest)
|
||||
set_target_properties(gmock PROPERTIES VERSION ${GOOGLETEST_VERSION})
|
||||
cxx_library(gmock_main "${cxx_strict}" src/gmock_main.cc)
|
||||
target_link_libraries(gmock_main PUBLIC gmock)
|
||||
set_target_properties(gmock_main PROPERTIES VERSION ${GOOGLETEST_VERSION})
|
||||
endif()
|
||||
# If the CMake version supports it, attach header directory information
|
||||
# to the targets for when we are part of a parent build (ie being pulled
|
||||
# in via add_subdirectory() rather than being a standalone build).
|
||||
if (DEFINED CMAKE_VERSION AND NOT "${CMAKE_VERSION}" VERSION_LESS "2.8.11")
|
||||
target_include_directories(gmock SYSTEM INTERFACE
|
||||
"$<BUILD_INTERFACE:${gmock_build_include_dirs}>"
|
||||
"$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/${CMAKE_INSTALL_INCLUDEDIR}>")
|
||||
target_include_directories(gmock_main SYSTEM INTERFACE
|
||||
"$<BUILD_INTERFACE:${gmock_build_include_dirs}>"
|
||||
"$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/${CMAKE_INSTALL_INCLUDEDIR}>")
|
||||
endif()
|
||||
|
||||
string(REPLACE ";" "$<SEMICOLON>" dirs "${gmock_build_include_dirs}")
|
||||
target_include_directories(gmock SYSTEM INTERFACE
|
||||
"$<BUILD_INTERFACE:${dirs}>"
|
||||
"$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/${CMAKE_INSTALL_INCLUDEDIR}>")
|
||||
target_include_directories(gmock_main SYSTEM INTERFACE
|
||||
"$<BUILD_INTERFACE:${dirs}>"
|
||||
"$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/${CMAKE_INSTALL_INCLUDEDIR}>")
|
||||
|
||||
########################################################################
|
||||
#
|
||||
# Install rules
|
||||
# Install rules.
|
||||
install_project(gmock gmock_main)
|
||||
|
||||
########################################################################
|
||||
@@ -127,8 +122,8 @@ install_project(gmock gmock_main)
|
||||
# You can skip this section if you aren't interested in testing
|
||||
# Google Mock itself.
|
||||
#
|
||||
# The tests are not built by default. To build them, set the
|
||||
# gmock_build_tests option to ON. You can do it by running ccmake
|
||||
# The tests are not built by default. To build them, set the
|
||||
# gmock_build_tests option to ON. You can do it by running ccmake
|
||||
# or specifying the -Dgmock_build_tests=ON flag when running cmake.
|
||||
|
||||
if (gmock_build_tests)
|
||||
@@ -136,26 +131,8 @@ if (gmock_build_tests)
|
||||
# 'make test' or ctest.
|
||||
enable_testing()
|
||||
|
||||
if (WIN32)
|
||||
file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/RunTest.ps1"
|
||||
CONTENT
|
||||
"$project_bin = \"${CMAKE_BINARY_DIR}/bin/$<CONFIG>\"
|
||||
$env:Path = \"$project_bin;$env:Path\"
|
||||
& $args")
|
||||
elseif (MINGW OR CYGWIN)
|
||||
file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/RunTest.ps1"
|
||||
CONTENT
|
||||
"$project_bin = (cygpath --windows ${CMAKE_BINARY_DIR}/bin)
|
||||
$env:Path = \"$project_bin;$env:Path\"
|
||||
& $args")
|
||||
endif()
|
||||
|
||||
if (MINGW OR CYGWIN)
|
||||
if (CMAKE_VERSION VERSION_LESS "2.8.12")
|
||||
add_compile_options("-Wa,-mbig-obj")
|
||||
else()
|
||||
add_definitions("-Wa,-mbig-obj")
|
||||
endif()
|
||||
add_compile_options("-Wa,-mbig-obj")
|
||||
endif()
|
||||
|
||||
############################################################
|
||||
@@ -165,11 +142,11 @@ $env:Path = \"$project_bin;$env:Path\"
|
||||
cxx_test(gmock-cardinalities_test gmock_main)
|
||||
cxx_test(gmock_ex_test gmock_main)
|
||||
cxx_test(gmock-function-mocker_test gmock_main)
|
||||
cxx_test(gmock-generated-actions_test gmock_main)
|
||||
cxx_test(gmock-generated-function-mockers_test gmock_main)
|
||||
cxx_test(gmock-generated-matchers_test gmock_main)
|
||||
cxx_test(gmock-internal-utils_test gmock_main)
|
||||
cxx_test(gmock-matchers_test gmock_main)
|
||||
cxx_test(gmock-matchers-arithmetic_test gmock_main)
|
||||
cxx_test(gmock-matchers-comparisons_test gmock_main)
|
||||
cxx_test(gmock-matchers-containers_test gmock_main)
|
||||
cxx_test(gmock-matchers-misc_test gmock_main)
|
||||
cxx_test(gmock-more-actions_test gmock_main)
|
||||
cxx_test(gmock-nice-strict_test gmock_main)
|
||||
cxx_test(gmock-port_test gmock_main)
|
||||
@@ -211,7 +188,7 @@ $env:Path = \"$project_bin;$env:Path\"
|
||||
cxx_shared_library(shared_gmock_main "${cxx_default}"
|
||||
"${gtest_dir}/src/gtest-all.cc" src/gmock-all.cc src/gmock_main.cc)
|
||||
|
||||
# Tests that a binary can be built with Google Mock as a shared library. On
|
||||
# Tests that a binary can be built with Google Mock as a shared library. On
|
||||
# some system configurations, it may not possible to run the binary without
|
||||
# knowing more details about the system configurations. We do not try to run
|
||||
# this binary. To get a more robust shared library coverage, configure with
|
||||
|
||||
-40
@@ -1,40 +0,0 @@
|
||||
# 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 <tsuna@google.com>
|
||||
Bogdan Piloca <boo@google.com>
|
||||
Chandler Carruth <chandlerc@google.com>
|
||||
Dave MacLachlan <dmaclach@gmail.com>
|
||||
David Anderson <danderson@google.com>
|
||||
Dean Sturtevant
|
||||
Gene Volovich <gv@cite.com>
|
||||
Hal Burch <gmock@hburch.com>
|
||||
Jeffrey Yasskin <jyasskin@google.com>
|
||||
Jim Keller <jimkeller@google.com>
|
||||
Joe Walnes <joe@truemesh.com>
|
||||
Jon Wray <jwray@google.com>
|
||||
Keir Mierle <mierle@gmail.com>
|
||||
Keith Ray <keith.ray@gmail.com>
|
||||
Kostya Serebryany <kcc@google.com>
|
||||
Lev Makhlis
|
||||
Manuel Klimek <klimek@google.com>
|
||||
Mario Tanev <radix@google.com>
|
||||
Mark Paskin
|
||||
Markus Heule <markus.heule@gmail.com>
|
||||
Matthew Simmons <simmonmt@acm.org>
|
||||
Mike Bland <mbland@google.com>
|
||||
Neal Norwitz <nnorwitz@gmail.com>
|
||||
Nermin Ozkiranartli <nermin@google.com>
|
||||
Owen Carlsen <ocarlsen@google.com>
|
||||
Paneendra Ba <paneendra@google.com>
|
||||
Paul Menage <menage@google.com>
|
||||
Piotr Kaminski <piotrk@google.com>
|
||||
Russ Rufer <russ@pentad.com>
|
||||
Sverre Sundsdal <sundsdal@gmail.com>
|
||||
Takeshi Yoshino <tyoshino@google.com>
|
||||
Vadim Berman <vadimb@google.com>
|
||||
Vlad Losev <vladl@google.com>
|
||||
Wolfgang Klier <wklier@google.com>
|
||||
Zhanyong Wan <wan@google.com>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user