Merge branch 'master' into kimkulling/create_skeleton_data_issue_4015

kimkulling/create_skeleton_data_issue_4015
Kim Kulling 2022-06-02 21:47:29 +02:00 committed by GitHub
commit 39a4627519
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
227 changed files with 11253 additions and 39353 deletions

View File

@ -84,23 +84,28 @@ Besides the toolchain, compilation should be the same as for Linux / Unix.
### CMake build options ### CMake build options
The cmake-build-environment provides options to configure the build. The following options can be used: The cmake-build-environment provides options to configure the build. The following options can be used:
- **BUILD_SHARED_LIBS ( default ON )**: Generation of shared libs ( dll for windows, so for Linux ). Set this to OFF to get a static lib. - **ASSIMP_HUNTER_ENABLED (default OFF)**: Enable Hunter package manager support.
- **BUILD_FRAMEWORK ( default OFF, MacOnly)**: Build package as Mac OS X Framework bundle - **BUILD_SHARED_LIBS (default ON)**: Generation of shared libs (dll for windows, so for Linux). Set this to OFF to get a static lib.
- **ASSIMP_DOUBLE_PRECISION( default OFF )**: All data will be stored as double values. - **ASSIMP_BUILD_FRAMEWORK (default OFF, MacOnly)**: Build package as Mac OS X Framework bundle.
- **ASSIMP_OPT_BUILD_PACKAGES ( default OFF)**: Set to ON to generate CPack configuration files and packaging targets - **ASSIMP_DOUBLE_PRECISION (default OFF)**: All data will be stored as double values.
- **ASSIMP_ANDROID_JNIIOSYSTEM ( default OFF )**: Android JNI IOSystem support is active - **ASSIMP_OPT_BUILD_PACKAGES (default OFF)**: Set to ON to generate CPack configuration files and packaging targets.
- **ASSIMP_NO_EXPORT ( default OFF )**: Disable Assimp's export functionality - **ASSIMP_ANDROID_JNIIOSYSTEM (default OFF)**: Android JNI IOSystem support is active.
- **ASSIMP_BUILD_ZLIB ( default OFF )**: Build your own zlib - **ASSIMP_NO_EXPORT (default OFF)**: Disable Assimp's export functionality.
- **ASSIMP_BUILD_ASSIMP_TOOLS ( default ON )**: If the supplementary tools for Assimp are built in addition to the library. - **ASSIMP_BUILD_ZLIB (default OFF)**: Build our own zlib.
- **ASSIMP_BUILD_SAMPLES ( default OFF )**: If the official samples are built as well (needs Glut). - **ASSIMP_BUILD_ALL_EXPORTERS_BY_DEFAULT (default ON)**: Build Assimp with all exporter senabled.
- **ASSIMP_BUILD_TESTS ( default ON )**: If the test suite for Assimp is built in addition to the library. - **ASSIMP_BUILD_ALL_IMPORTERS_BY_DEFAULT (default ON)**: Build Assimp with all importer senabled.
- **ASSIMP_COVERALLS ( default OFF )**: Enable this to measure test coverage. - **ASSIMP_BUILD_ASSIMP_TOOLS (default ON)**: If the supplementary tools for Assimp are built in addition to the library.
- **ASSIMP_ERROR_MAX( default OFF)**: Enable all warnings. - **ASSIMP_BUILD_SAMPLES (default OFF)**: If the official samples are built as well (needs Glut).
- **ASSIMP_WERROR( default OFF )**: Treat warnings as errors. - **ASSIMP_BUILD_TESTS (default ON)**: If the test suite for Assimp is built in addition to the library.
- **ASSIMP_ASAN ( default OFF )**: Enable AddressSanitizer. - **ASSIMP_COVERALLS (default OFF)**: Enable this to measure test coverage.
- **ASSIMP_UBSAN ( default OFF )**: Enable Undefined Behavior sanitizer. - **ASSIMP_INSTALL (default ON)**: Install Assimp library. Disable this if you want to use Assimp as a submodule.
- **SYSTEM_IRRXML ( default OFF )**: Use system installed Irrlicht/IrrXML library. - **ASSIMP_WARNINGS_AS_ERRORS (default ON)**: Treat all warnings as errors.
- **BUILD_DOCS ( default OFF )**: Build documentation using Doxygen. - **ASSIMP_ASAN (default OFF)**: Enable AddressSanitizer.
- **INJECT_DEBUG_POSTFIX( default ON )**: Inject debug postfix in .a/.so lib names - **ASSIMP_UBSAN (default OFF)**: Enable Undefined Behavior sanitizer.
- **IGNORE_GIT_HASH ( default OFF )**: Don't call git to get the hash. - **ASSIMP_BUILD_DOCS (default OFF)**: Build documentation using Doxygen. OBSOLETE, see https://github.com/assimp/assimp-docs
- **ASSIMP_INSTALL_PDB ( default ON )**: Install MSVC debug files. - **ASSIMP_INJECT_DEBUG_POSTFIX (default ON)**: Inject debug postfix in .a/.so/.lib/.dll lib names
- **ASSIMP_IGNORE_GIT_HASH (default OFF)**: Don't call git to get the hash.
- **ASSIMP_INSTALL_PDB (default ON)**: Install MSVC debug files.
- **USE_STATIC_CRT (default OFF)**: Link against the static MSVC runtime libraries.
- **ASSIMP_BUILD_DRACO (default OFF)**: Build Draco libraries. Primarily for glTF.
- **ASSIMP_BUILD_ASSIMP_VIEW (default ON, if DirectX found, OFF otherwise)**: Build Assimp view tool (requires DirectX).

View File

@ -108,9 +108,9 @@ OPTION( ASSIMP_INSTALL
"Disable this if you want to use assimp as a submodule." "Disable this if you want to use assimp as a submodule."
ON ON
) )
OPTION ( ASSIMP_ERROR_MAX OPTION ( ASSIMP_WARNINGS_AS_ERRORS
"Enable all warnings." "Treat all warnings as errors."
OFF ON
) )
OPTION ( ASSIMP_ASAN OPTION ( ASSIMP_ASAN
"Enable AddressSanitizer." "Enable AddressSanitizer."
@ -139,10 +139,6 @@ IF (WIN32)
ADD_DEFINITIONS( -DWIN32_LEAN_AND_MEAN ) ADD_DEFINITIONS( -DWIN32_LEAN_AND_MEAN )
IF(MSVC) IF(MSVC)
OPTION (ASSIMP_BUILD_ASSIMP_VIEW
"If the Assimp view tool is built. (requires DirectX)"
OFF )
OPTION( ASSIMP_INSTALL_PDB OPTION( ASSIMP_INSTALL_PDB
"Install MSVC debug files." "Install MSVC debug files."
ON ) ON )
@ -150,6 +146,21 @@ IF (WIN32)
# Multibyte character set is deprecated since at least MSVC2015 (possibly earlier) # Multibyte character set is deprecated since at least MSVC2015 (possibly earlier)
ADD_DEFINITIONS( -DUNICODE -D_UNICODE ) ADD_DEFINITIONS( -DUNICODE -D_UNICODE )
ENDIF() ENDIF()
# Link statically against c/c++ lib to avoid missing redistriburable such as
# "VCRUNTIME140.dll not found. Try reinstalling the app.", but give users
# a choice to opt for the shared runtime if they want.
option(USE_STATIC_CRT "Link against the static runtime libraries." OFF)
# The CMAKE_CXX_FLAGS vars can be overriden by some Visual Studio generators, so we use an alternative
# global method here:
if (${USE_STATIC_CRT})
add_compile_options(
$<$<CONFIG:>:/MT>
$<$<CONFIG:Debug>:/MTd>
$<$<CONFIG:Release>:/MT>
)
endif()
ENDIF() ENDIF()
ENDIF() ENDIF()
@ -169,6 +180,7 @@ ENDIF()
IF(NOT BUILD_SHARED_LIBS) IF(NOT BUILD_SHARED_LIBS)
MESSAGE(STATUS "Shared libraries disabled") MESSAGE(STATUS "Shared libraries disabled")
SET(LINK_SEARCH_START_STATIC TRUE) SET(LINK_SEARCH_START_STATIC TRUE)
SET(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_STATIC_LIBRARY_SUFFIX} ${CMAKE_FIND_LIBRARY_SUFFIXES})
ELSE() ELSE()
MESSAGE(STATUS "Shared libraries enabled") MESSAGE(STATUS "Shared libraries enabled")
ENDIF() ENDIF()
@ -216,16 +228,6 @@ IF(ASSIMP_DOUBLE_PRECISION)
ADD_DEFINITIONS(-DASSIMP_DOUBLE_PRECISION) ADD_DEFINITIONS(-DASSIMP_DOUBLE_PRECISION)
ENDIF() ENDIF()
CONFIGURE_FILE(
${CMAKE_CURRENT_LIST_DIR}/revision.h.in
${CMAKE_CURRENT_BINARY_DIR}/revision.h
)
CONFIGURE_FILE(
${CMAKE_CURRENT_LIST_DIR}/include/assimp/config.h.in
${CMAKE_CURRENT_BINARY_DIR}/include/assimp/config.h
)
INCLUDE_DIRECTORIES( BEFORE INCLUDE_DIRECTORIES( BEFORE
./ ./
code/ code/
@ -322,16 +324,6 @@ IF (ASSIMP_COVERALLS)
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage") SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage")
ENDIF() ENDIF()
IF (ASSIMP_ERROR_MAX)
MESSAGE(STATUS "Turning on all warnings")
IF (MSVC)
ADD_COMPILE_OPTIONS(/W4) # NB: there is a /Wall option, pedantic mode
ELSE()
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
ENDIF()
ENDIF()
IF (ASSIMP_ASAN) IF (ASSIMP_ASAN)
MESSAGE(STATUS "AddressSanitizer enabled") MESSAGE(STATUS "AddressSanitizer enabled")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address") SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address")
@ -692,10 +684,12 @@ ENDIF()
# Main assimp code # Main assimp code
ADD_SUBDIRECTORY( code/ ) ADD_SUBDIRECTORY( code/ )
IF ( ASSIMP_BUILD_ASSIMP_TOOLS ) IF ( ASSIMP_BUILD_ASSIMP_TOOLS )
# The viewer for windows only # The viewer for windows only
IF (WIN32) IF (WIN32)
OPTION ( ASSIMP_BUILD_ASSIMP_VIEW "If the Assimp view tool is built. (requires DirectX)" OFF ) FIND_PACKAGE(DirectX)
OPTION ( ASSIMP_BUILD_ASSIMP_VIEW "If the Assimp view tool is built. (requires DirectX)" ${DirectX_FOUND} )
IF ( ASSIMP_BUILD_ASSIMP_VIEW ) IF ( ASSIMP_BUILD_ASSIMP_VIEW )
ADD_SUBDIRECTORY( tools/assimp_view/ ) ADD_SUBDIRECTORY( tools/assimp_view/ )
ENDIF () ENDIF ()
@ -720,12 +714,22 @@ IF ( ASSIMP_BUILD_TESTS )
ADD_SUBDIRECTORY( test/ ) ADD_SUBDIRECTORY( test/ )
ENDIF () ENDIF ()
# Generate a pkg-config .pc for the Assimp library. # Generate a pkg-config .pc, revision.h, and config.h for the Assimp library.
CONFIGURE_FILE( "${PROJECT_SOURCE_DIR}/assimp.pc.in" "${PROJECT_BINARY_DIR}/assimp.pc" @ONLY ) CONFIGURE_FILE( "${PROJECT_SOURCE_DIR}/assimp.pc.in" "${PROJECT_BINARY_DIR}/assimp.pc" @ONLY )
IF ( ASSIMP_INSTALL ) IF ( ASSIMP_INSTALL )
INSTALL( FILES "${PROJECT_BINARY_DIR}/assimp.pc" DESTINATION ${ASSIMP_LIB_INSTALL_DIR}/pkgconfig/ COMPONENT ${LIBASSIMP-DEV_COMPONENT}) INSTALL( FILES "${PROJECT_BINARY_DIR}/assimp.pc" DESTINATION ${ASSIMP_LIB_INSTALL_DIR}/pkgconfig/ COMPONENT ${LIBASSIMP-DEV_COMPONENT})
ENDIF() ENDIF()
CONFIGURE_FILE(
${CMAKE_CURRENT_LIST_DIR}/revision.h.in
${CMAKE_CURRENT_BINARY_DIR}/revision.h
)
CONFIGURE_FILE(
${CMAKE_CURRENT_LIST_DIR}/include/assimp/config.h.in
${CMAKE_CURRENT_BINARY_DIR}/include/assimp/config.h
)
IF ( ASSIMP_INSTALL ) IF ( ASSIMP_INSTALL )
IF(CMAKE_CPACK_COMMAND AND UNIX AND ASSIMP_OPT_BUILD_PACKAGES) IF(CMAKE_CPACK_COMMAND AND UNIX AND ASSIMP_OPT_BUILD_PACKAGES)
# Packing information # Packing information

1
README
View File

@ -1 +0,0 @@
See Readme.md

View File

@ -9,9 +9,11 @@ A library to import and export various 3d-model-formats including scene-post-pro
src="https://scan.coverity.com/projects/5607/badge.svg"/> src="https://scan.coverity.com/projects/5607/badge.svg"/>
</a> </a>
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/9973693b7bdd4543b07084d5d9cf4745)](https://www.codacy.com/gh/assimp/assimp/dashboard?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=assimp/assimp&amp;utm_campaign=Badge_Grade) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/9973693b7bdd4543b07084d5d9cf4745)](https://www.codacy.com/gh/assimp/assimp/dashboard?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=assimp/assimp&amp;utm_campaign=Badge_Grade)
[![Coverage Status](https://coveralls.io/repos/github/assimp/assimp/badge.svg?branch=master)](https://coveralls.io/github/assimp/assimp?branch=master) [![Coverage Status](https://coveralls.io/repos/github/assimp/assimp/badge.svg?branch=master)](https://coveralls.io/github/assimp/assimp?branch=master)
[![Join the chat at https://gitter.im/assimp/assimp](https://badges.gitter.im/assimp/assimp.svg)](https://gitter.im/assimp/assimp?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Join the chat at https://gitter.im/assimp/assimp](https://badges.gitter.im/assimp/assimp.svg)](https://gitter.im/assimp/assimp?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/assimp/assimp.svg)](http://isitmaintained.com/project/assimp/assimp "Average time to resolve an issue") [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/assimp/assimp.svg)](http://isitmaintained.com/project/assimp/assimp "Average time to resolve an issue")
[![Percentage of issues still open](http://isitmaintained.com/badge/open/assimp/assimp.svg)](http://isitmaintained.com/project/assimp/assimp "Percentage of issues still open")
[![Total alerts](https://img.shields.io/lgtm/alerts/g/assimp/assimp.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/assimp/assimp/alerts/) [![Total alerts](https://img.shields.io/lgtm/alerts/g/assimp/assimp.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/assimp/assimp/alerts/)
<br> <br>
@ -58,6 +60,7 @@ Open Asset Import Library is implemented in C++. The directory structure looks l
/code Source code /code Source code
/contrib Third-party libraries /contrib Third-party libraries
/doc Documentation (doxysource and pre-compiled docs) /doc Documentation (doxysource and pre-compiled docs)
/fuzz Contains the test-code for the Google-Fuzzer project
/include Public header C and C++ header files /include Public header C and C++ header files
/scripts Scripts used to generate the loading code for some formats /scripts Scripts used to generate the loading code for some formats
/port Ports to other languages and scripts to maintain those. /port Ports to other languages and scripts to maintain those.

View File

@ -55,7 +55,7 @@ if(WIN32) # The only platform it makes sense to check for DirectX SDK
endif(CMAKE_CL_64) endif(CMAKE_CL_64)
find_library(DirectX_LIBRARY NAMES d3d9 HINTS ${DirectX_LIB_SEARCH_PATH} PATH_SUFFIXES ${DirectX_LIBPATH_SUFFIX}) find_library(DirectX_LIBRARY NAMES d3d9 HINTS ${DirectX_LIB_SEARCH_PATH} PATH_SUFFIXES ${DirectX_LIBPATH_SUFFIX})
find_library(DirectX_D3DX9_LIBRARY NAMES d3dx9 HINTS ${DirectX_LIB_SEARCH_PATH} PATH_SUFFIXES ${DirectX_LIBPATH_SUFFIX}) find_library(DirectX_D3DX9_LIBRARY NAMES d3dx9 HINTS ${DirectX_LIB_SEARCH_PATH} PATH_SUFFIXES ${DirectX_LIBPATH_SUFFIX})
find_library(DirectX_DXERR_LIBRARY NAMES DxErr HINTS ${DirectX_LIB_SEARCH_PATH} PATH_SUFFIXES ${DirectX_LIBPATH_SUFFIX}) find_library(DirectX_DXERR_LIBRARY NAMES DxErr DxErr9 HINTS ${DirectX_LIB_SEARCH_PATH} PATH_SUFFIXES ${DirectX_LIBPATH_SUFFIX})
find_library(DirectX_DXGUID_LIBRARY NAMES dxguid HINTS ${DirectX_LIB_SEARCH_PATH} PATH_SUFFIXES ${DirectX_LIBPATH_SUFFIX}) find_library(DirectX_DXGUID_LIBRARY NAMES dxguid HINTS ${DirectX_LIB_SEARCH_PATH} PATH_SUFFIXES ${DirectX_LIBPATH_SUFFIX})

View File

@ -106,7 +106,7 @@ bool getNodeAttribute(const XmlNode &node, const std::string &attribute, int &va
return false; return false;
} }
aiMatrix4x4 parseTransformMatrix(std::string matrixStr) { aiMatrix4x4 parseTransformMatrix(const std::string& matrixStr) {
// split the string // split the string
std::vector<float> numbers; std::vector<float> numbers;
std::string currentNumber; std::string currentNumber;

View File

@ -646,10 +646,13 @@ void Parser::ParseLV2MaterialBlock(ASE::Material &mat) {
} }
// get a reference to the material // get a reference to the material
Material &sMat = mat.avSubMaterials[iIndex]; if (iIndex < mat.avSubMaterials.size()) {
Material &sMat = mat.avSubMaterials[iIndex];
// parse the material block
ParseLV2MaterialBlock(sMat);
}
// parse the material block
ParseLV2MaterialBlock(sMat);
continue; continue;
} }
} }

View File

@ -365,7 +365,7 @@ static void WriteDump(const char *pFile, const char *cmd, const aiScene *scene,
ioprintf(io, "\t\t\t<MatProperty key=\"%s\" \n\t\t\ttype=\"%s\" tex_usage=\"%s\" tex_index=\"%u\"", ioprintf(io, "\t\t\t<MatProperty key=\"%s\" \n\t\t\ttype=\"%s\" tex_usage=\"%s\" tex_index=\"%u\"",
prop->mKey.data, sz, prop->mKey.data, sz,
::TextureTypeToString((aiTextureType)prop->mSemantic), prop->mIndex); ::aiTextureTypeToString((aiTextureType)prop->mSemantic), prop->mIndex);
if (prop->mType == aiPTI_Float) { if (prop->mType == aiPTI_Float) {
ioprintf(io, " size=\"%i\">\n\t\t\t\t", ioprintf(io, " size=\"%i\">\n\t\t\t\t",

View File

@ -281,7 +281,7 @@ void BlenderImporter::ExtractScene(Scene &out, const FileDatabase &file) {
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void BlenderImporter::ParseSubCollection(const Blender::Scene &in, aiNode *root, std::shared_ptr<Collection> collection, ConversionData &conv_data) { void BlenderImporter::ParseSubCollection(const Blender::Scene &in, aiNode *root, const std::shared_ptr<Collection>& collection, ConversionData &conv_data) {
std::deque<Object *> root_objects; std::deque<Object *> root_objects;
// Count number of objects // Count number of objects

View File

@ -117,7 +117,7 @@ protected:
void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) override; void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) override;
void ParseBlendFile(Blender::FileDatabase &out, std::shared_ptr<IOStream> stream); void ParseBlendFile(Blender::FileDatabase &out, std::shared_ptr<IOStream> stream);
void ExtractScene(Blender::Scene &out, const Blender::FileDatabase &file); void ExtractScene(Blender::Scene &out, const Blender::FileDatabase &file);
void ParseSubCollection(const Blender::Scene &in, aiNode *root, std::shared_ptr<Blender::Collection> collection, Blender::ConversionData &conv_data); void ParseSubCollection(const Blender::Scene &in, aiNode *root, const std::shared_ptr<Blender::Collection>& collection, Blender::ConversionData &conv_data);
void ConvertBlendFile(aiScene *out, const Blender::Scene &in, const Blender::FileDatabase &file); void ConvertBlendFile(aiScene *out, const Blender::Scene &in, const Blender::FileDatabase &file);
private: private:

View File

@ -228,7 +228,6 @@ void FBXConverter::ConvertNodes(uint64_t id, aiNode *parent, aiNode *root_node)
nodes_chain.clear(); nodes_chain.clear();
post_nodes_chain.clear(); post_nodes_chain.clear();
aiMatrix4x4 new_abs_transform = parent->mTransformation;
std::string node_name = FixNodeName(model->Name()); std::string node_name = FixNodeName(model->Name());
// even though there is only a single input node, the design of // even though there is only a single input node, the design of
// assimp (or rather: the complicated transformation chain that // assimp (or rather: the complicated transformation chain that
@ -262,12 +261,10 @@ void FBXConverter::ConvertNodes(uint64_t id, aiNode *parent, aiNode *root_node)
child->mParent = last_parent; child->mParent = last_parent;
last_parent = child.mNode; last_parent = child.mNode;
new_abs_transform *= child->mTransformation;
} }
// attach geometry // attach geometry
ConvertModel(*model, nodes_chain.back().mNode, root_node, new_abs_transform); ConvertModel(*model, nodes_chain.back().mNode, root_node);
// check if there will be any child nodes // check if there will be any child nodes
const std::vector<const Connection *> &child_conns = doc.GetConnectionsByDestinationSequenced(model->ID(), "Model"); const std::vector<const Connection *> &child_conns = doc.GetConnectionsByDestinationSequenced(model->ID(), "Model");
@ -286,8 +283,6 @@ void FBXConverter::ConvertNodes(uint64_t id, aiNode *parent, aiNode *root_node)
postnode->mParent = last_parent; postnode->mParent = last_parent;
last_parent = postnode.mNode; last_parent = postnode.mNode;
new_abs_transform *= postnode->mTransformation;
} }
} else { } else {
// free the nodes we allocated as we don't need them // free the nodes we allocated as we don't need them
@ -895,8 +890,7 @@ void FBXConverter::SetupNodeMetadata(const Model &model, aiNode &nd) {
} }
} }
void FBXConverter::ConvertModel(const Model &model, aiNode *parent, aiNode *root_node, void FBXConverter::ConvertModel(const Model &model, aiNode *parent, aiNode *root_node) {
const aiMatrix4x4 &absolute_transform) {
const std::vector<const Geometry *> &geos = model.GetGeometry(); const std::vector<const Geometry *> &geos = model.GetGeometry();
std::vector<unsigned int> meshes; std::vector<unsigned int> meshes;
@ -906,8 +900,7 @@ void FBXConverter::ConvertModel(const Model &model, aiNode *parent, aiNode *root
const MeshGeometry *const mesh = dynamic_cast<const MeshGeometry *>(geo); const MeshGeometry *const mesh = dynamic_cast<const MeshGeometry *>(geo);
const LineGeometry *const line = dynamic_cast<const LineGeometry *>(geo); const LineGeometry *const line = dynamic_cast<const LineGeometry *>(geo);
if (mesh) { if (mesh) {
const std::vector<unsigned int> &indices = ConvertMesh(*mesh, model, parent, root_node, const std::vector<unsigned int> &indices = ConvertMesh(*mesh, model, parent, root_node);
absolute_transform);
std::copy(indices.begin(), indices.end(), std::back_inserter(meshes)); std::copy(indices.begin(), indices.end(), std::back_inserter(meshes));
} else if (line) { } else if (line) {
const std::vector<unsigned int> &indices = ConvertLine(*line, root_node); const std::vector<unsigned int> &indices = ConvertLine(*line, root_node);
@ -928,8 +921,7 @@ void FBXConverter::ConvertModel(const Model &model, aiNode *parent, aiNode *root
} }
std::vector<unsigned int> std::vector<unsigned int>
FBXConverter::ConvertMesh(const MeshGeometry &mesh, const Model &model, aiNode *parent, aiNode *root_node, FBXConverter::ConvertMesh(const MeshGeometry &mesh, const Model &model, aiNode *parent, aiNode *root_node) {
const aiMatrix4x4 &absolute_transform) {
std::vector<unsigned int> temp; std::vector<unsigned int> temp;
MeshMap::const_iterator it = meshes_converted.find(&mesh); MeshMap::const_iterator it = meshes_converted.find(&mesh);
@ -952,13 +944,13 @@ FBXConverter::ConvertMesh(const MeshGeometry &mesh, const Model &model, aiNode *
const MatIndexArray::value_type base = mindices[0]; const MatIndexArray::value_type base = mindices[0];
for (MatIndexArray::value_type index : mindices) { for (MatIndexArray::value_type index : mindices) {
if (index != base) { if (index != base) {
return ConvertMeshMultiMaterial(mesh, model, parent, root_node, absolute_transform); return ConvertMeshMultiMaterial(mesh, model, parent, root_node);
} }
} }
} }
// faster code-path, just copy the data // faster code-path, just copy the data
temp.push_back(ConvertMeshSingleMaterial(mesh, model, absolute_transform, parent, root_node)); temp.push_back(ConvertMeshSingleMaterial(mesh, model, parent, root_node));
return temp; return temp;
} }
@ -1055,8 +1047,7 @@ static aiSkeleton *createAiSkeleton(SkeletonBoneContainer &sbc) {
} }
unsigned int FBXConverter::ConvertMeshSingleMaterial(const MeshGeometry &mesh, const Model &model, unsigned int FBXConverter::ConvertMeshSingleMaterial(const MeshGeometry &mesh, const Model &model,
const aiMatrix4x4 &absolute_transform, aiNode *parent, aiNode *parent, aiNode *) {
aiNode *) {
const MatIndexArray &mindices = mesh.GetMaterialIndices(); const MatIndexArray &mindices = mesh.GetMaterialIndices();
aiMesh *const out_mesh = SetupEmptyMesh(mesh, parent); aiMesh *const out_mesh = SetupEmptyMesh(mesh, parent);
@ -1230,8 +1221,7 @@ unsigned int FBXConverter::ConvertMeshSingleMaterial(const MeshGeometry &mesh, c
std::vector<unsigned int> std::vector<unsigned int>
FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model, aiNode *parent, FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model, aiNode *parent,
aiNode *root_node, aiNode *root_node) {
const aiMatrix4x4 &absolute_transform) {
const MatIndexArray &mindices = mesh.GetMaterialIndices(); const MatIndexArray &mindices = mesh.GetMaterialIndices();
ai_assert(mindices.size()); ai_assert(mindices.size());
@ -1241,7 +1231,7 @@ FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &mo
for (MatIndexArray::value_type index : mindices) { for (MatIndexArray::value_type index : mindices) {
if (had.find(index) == had.end()) { if (had.find(index) == had.end()) {
indices.push_back(ConvertMeshMultiMaterial(mesh, model, index, parent, root_node, absolute_transform)); indices.push_back(ConvertMeshMultiMaterial(mesh, model, index, parent, root_node));
had.insert(index); had.insert(index);
} }
} }
@ -1251,8 +1241,7 @@ FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &mo
unsigned int FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model, unsigned int FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model,
MatIndexArray::value_type index, MatIndexArray::value_type index,
aiNode *parent, aiNode *, aiNode *parent, aiNode *) {
const aiMatrix4x4 &absolute_transform) {
aiMesh *const out_mesh = SetupEmptyMesh(mesh, parent); aiMesh *const out_mesh = SetupEmptyMesh(mesh, parent);
const MatIndexArray &mindices = mesh.GetMaterialIndices(); const MatIndexArray &mindices = mesh.GetMaterialIndices();
@ -1415,7 +1404,7 @@ unsigned int FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, co
ConvertMaterialForMesh(out_mesh, model, mesh, index); ConvertMaterialForMesh(out_mesh, model, mesh, index);
if (process_weights) { if (process_weights) {
ConvertWeights(out_mesh, mesh, absolute_transform, parent, index, &reverseMapping); ConvertWeights(out_mesh, mesh, parent, index, &reverseMapping);
} }
std::vector<aiAnimMesh *> animMeshes; std::vector<aiAnimMesh *> animMeshes;
@ -1497,7 +1486,6 @@ void FBXConverter::ConvertWeightsToSkeleton(aiMesh *out, const MeshGeometry &geo
} }
void FBXConverter::ConvertWeights(aiMesh *out, const MeshGeometry &geo, void FBXConverter::ConvertWeights(aiMesh *out, const MeshGeometry &geo,
const aiMatrix4x4 &absolute_transform,
aiNode *parent, unsigned int materialIndex, aiNode *parent, unsigned int materialIndex,
std::vector<unsigned int> *outputVertStartIndices) { std::vector<unsigned int> *outputVertStartIndices) {
ai_assert(geo.DeformerSkin()); ai_assert(geo.DeformerSkin());
@ -1566,7 +1554,7 @@ void FBXConverter::ConvertWeights(aiMesh *out, const MeshGeometry &geo,
// XXX this could be heavily simplified by collecting the bone // XXX this could be heavily simplified by collecting the bone
// data in a single step. // data in a single step.
ConvertCluster(bones, cluster, out_indices, index_out_indices, ConvertCluster(bones, cluster, out_indices, index_out_indices,
count_out_indices, absolute_transform, parent); count_out_indices, parent);
} }
bone_map.clear(); bone_map.clear();
@ -1605,14 +1593,16 @@ void FBXConverter::ConvertCluster(std::vector<aiBone*> &local_mesh_bones, const
bone = new aiBone(); bone = new aiBone();
bone->mName = bone_name; bone->mName = bone_name;
bone->mOffsetMatrix = cl->Transform();
// store local transform link for post processing // store local transform link for post processing
/*
bone->mOffsetMatrix = cluster->TransformLink(); bone->mOffsetMatrix = cluster->TransformLink();
bone->mOffsetMatrix.Inverse(); bone->mOffsetMatrix.Inverse();
aiMatrix4x4 matrix = (aiMatrix4x4)absolute_transform; aiMatrix4x4 matrix = (aiMatrix4x4)absolute_transform;
bone->mOffsetMatrix = bone->mOffsetMatrix * matrix; // * mesh_offset bone->mOffsetMatrix = bone->mOffsetMatrix * matrix; // * mesh_offset
*/
// //
// Now calculate the aiVertexWeights // Now calculate the aiVertexWeights
// //
@ -2213,6 +2203,9 @@ void FBXConverter::SetShadingPropertiesCommon(aiMaterial *out_mat, const Propert
const float ShininessExponent = PropertyGet<float>(props, "ShininessExponent", ok); const float ShininessExponent = PropertyGet<float>(props, "ShininessExponent", ok);
if (ok) { if (ok) {
out_mat->AddProperty(&ShininessExponent, 1, AI_MATKEY_SHININESS); out_mat->AddProperty(&ShininessExponent, 1, AI_MATKEY_SHININESS);
// Match Blender behavior to extract roughness when only shininess is present
const float roughness = 1.0f - (sqrt(ShininessExponent) / 10.0f);
out_mat->AddProperty(&roughness, 1, AI_MATKEY_ROUGHNESS_FACTOR);
} }
// TransparentColor / TransparencyFactor... gee thanks FBX :rolleyes: // TransparentColor / TransparencyFactor... gee thanks FBX :rolleyes:

View File

@ -191,14 +191,12 @@ private:
void SetupNodeMetadata(const Model& model, aiNode& nd); void SetupNodeMetadata(const Model& model, aiNode& nd);
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void ConvertModel(const Model &model, aiNode *parent, aiNode *root_node, void ConvertModel(const Model &model, aiNode *parent, aiNode *root_node);
const aiMatrix4x4 &absolute_transform);
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// MeshGeometry -> aiMesh, return mesh index + 1 or 0 if the conversion failed // MeshGeometry -> aiMesh, return mesh index + 1 or 0 if the conversion failed
std::vector<unsigned int> std::vector<unsigned int>
ConvertMesh(const MeshGeometry &mesh, const Model &model, aiNode *parent, aiNode *root_node, ConvertMesh(const MeshGeometry &mesh, const Model &model, aiNode *parent, aiNode *root_node);
const aiMatrix4x4 &absolute_transform);
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
std::vector<unsigned int> ConvertLine(const LineGeometry& line, aiNode *root_node); std::vector<unsigned int> ConvertLine(const LineGeometry& line, aiNode *root_node);
@ -208,17 +206,15 @@ private:
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
unsigned int ConvertMeshSingleMaterial(const MeshGeometry &mesh, const Model &model, unsigned int ConvertMeshSingleMaterial(const MeshGeometry &mesh, const Model &model,
const aiMatrix4x4 &absolute_transform, aiNode *parent, aiNode *parent, aiNode *root_node);
aiNode *root_node);
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
std::vector<unsigned int> std::vector<unsigned int>
ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model, aiNode *parent, aiNode *root_node, ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model, aiNode *parent, aiNode *root_node);
const aiMatrix4x4 &absolute_transform);
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
unsigned int ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model, MatIndexArray::value_type index, unsigned int ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model, MatIndexArray::value_type index,
aiNode *parent, aiNode *root_node, const aiMatrix4x4 &absolute_transform); aiNode *parent, aiNode *root_node);
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
static const unsigned int NO_MATERIAL_SEPARATION = /* std::numeric_limits<unsigned int>::max() */ static const unsigned int NO_MATERIAL_SEPARATION = /* std::numeric_limits<unsigned int>::max() */
@ -231,8 +227,8 @@ private:
* - outputVertStartIndices is only used when a material index is specified, it gives for * - outputVertStartIndices is only used when a material index is specified, it gives for
* each output vertex the DOM index it maps to. * each output vertex the DOM index it maps to.
*/ */
void ConvertWeights(aiMesh *out, const MeshGeometry &geo, const aiMatrix4x4 &absolute_transform, void ConvertWeights(aiMesh *out, const MeshGeometry &geo, aiNode *parent = nullptr,
aiNode *parent = nullptr, unsigned int materialIndex = NO_MATERIAL_SEPARATION, unsigned int materialIndex = NO_MATERIAL_SEPARATION,
std::vector<unsigned int> *outputVertStartIndices = nullptr); std::vector<unsigned int> *outputVertStartIndices = nullptr);
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -243,8 +239,7 @@ private:
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void ConvertCluster(std::vector<aiBone *> &local_mesh_bones, const Cluster *cl, void ConvertCluster(std::vector<aiBone *> &local_mesh_bones, const Cluster *cl,
std::vector<size_t> &out_indices, std::vector<size_t> &index_out_indices, std::vector<size_t> &out_indices, std::vector<size_t> &index_out_indices,
std::vector<size_t> &count_out_indices, const aiMatrix4x4 &absolute_transform, std::vector<size_t> &count_out_indices, aiNode *parent );
aiNode *parent );
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void ConvertMaterialForMesh(aiMesh* out, const Model& model, const MeshGeometry& geo, void ConvertMaterialForMesh(aiMesh* out, const Model& model, const MeshGeometry& geo,

View File

@ -235,10 +235,8 @@ FileGlobalSettings::FileGlobalSettings(const Document &doc, std::shared_ptr<cons
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
Document::Document(const Parser& parser, const ImportSettings& settings) Document::Document(const Parser& parser, const ImportSettings& settings) :
: settings(settings) settings(settings), parser(parser) {
, parser(parser)
{
ASSIMP_LOG_DEBUG("Creating FBX Document"); ASSIMP_LOG_DEBUG("Creating FBX Document");
// Cannot use array default initialization syntax because vc8 fails on it // Cannot use array default initialization syntax because vc8 fails on it
@ -259,8 +257,7 @@ Document::Document(const Parser& parser, const ImportSettings& settings)
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
Document::~Document() Document::~Document() {
{
for(ObjectMap::value_type& v : objects) { for(ObjectMap::value_type& v : objects) {
delete v.second; delete v.second;
} }
@ -322,8 +319,7 @@ void Document::ReadHeader() {
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void Document::ReadGlobalSettings() void Document::ReadGlobalSettings() {
{
const Scope& sc = parser.GetRootScope(); const Scope& sc = parser.GetRootScope();
const Element* const ehead = sc["GlobalSettings"]; const Element* const ehead = sc["GlobalSettings"];
if ( nullptr == ehead || !ehead->Compound() ) { if ( nullptr == ehead || !ehead->Compound() ) {
@ -344,8 +340,7 @@ void Document::ReadGlobalSettings()
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void Document::ReadObjects() void Document::ReadObjects() {
{
// read ID objects from "Objects" section // read ID objects from "Objects" section
const Scope& sc = parser.GetRootScope(); const Scope& sc = parser.GetRootScope();
const Element* const eobjects = sc["Objects"]; const Element* const eobjects = sc["Objects"];
@ -392,8 +387,7 @@ void Document::ReadObjects()
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void Document::ReadPropertyTemplates() void Document::ReadPropertyTemplates() {
{
const Scope& sc = parser.GetRootScope(); const Scope& sc = parser.GetRootScope();
// read property templates from "Definitions" section // read property templates from "Definitions" section
const Element* const edefs = sc["Definitions"]; const Element* const edefs = sc["Definitions"];
@ -450,8 +444,7 @@ void Document::ReadPropertyTemplates()
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void Document::ReadConnections() void Document::ReadConnections() {
{
const Scope& sc = parser.GetRootScope(); const Scope& sc = parser.GetRootScope();
// read property templates from "Definitions" section // read property templates from "Definitions" section
const Element* const econns = sc["Connections"]; const Element* const econns = sc["Connections"];
@ -498,8 +491,7 @@ void Document::ReadConnections()
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
const std::vector<const AnimationStack*>& Document::AnimationStacks() const const std::vector<const AnimationStack*>& Document::AnimationStacks() const {
{
if (!animationStacksResolved.empty() || animationStacks.empty()) { if (!animationStacksResolved.empty() || animationStacks.empty()) {
return animationStacksResolved; return animationStacksResolved;
} }
@ -519,17 +511,15 @@ const std::vector<const AnimationStack*>& Document::AnimationStacks() const
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
LazyObject* Document::GetObject(uint64_t id) const LazyObject* Document::GetObject(uint64_t id) const {
{
ObjectMap::const_iterator it = objects.find(id); ObjectMap::const_iterator it = objects.find(id);
return it == objects.end() ? nullptr : (*it).second; return it == objects.end() ? nullptr : (*it).second;
} }
#define MAX_CLASSNAMES 6 constexpr size_t MAX_CLASSNAMES = 6;
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
std::vector<const Connection*> Document::GetConnectionsSequenced(uint64_t id, const ConnectionMap& conns) const std::vector<const Connection*> Document::GetConnectionsSequenced(uint64_t id, const ConnectionMap& conns) const {
{
std::vector<const Connection*> temp; std::vector<const Connection*> temp;
const std::pair<ConnectionMap::const_iterator,ConnectionMap::const_iterator> range = const std::pair<ConnectionMap::const_iterator,ConnectionMap::const_iterator> range =
@ -594,36 +584,31 @@ std::vector<const Connection*> Document::GetConnectionsSequenced(uint64_t id, bo
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
std::vector<const Connection*> Document::GetConnectionsBySourceSequenced(uint64_t source) const std::vector<const Connection*> Document::GetConnectionsBySourceSequenced(uint64_t source) const {
{
return GetConnectionsSequenced(source, ConnectionsBySource()); return GetConnectionsSequenced(source, ConnectionsBySource());
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
std::vector<const Connection*> Document::GetConnectionsBySourceSequenced(uint64_t src, const char* classname) const std::vector<const Connection*> Document::GetConnectionsBySourceSequenced(uint64_t src, const char* classname) const {
{
const char* arr[] = {classname}; const char* arr[] = {classname};
return GetConnectionsBySourceSequenced(src, arr,1); return GetConnectionsBySourceSequenced(src, arr,1);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
std::vector<const Connection*> Document::GetConnectionsBySourceSequenced(uint64_t source, std::vector<const Connection*> Document::GetConnectionsBySourceSequenced(uint64_t source,
const char* const* classnames, size_t count) const const char* const* classnames, size_t count) const {
{
return GetConnectionsSequenced(source, true, ConnectionsBySource(),classnames, count); return GetConnectionsSequenced(source, true, ConnectionsBySource(),classnames, count);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
std::vector<const Connection*> Document::GetConnectionsByDestinationSequenced(uint64_t dest, std::vector<const Connection*> Document::GetConnectionsByDestinationSequenced(uint64_t dest,
const char* classname) const const char* classname) const {
{
const char* arr[] = {classname}; const char* arr[] = {classname};
return GetConnectionsByDestinationSequenced(dest, arr,1); return GetConnectionsByDestinationSequenced(dest, arr,1);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
std::vector<const Connection*> Document::GetConnectionsByDestinationSequenced(uint64_t dest) const std::vector<const Connection*> Document::GetConnectionsByDestinationSequenced(uint64_t dest) const {
{
return GetConnectionsSequenced(dest, ConnectionsByDestination()); return GetConnectionsSequenced(dest, ConnectionsByDestination());
} }
@ -635,52 +620,36 @@ std::vector<const Connection*> Document::GetConnectionsByDestinationSequenced(ui
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
Connection::Connection(uint64_t insertionOrder, uint64_t src, uint64_t dest, const std::string& prop, Connection::Connection(uint64_t insertionOrder, uint64_t src, uint64_t dest, const std::string& prop,
const Document& doc) const Document& doc) :
insertionOrder(insertionOrder), prop(prop), src(src), dest(dest), doc(doc) {
: insertionOrder(insertionOrder)
, prop(prop)
, src(src)
, dest(dest)
, doc(doc)
{
ai_assert(doc.Objects().find(src) != doc.Objects().end()); ai_assert(doc.Objects().find(src) != doc.Objects().end());
// dest may be 0 (root node) // dest may be 0 (root node)
ai_assert(!dest || doc.Objects().find(dest) != doc.Objects().end()); ai_assert(!dest || doc.Objects().find(dest) != doc.Objects().end());
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
Connection::~Connection() LazyObject& Connection::LazySourceObject() const {
{
// empty
}
// ------------------------------------------------------------------------------------------------
LazyObject& Connection::LazySourceObject() const
{
LazyObject* const lazy = doc.GetObject(src); LazyObject* const lazy = doc.GetObject(src);
ai_assert(lazy); ai_assert(lazy);
return *lazy; return *lazy;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
LazyObject& Connection::LazyDestinationObject() const LazyObject& Connection::LazyDestinationObject() const {
{
LazyObject* const lazy = doc.GetObject(dest); LazyObject* const lazy = doc.GetObject(dest);
ai_assert(lazy); ai_assert(lazy);
return *lazy; return *lazy;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
const Object* Connection::SourceObject() const const Object* Connection::SourceObject() const {
{
LazyObject* const lazy = doc.GetObject(src); LazyObject* const lazy = doc.GetObject(src);
ai_assert(lazy); ai_assert(lazy);
return lazy->Get(); return lazy->Get();
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
const Object* Connection::DestinationObject() const const Object* Connection::DestinationObject() const {
{
LazyObject* const lazy = doc.GetObject(dest); LazyObject* const lazy = doc.GetObject(dest);
ai_assert(lazy); ai_assert(lazy);
return lazy->Get(); return lazy->Get();
@ -689,4 +658,4 @@ const Object* Connection::DestinationObject() const
} // !FBX } // !FBX
} // !Assimp } // !Assimp
#endif #endif // ASSIMP_BUILD_NO_FBX_IMPORTER

View File

@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2022, assimp team Copyright (c) 2006-2022, assimp team
All rights reserved. All rights reserved.
Redistribution and use of this software in source and binary forms, Redistribution and use of this software in source and binary forms,
@ -267,8 +266,7 @@ public:
Light(uint64_t id, const Element& element, const Document& doc, const std::string& name); Light(uint64_t id, const Element& element, const Document& doc, const std::string& name);
virtual ~Light() = default; virtual ~Light() = default;
enum Type enum Type {
{
Type_Point, Type_Point,
Type_Directional, Type_Directional,
Type_Spot, Type_Spot,
@ -278,8 +276,7 @@ public:
Type_MAX // end-of-enum sentinel Type_MAX // end-of-enum sentinel
}; };
enum Decay enum Decay {
{
Decay_None, Decay_None,
Decay_Linear, Decay_Linear,
Decay_Quadratic, Decay_Quadratic,
@ -347,7 +344,7 @@ public:
Model(uint64_t id, const Element& element, const Document& doc, const std::string& name); Model(uint64_t id, const Element& element, const Document& doc, const std::string& name);
virtual ~Model(); virtual ~Model() = default;
fbx_simple_property(QuaternionInterpolate, int, 0) fbx_simple_property(QuaternionInterpolate, int, 0)
@ -578,31 +575,27 @@ public:
BlendMode_BlendModeCount BlendMode_BlendModeCount
}; };
const Texture* getTexture(int index=0) const const Texture* getTexture(int index=0) const {
{
return textures[index]; return textures[index];
} }
int textureCount() const { int textureCount() const {
return static_cast<int>(textures.size()); return static_cast<int>(textures.size());
} }
BlendMode GetBlendMode() const BlendMode GetBlendMode() const {
{
return blendMode; return blendMode;
} }
float Alpha() float Alpha() {
{
return alpha; return alpha;
} }
private: private:
std::vector<const Texture*> textures; std::vector<const Texture*> textures;
BlendMode blendMode; BlendMode blendMode;
float alpha; float alpha;
}; };
typedef std::fbx_unordered_map<std::string, const Texture*> TextureMap; using TextureMap = std::fbx_unordered_map<std::string, const Texture*>;
typedef std::fbx_unordered_map<std::string, const LayeredTexture*> LayeredTextureMap; using LayeredTextureMap = std::fbx_unordered_map<std::string, const LayeredTexture*>;
/** DOM class for generic FBX videos */ /** DOM class for generic FBX videos */
class Video : public Object { class Video : public Object {
@ -690,8 +683,8 @@ private:
LayeredTextureMap layeredTextures; LayeredTextureMap layeredTextures;
}; };
typedef std::vector<int64_t> KeyTimeList; using KeyTimeList = std::vector<int64_t>;
typedef std::vector<float> KeyValueList; using KeyValueList = std::vector<float>;
/** Represents a FBX animation curve (i.e. a 1-dimensional set of keyframes and values therefore) */ /** Represents a FBX animation curve (i.e. a 1-dimensional set of keyframes and values therefore) */
class AnimationCurve : public Object { class AnimationCurve : public Object {
@ -727,7 +720,7 @@ private:
}; };
// property-name -> animation curve // property-name -> animation curve
typedef std::map<std::string, const AnimationCurve*> AnimationCurveMap; using AnimationCurveMap = std::map<std::string, const AnimationCurve*>;
/** Represents a FBX animation curve (i.e. a mapping from single animation curves to nodes) */ /** Represents a FBX animation curve (i.e. a mapping from single animation curves to nodes) */
class AnimationCurveNode : public Object { class AnimationCurveNode : public Object {
@ -777,7 +770,7 @@ private:
const Document& doc; const Document& doc;
}; };
typedef std::vector<const AnimationCurveNode*> AnimationCurveNodeList; using AnimationCurveNodeList = std::vector<const AnimationCurveNode*>;
/** Represents a FBX animation layer (i.e. a list of node animations) */ /** Represents a FBX animation layer (i.e. a list of node animations) */
class AnimationLayer : public Object { class AnimationLayer : public Object {
@ -800,7 +793,7 @@ private:
const Document& doc; const Document& doc;
}; };
typedef std::vector<const AnimationLayer*> AnimationLayerList; using AnimationLayerList = std::vector<const AnimationLayer*>;
/** Represents a FBX animation stack (i.e. a list of animation layers) */ /** Represents a FBX animation stack (i.e. a list of animation layers) */
class AnimationStack : public Object { class AnimationStack : public Object {
@ -843,8 +836,8 @@ private:
std::shared_ptr<const PropertyTable> props; std::shared_ptr<const PropertyTable> props;
}; };
typedef std::vector<float> WeightArray; using WeightArray = std::vector<float>;
typedef std::vector<unsigned int> WeightIndexArray; using WeightIndexArray = std::vector<unsigned int>;
/** DOM class for BlendShapeChannel deformers */ /** DOM class for BlendShapeChannel deformers */
@ -956,7 +949,7 @@ class Connection {
public: public:
Connection(uint64_t insertionOrder, uint64_t src, uint64_t dest, const std::string& prop, const Document& doc); Connection(uint64_t insertionOrder, uint64_t src, uint64_t dest, const std::string& prop, const Document& doc);
~Connection(); ~Connection() = default;
// note: a connection ensures that the source and dest objects exist, but // note: a connection ensures that the source and dest objects exist, but
// not that they have DOM representations, so the return value of one of // not that they have DOM representations, so the return value of one of
@ -1011,10 +1004,9 @@ public:
// during their entire lifetime (Document). FBX files have // during their entire lifetime (Document). FBX files have
// up to many thousands of objects (most of which we never use), // up to many thousands of objects (most of which we never use),
// so the memory overhead for them should be kept at a minimum. // so the memory overhead for them should be kept at a minimum.
typedef std::fbx_unordered_map<uint64_t, LazyObject*> ObjectMap; using ObjectMap = std::fbx_unordered_map<uint64_t, LazyObject*> ;
typedef std::fbx_unordered_map<std::string, std::shared_ptr<const PropertyTable> > PropertyTemplateMap; using PropertyTemplateMap = std::fbx_unordered_map<std::string, std::shared_ptr<const PropertyTable> > ;
using ConnectionMap = std::fbx_unordered_multimap<uint64_t, const Connection*>;
typedef std::fbx_unordered_multimap<uint64_t, const Connection*> ConnectionMap;
/** DOM class for global document settings, a single instance per document can /** DOM class for global document settings, a single instance per document can
* be accessed via Document.Globals(). */ * be accessed via Document.Globals(). */

View File

@ -140,11 +140,32 @@ Material::~Material() {
// empty // empty
} }
aiVector2D uvTrans;
aiVector2D uvScaling;
ai_real uvRotation;
std::string type;
std::string relativeFileName;
std::string fileName;
std::string alphaSource;
std::shared_ptr<const PropertyTable> props;
unsigned int crop[4]{};
const Video* media;
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
Texture::Texture(uint64_t id, const Element& element, const Document& doc, const std::string& name) : Texture::Texture(uint64_t id, const Element& element, const Document& doc, const std::string& name) :
Object(id,element,name), Object(id,element,name),
uvTrans(0.0f, 0.0f),
uvScaling(1.0f,1.0f), uvScaling(1.0f,1.0f),
media(0) { uvRotation(0.0f),
type(),
relativeFileName(),
fileName(),
alphaSource(),
props(),
media(nullptr) {
const Scope& sc = GetRequiredScope(element); const Scope& sc = GetRequiredScope(element);
const Element* const Type = sc["Type"]; const Element* const Type = sc["Type"];

View File

@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2022, assimp team Copyright (c) 2006-2022, assimp team
All rights reserved. All rights reserved.
Redistribution and use of this software in source and binary forms, Redistribution and use of this software in source and binary forms,
@ -76,10 +75,6 @@ Model::Model(uint64_t id, const Element &element, const Document &doc, const std
ResolveLinks(element, doc); ResolveLinks(element, doc);
} }
// ------------------------------------------------------------------------------------------------
Model::~Model() {
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void Model::ResolveLinks(const Element&, const Document &doc) { void Model::ResolveLinks(const Element&, const Document &doc) {
const char *const arr[] = { "Geometry", "Material", "NodeAttribute" }; const char *const arr[] = { "Geometry", "Material", "NodeAttribute" };

View File

@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2022, assimp team Copyright (c) 2006-2022, assimp team
All rights reserved. All rights reserved.
Redistribution and use of this software in source and binary forms, Redistribution and use of this software in source and binary forms,
@ -51,7 +50,6 @@ namespace Assimp {
namespace IFC { namespace IFC {
namespace { namespace {
// -------------------------------------------------------------------------------- // --------------------------------------------------------------------------------
// Conic is the base class for Circle and Ellipse // Conic is the base class for Circle and Ellipse
// -------------------------------------------------------------------------------- // --------------------------------------------------------------------------------
@ -546,8 +544,10 @@ IfcFloat RecursiveSearch(const Curve* cv, const IfcVector3& val, IfcFloat a, Ifc
} }
} }
#ifndef __INTEL_LLVM_COMPILER
ai_assert( min_diff[ 0 ] != inf ); ai_assert( min_diff[ 0 ] != inf );
ai_assert( min_diff[ 1 ] != inf ); ai_assert( min_diff[ 1 ] != inf );
#endif // __INTEL_LLVM_COMPILER
if ( std::fabs(a-min_point[0]) < threshold || recurse >= max_recurse) { if ( std::fabs(a-min_point[0]) < threshold || recurse >= max_recurse) {
return min_point[0]; return min_point[0];
} }
@ -606,8 +606,10 @@ bool BoundedCurve::IsClosed() const {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void BoundedCurve::SampleDiscrete(TempMesh& out) const { void BoundedCurve::SampleDiscrete(TempMesh& out) const {
const ParamRange& range = GetParametricRange(); const ParamRange& range = GetParametricRange();
#ifndef __INTEL_LLVM_COMPILER
ai_assert( range.first != std::numeric_limits<IfcFloat>::infinity() ); ai_assert( range.first != std::numeric_limits<IfcFloat>::infinity() );
ai_assert( range.second != std::numeric_limits<IfcFloat>::infinity() ); ai_assert( range.second != std::numeric_limits<IfcFloat>::infinity() );
#endif // __INTEL_LLVM_COMPILER
return SampleDiscrete(out,range.first,range.second); return SampleDiscrete(out,range.first,range.second);
} }

View File

@ -1428,7 +1428,7 @@ bool GenerateOpenings(std::vector<TempOpening>& openings,
return true; return true;
} }
std::vector<IfcVector2> GetContourInPlane2D(std::shared_ptr<TempMesh> mesh,IfcMatrix3 planeSpace, std::vector<IfcVector2> GetContourInPlane2D(const std::shared_ptr<TempMesh>& mesh,IfcMatrix3 planeSpace,
IfcVector3 planeNor,IfcFloat planeOffset, IfcVector3 planeNor,IfcFloat planeOffset,
IfcVector3 extrusionDir,IfcVector3& wall_extrusion,bool& first,bool& ok) { IfcVector3 extrusionDir,IfcVector3& wall_extrusion,bool& first,bool& ok) {
std::vector<IfcVector2> contour; std::vector<IfcVector2> contour;
@ -1476,7 +1476,7 @@ std::vector<IfcVector2> GetContourInPlane2D(std::shared_ptr<TempMesh> mesh,IfcMa
return contour; return contour;
} }
const float close{ ai_epsilon }; const ai_real close{ ai_epsilon };
static bool isClose(IfcVector2 first,IfcVector2 second) { static bool isClose(IfcVector2 first,IfcVector2 second) {
auto diff = (second - first); auto diff = (second - first);
@ -1491,7 +1491,7 @@ static void logSegment(std::pair<IfcVector2,IfcVector2> segment) {
IFCImporter::LogInfo(msg2.str().c_str()); IFCImporter::LogInfo(msg2.str().c_str());
} }
std::vector<std::vector<IfcVector2>> GetContoursInPlane3D(std::shared_ptr<TempMesh> mesh,IfcMatrix3 planeSpace, std::vector<std::vector<IfcVector2>> GetContoursInPlane3D(const std::shared_ptr<TempMesh>& mesh,IfcMatrix3 planeSpace,
IfcFloat planeOffset) { IfcFloat planeOffset) {
{ {
@ -1676,7 +1676,7 @@ std::vector<std::vector<IfcVector2>> GetContoursInPlane3D(std::shared_ptr<TempMe
std::stringstream msg; std::stringstream msg;
msg << "GetContoursInPlane3D: found " << contours.size() << " contours:\n"; msg << "GetContoursInPlane3D: found " << contours.size() << " contours:\n";
for(auto c : contours) { for(const auto& c : contours) {
msg << " Contour: \n"; msg << " Contour: \n";
for(auto p : c) { for(auto p : c) {
msg << " " << p.x << " " << p.y << " \n"; msg << " " << p.x << " " << p.y << " \n";
@ -1690,7 +1690,7 @@ std::vector<std::vector<IfcVector2>> GetContoursInPlane3D(std::shared_ptr<TempMe
return contours; return contours;
} }
std::vector<std::vector<IfcVector2>> GetContoursInPlane(std::shared_ptr<TempMesh> mesh,IfcMatrix3 planeSpace, std::vector<std::vector<IfcVector2>> GetContoursInPlane(const std::shared_ptr<TempMesh>& mesh,IfcMatrix3 planeSpace,
IfcVector3 planeNor,IfcFloat planeOffset, IfcVector3 planeNor,IfcFloat planeOffset,
IfcVector3 extrusionDir,IfcVector3& wall_extrusion,bool& first) { IfcVector3 extrusionDir,IfcVector3& wall_extrusion,bool& first) {

View File

@ -64,6 +64,7 @@ namespace LWO {
#define AI_LWO_FOURCC_LWOB AI_IFF_FOURCC('L', 'W', 'O', 'B') #define AI_LWO_FOURCC_LWOB AI_IFF_FOURCC('L', 'W', 'O', 'B')
#define AI_LWO_FOURCC_LWO2 AI_IFF_FOURCC('L', 'W', 'O', '2') #define AI_LWO_FOURCC_LWO2 AI_IFF_FOURCC('L', 'W', 'O', '2')
#define AI_LWO_FOURCC_LWO3 AI_IFF_FOURCC('L', 'W', 'O', '3')
#define AI_LWO_FOURCC_LXOB AI_IFF_FOURCC('L', 'X', 'O', 'B') #define AI_LWO_FOURCC_LXOB AI_IFF_FOURCC('L', 'X', 'O', 'B')
// chunks specific to the LWOB format // chunks specific to the LWOB format
@ -248,6 +249,57 @@ namespace LWO {
#define AI_LWO_SPOT AI_IFF_FOURCC('S', 'P', 'O', 'T') #define AI_LWO_SPOT AI_IFF_FOURCC('S', 'P', 'O', 'T')
#define AI_LWO_PICK AI_IFF_FOURCC('P', 'I', 'C', 'K') #define AI_LWO_PICK AI_IFF_FOURCC('P', 'I', 'C', 'K')
// Surface Part
#define AI_LWO_NODS AI_IFF_FOURCC('N', 'O', 'D', 'S')
#define AI_LWO_NNDS AI_IFF_FOURCC('N', 'N', 'D', 'S')
#define AI_LWO_NTAG AI_IFF_FOURCC('N', 'T', 'A', 'G')
#define AI_LWO_NRNM AI_IFF_FOURCC('N', 'R', 'N', 'M')
#define AI_LWO_NRME AI_IFF_FOURCC('N', 'R', 'M', 'E')
#define AI_LWO_NDTA AI_IFF_FOURCC('N', 'D', 'T', 'A')
#define AI_LWO_ATTR AI_IFF_FOURCC('A', 'T', 'T', 'R')
#define AI_LWO_VERS AI_IFF_FOURCC('V', 'E', 'R', 'S')
#define AI_LWO_ENUM AI_IFF_FOURCC('E', 'N', 'U', 'M')
#define AI_LWO_ENTR AI_IFF_FOURCC('E', 'N', 'T', 'R')
#define AI_LWO_NAME AI_IFF_FOURCC('N', 'A', 'M', 'E')
#define AI_LWO_FLAG AI_IFF_FOURCC('F', 'L', 'A', 'G')
#define AI_LWO_TAG AI_IFF_FOURCC('T', 'A', 'G', ' ')
#define AI_LWO_VALU AI_IFF_FOURCC('V', 'A', 'L', 'U')
#define AI_LWO_IBGC AI_IFF_FOURCC('I', 'B', 'G', 'C')
#define AI_LWO_IOPC AI_IFF_FOURCC('I', 'O', 'P', 'C')
#define AI_LWO_IIMG AI_IFF_FOURCC('I', 'I', 'M', 'G')
#define AI_LWO_TXTR AI_IFF_FOURCC('T', 'X', 'T', 'R')
#define AI_LWO_IFAL AI_IFF_FOURCC('I', 'F', 'A', 'L')
#define AI_LWO_ISCL AI_IFF_FOURCC('I', 'S', 'C', 'L')
#define AI_LWO_IPOS AI_IFF_FOURCC('I', 'P', 'O', 'S')
#define AI_LWO_IROT AI_IFF_FOURCC('I', 'R', 'O', 'T')
#define AI_LWO_IBMP AI_IFF_FOURCC('I', 'B', 'M', 'P')
#define AI_LWO_IUTD AI_IFF_FOURCC('I', 'U', 'T', 'D')
#define AI_LWO_IVTD AI_IFF_FOURCC('I', 'V', 'T', 'D')
#define AI_LWO_IPIX AI_IFF_FOURCC('I', 'P', 'I', 'X')
#define AI_LWO_IMIP AI_IFF_FOURCC('I', 'M', 'I', 'P')
#define AI_LWO_IMOD AI_IFF_FOURCC('I', 'M', 'O', 'D')
#define AI_LWO_AMOD AI_IFF_FOURCC('A', 'M', 'O', 'D')
#define AI_LWO_IINV AI_IFF_FOURCC('I', 'I', 'N', 'V')
#define AI_LWO_INCR AI_IFF_FOURCC('I', 'N', 'C', 'R')
#define AI_LWO_IAXS AI_IFF_FOURCC('I', 'A', 'X', 'S')
#define AI_LWO_IFOT AI_IFF_FOURCC('I', 'F', 'O', 'T')
#define AI_LWO_ITIM AI_IFF_FOURCC('I', 'T', 'I', 'M')
#define AI_LWO_IWRL AI_IFF_FOURCC('I', 'W', 'R', 'L')
#define AI_LWO_IUTI AI_IFF_FOURCC('I', 'U', 'T', 'I')
#define AI_LWO_IINX AI_IFF_FOURCC('I', 'I', 'N', 'X')
#define AI_LWO_IINY AI_IFF_FOURCC('I', 'I', 'N', 'Y')
#define AI_LWO_IINZ AI_IFF_FOURCC('I', 'I', 'N', 'Z')
#define AI_LWO_IREF AI_IFF_FOURCC('I', 'R', 'E', 'F')
#define AI_LWO_IMST AI_IFF_FOURCC('I', 'M', 'S', 'T')
#define AI_LWO_VPVL AI_IFF_FOURCC('V', 'P', 'V', 'L')
#define AI_LWO_VPRM AI_IFF_FOURCC('V', 'P', 'R', 'M')
#define AI_LWO_IMAP AI_IFF_FOURCC('I', 'M', 'A', 'P')
#define AI_LWO_IUVI AI_IFF_FOURCC('I', 'U', 'V', 'I')
#define AI_LWO_IUTL AI_IFF_FOURCC('I', 'U', 'T', 'L')
#define AI_LWO_IVTL AI_IFF_FOURCC('I', 'V', 'T', 'L')
// MODO extension - per-vertex normal vectors // MODO extension - per-vertex normal vectors
#define AI_LWO_MODO_NORM AI_IFF_FOURCC('N', 'O', 'R', 'M') #define AI_LWO_MODO_NORM AI_IFF_FOURCC('N', 'O', 'R', 'M')
@ -555,6 +607,31 @@ struct Surface {
float mAdditiveTransparency; float mAdditiveTransparency;
}; };
// ---------------------------------------------------------------------------
/** \brief Data structure for a LWO node
*/
struct Node {
// Name of node
std::string mName;
// RefName of node
std::string mRefName;
// Ref FileName
std::string fileName;
};
struct NodeAttribute {
//! Color of the surface
aiColor3D mColor;
//! true for two-sided materials
bool bDoubleSided;
//! Various material parameters
float mDiffuseValue, mSpecularValue, mTransparency, mGlossiness, mLuminosity, mColorHighlights;
};
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
#define AI_LWO_VALIDATE_CHUNK_LENGTH(length, name, size) \ #define AI_LWO_VALIDATE_CHUNK_LENGTH(length, name, size) \
if (length < size) { \ if (length < size) { \

View File

@ -83,6 +83,7 @@ static const aiImporterDesc desc = {
LWOImporter::LWOImporter() : LWOImporter::LWOImporter() :
mIsLWO2(), mIsLWO2(),
mIsLXOB(), mIsLXOB(),
mIsLWO3(),
mLayers(), mLayers(),
mCurLayer(), mCurLayer(),
mTags(), mTags(),
@ -182,16 +183,19 @@ void LWOImporter::InternReadFile(const std::string &pFile,
mCurLayer->mIndex = (uint16_t) -1; mCurLayer->mIndex = (uint16_t) -1;
// old lightwave file format (prior to v6) // old lightwave file format (prior to v6)
mIsLWO2 = false;
mIsLWO3 = false;
mIsLXOB = false;
if (AI_LWO_FOURCC_LWOB == fileType) { if (AI_LWO_FOURCC_LWOB == fileType) {
ASSIMP_LOG_INFO("LWO file format: LWOB (<= LightWave 5.5)"); ASSIMP_LOG_INFO("LWO file format: LWOB (<= LightWave 5.5)");
mIsLWO2 = false;
mIsLXOB = false;
LoadLWOBFile(); LoadLWOBFile();
} else if (AI_LWO_FOURCC_LWO2 == fileType) { } else if (AI_LWO_FOURCC_LWO2 == fileType) {
// New lightwave format // New lightwave format
mIsLXOB = false;
ASSIMP_LOG_INFO("LWO file format: LWO2 (>= LightWave 6)"); ASSIMP_LOG_INFO("LWO file format: LWO2 (>= LightWave 6)");
} else if ( AI_LWO_FOURCC_LWO3 == fileType ) {
ASSIMP_LOG_INFO("LWO file format: LWO3 (>= LightWave 2018)");
} else if (AI_LWO_FOURCC_LXOB == fileType) { } else if (AI_LWO_FOURCC_LXOB == fileType) {
// MODO file format // MODO file format
mIsLXOB = true; mIsLXOB = true;
@ -207,8 +211,13 @@ void LWOImporter::InternReadFile(const std::string &pFile,
throw DeadlyImportError("Unknown LWO sub format: ", szBuff); throw DeadlyImportError("Unknown LWO sub format: ", szBuff);
} }
if (AI_LWO_FOURCC_LWOB != fileType) { if (AI_LWO_FOURCC_LWOB != fileType) { //
mIsLWO2 = true; if( AI_LWO_FOURCC_LWO3 == fileType ) {
mIsLWO3 = true;
} else {
mIsLWO2 = true;
}
LoadLWO2File(); LoadLWO2File();
// The newer lightwave format allows the user to configure the // The newer lightwave format allows the user to configure the
@ -442,6 +451,7 @@ void LWOImporter::InternReadFile(const std::string &pFile,
// The RemoveRedundantMaterials step will clean this up later // The RemoveRedundantMaterials step will clean this up later
pScene->mMaterials = new aiMaterial *[pScene->mNumMaterials = (unsigned int)mSurfaces->size()]; pScene->mMaterials = new aiMaterial *[pScene->mNumMaterials = (unsigned int)mSurfaces->size()];
for (unsigned int mat = 0; mat < pScene->mNumMaterials; ++mat) { for (unsigned int mat = 0; mat < pScene->mNumMaterials; ++mat) {
aiMaterial *pcMat = new aiMaterial(); aiMaterial *pcMat = new aiMaterial();
pScene->mMaterials[mat] = pcMat; pScene->mMaterials[mat] = pcMat;
@ -687,7 +697,7 @@ void LWOImporter::ResolveClips() {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void LWOImporter::AdjustTexturePath(std::string &out) { void LWOImporter::AdjustTexturePath(std::string &out) {
// --- this function is used for both LWO2 and LWOB // --- this function is used for both LWO2 and LWOB
if (!mIsLWO2 && ::strstr(out.c_str(), "(sequence)")) { if (!mIsLWO2 && !mIsLWO3 && ::strstr(out.c_str(), "(sequence)")) {
// remove the (sequence) and append 000 // remove the (sequence) and append 000
ASSIMP_LOG_INFO("LWOB: Sequence of animated texture found. It will be ignored"); ASSIMP_LOG_INFO("LWOB: Sequence of animated texture found. It will be ignored");
@ -730,7 +740,7 @@ void LWOImporter::LoadLWOPoints(unsigned int length) {
throw DeadlyImportError("LWO2: Points chunk length is not multiple of vertexLen (12)"); throw DeadlyImportError("LWO2: Points chunk length is not multiple of vertexLen (12)");
} }
unsigned int regularSize = (unsigned int)mCurLayer->mTempPoints.size() + length / 12; unsigned int regularSize = (unsigned int)mCurLayer->mTempPoints.size() + length / 12;
if (mIsLWO2) { if (mIsLWO2 || mIsLWO3) {
mCurLayer->mTempPoints.reserve(regularSize + (regularSize >> 2u)); mCurLayer->mTempPoints.reserve(regularSize + (regularSize >> 2u));
mCurLayer->mTempPoints.resize(regularSize); mCurLayer->mTempPoints.resize(regularSize);
@ -1155,6 +1165,76 @@ void LWOImporter::LoadLWO2Clip(unsigned int length) {
} }
} }
void LWOImporter::LoadLWO3Clip(unsigned int length) {
AI_LWO_VALIDATE_CHUNK_LENGTH(length, CLIP, 12);
mClips.push_back(LWO::Clip());
LWO::Clip &clip = mClips.back();
// first - get the index of the clip
clip.idx = GetU4();
IFF::ChunkHeader head = IFF::LoadChunk(mFileBuffer);
switch (head.type) {
case AI_LWO_STIL:
AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, STIL, 1);
// "Normal" texture
GetS0(clip.path, head.length);
clip.type = Clip::STILL;
break;
case AI_LWO_ISEQ:
AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, ISEQ, 16);
// Image sequence. We'll later take the first.
{
uint8_t digits = GetU1();
mFileBuffer++;
int16_t offset = GetU2();
mFileBuffer += 4;
int16_t start = GetU2();
mFileBuffer += 4;
std::string s;
std::ostringstream ss;
GetS0(s, head.length);
head.length -= (uint16_t)s.length() + 1;
ss << s;
ss << std::setw(digits) << offset + start;
GetS0(s, head.length);
ss << s;
clip.path = ss.str();
clip.type = Clip::SEQ;
}
break;
case AI_LWO_STCC:
ASSIMP_LOG_WARN("LWO3: Color shifted images are not supported");
break;
case AI_LWO_ANIM:
ASSIMP_LOG_WARN("LWO3: Animated textures are not supported");
break;
case AI_LWO_XREF:
AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, XREF, 4);
// Just a cross-reference to another CLIp
clip.type = Clip::REF;
clip.clipRef = GetU4();
break;
case AI_LWO_NEGA:
AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, NEGA, 2);
clip.negate = (0 != GetU2());
break;
default:
ASSIMP_LOG_WARN("LWO3: Encountered unknown CLIP sub-chunk");
}
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Load envelope description // Load envelope description
void LWOImporter::LoadLWO2Envelope(unsigned int length) { void LWOImporter::LoadLWO2Envelope(unsigned int length) {
@ -1265,6 +1345,104 @@ void LWOImporter::LoadLWO2Envelope(unsigned int length) {
} }
} }
void LWOImporter::LoadLWO3Envelope(unsigned int length) {
LE_NCONST uint8_t *const end = mFileBuffer + length;
AI_LWO_VALIDATE_CHUNK_LENGTH(length, ENVL, 4);
mEnvelopes.push_back(LWO::Envelope());
LWO::Envelope &envelope = mEnvelopes.back();
// Get the index of the envelope
envelope.index = ReadVSizedIntLWO2(mFileBuffer);
// ... and read all blocks
while (true) {
if (mFileBuffer + 8 >= end) break;
LE_NCONST IFF::ChunkHeader head = IFF::LoadChunk(mFileBuffer);
if (mFileBuffer + head.length > end)
throw DeadlyImportError("LWO3: Invalid envelope chunk length");
uint8_t *const next = mFileBuffer + head.length;
switch (head.type) {
// Type & representation of the envelope
case AI_LWO_TYPE:
AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, TYPE, 4);
mFileBuffer++; // skip user format
// Determine type of envelope
envelope.type = (LWO::EnvelopeType)*mFileBuffer;
++mFileBuffer;
break;
// precondition
case AI_LWO_PRE:
AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, PRE, 4);
envelope.pre = (LWO::PrePostBehaviour)GetU2();
break;
// postcondition
case AI_LWO_POST:
AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, POST, 4);
envelope.post = (LWO::PrePostBehaviour)GetU2();
break;
// keyframe
case AI_LWO_KEY: {
AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, KEY, 10);
envelope.keys.push_back(LWO::Key());
LWO::Key &key = envelope.keys.back();
key.time = GetF4();
key.value = GetF4();
break;
}
// interval interpolation
case AI_LWO_SPAN: {
AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, SPAN, 6);
if (envelope.keys.size() < 2)
ASSIMP_LOG_WARN("LWO3: Unexpected SPAN chunk");
else {
LWO::Key &key = envelope.keys.back();
switch (GetU4()) {
case AI_LWO_STEP:
key.inter = LWO::IT_STEP;
break;
case AI_LWO_LINE:
key.inter = LWO::IT_LINE;
break;
case AI_LWO_TCB:
key.inter = LWO::IT_TCB;
break;
case AI_LWO_HERM:
key.inter = LWO::IT_HERM;
break;
case AI_LWO_BEZI:
key.inter = LWO::IT_BEZI;
break;
case AI_LWO_BEZ2:
key.inter = LWO::IT_BEZ2;
break;
default:
ASSIMP_LOG_WARN("LWO3: Unknown interval interpolation mode");
};
// todo ... read params
}
break;
}
default:
ASSIMP_LOG_WARN("LWO3: Encountered unknown ENVL subchunk");
break;
}
// regardless how much we did actually read, go to the next chunk
mFileBuffer = next;
}
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Load file - master function // Load file - master function
void LWOImporter::LoadLWO2File() { void LWOImporter::LoadLWO2File() {
@ -1272,16 +1450,25 @@ void LWOImporter::LoadLWO2File() {
LE_NCONST uint8_t *const end = mFileBuffer + fileSize; LE_NCONST uint8_t *const end = mFileBuffer + fileSize;
unsigned int iUnnamed = 0; unsigned int iUnnamed = 0;
while (true) { while (true) {
if (mFileBuffer + sizeof(IFF::ChunkHeader) > end) break; if (mFileBuffer + sizeof(IFF::ChunkHeader) > end) break;
const IFF::ChunkHeader head = IFF::LoadChunk(mFileBuffer);
IFF::ChunkHeader head = IFF::LoadChunk(mFileBuffer);
int bufOffset = 0;
if( head.type == AI_IFF_FOURCC_FORM ) { // not chunk, it's a form
mFileBuffer -= 8;
head = IFF::LoadForm(mFileBuffer);
bufOffset = 4;
}
if (mFileBuffer + head.length > end) { if (mFileBuffer + head.length > end) {
throw DeadlyImportError("LWO2: Chunk length points behind the file"); throw DeadlyImportError("LWO2: Chunk length points behind the file");
break; break;
} }
uint8_t *const next = mFileBuffer + head.length; uint8_t *const next = mFileBuffer + head.length;
mFileBuffer += bufOffset;
if (!head.length) { if (!head.length) {
mFileBuffer = next; mFileBuffer = next;
continue; continue;
@ -1337,7 +1524,6 @@ void LWOImporter::LoadLWO2File() {
break; break;
} }
// vertex list // vertex list
case AI_LWO_PNTS: { case AI_LWO_PNTS: {
if (skip) if (skip)
@ -1399,19 +1585,29 @@ void LWOImporter::LoadLWO2File() {
// surface chunk // surface chunk
case AI_LWO_SURF: { case AI_LWO_SURF: {
LoadLWO2Surface(head.length); if( mIsLWO3 )
LoadLWO3Surface(head.length);
else
LoadLWO2Surface(head.length);
break; break;
} }
// clip chunk // clip chunk
case AI_LWO_CLIP: { case AI_LWO_CLIP: {
LoadLWO2Clip(head.length); if( mIsLWO3 )
LoadLWO3Clip(head.length);
else
LoadLWO2Clip(head.length);
break; break;
} }
// envelope chunk // envelope chunk
case AI_LWO_ENVL: { case AI_LWO_ENVL: {
LoadLWO2Envelope(head.length); if( mIsLWO3 )
LoadLWO3Envelope(head.length);
else
LoadLWO2Envelope(head.length);
break; break;
} }
} }

View File

@ -116,6 +116,8 @@ private:
*/ */
inline void GetS0(std::string &out, unsigned int max); inline void GetS0(std::string &out, unsigned int max);
inline float GetF4(); inline float GetF4();
inline float GetF8();
inline uint64_t GetU8();
inline uint32_t GetU4(); inline uint32_t GetU4();
inline uint16_t GetU2(); inline uint16_t GetU2();
inline uint8_t GetU1(); inline uint8_t GetU1();
@ -131,6 +133,7 @@ private:
* @param size Maximum size to be read, in bytes. * @param size Maximum size to be read, in bytes.
*/ */
void LoadLWO2Surface(unsigned int size); void LoadLWO2Surface(unsigned int size);
void LoadLWO3Surface(unsigned int size);
// ------------------------------------------------------------------- // -------------------------------------------------------------------
/** Loads a texture block from a LWO2 file. /** Loads a texture block from a LWO2 file.
@ -197,12 +200,23 @@ private:
* @param length Size of the chunk * @param length Size of the chunk
*/ */
void LoadLWO2Clip(unsigned int length); void LoadLWO2Clip(unsigned int length);
void LoadLWO3Clip(unsigned int length);
// ------------------------------------------------------------------- // -------------------------------------------------------------------
/** Load an envelope from an EVL chunk /** Load an envelope from an EVL chunk
* @param length Size of the chunk * @param length Size of the chunk
*/ */
void LoadLWO2Envelope(unsigned int length); void LoadLWO2Envelope(unsigned int length);
void LoadLWO3Envelope(unsigned int length);
// -------------------------------------------------------------------
/** Load an nodal blocks from surface form
* @param length Size of the chunk
*/
void LoadNodalBlocks(unsigned int length);
void LoadNodes(unsigned int length);
void LoadNodeTag(unsigned int length);
void LoadNodeData(unsigned int length);
// ------------------------------------------------------------------- // -------------------------------------------------------------------
/** Count vertices and faces in a LWOB/LWO2 file /** Count vertices and faces in a LWOB/LWO2 file
@ -347,6 +361,8 @@ protected:
/** true if the file is a LXOB file*/ /** true if the file is a LXOB file*/
bool mIsLXOB; bool mIsLXOB;
bool mIsLWO3;
/** Temporary list of layers from the file */ /** Temporary list of layers from the file */
LayerList *mLayers; LayerList *mLayers;
@ -400,6 +416,22 @@ inline float LWOImporter::GetF4() {
return f; return f;
} }
inline float LWOImporter::GetF8() {
double f;
::memcpy(&f, mFileBuffer, 8);
mFileBuffer += 8;
AI_LSWAP8(f);
return (float)f;
}
inline uint64_t LWOImporter::GetU8() {
uint64_t f;
::memcpy(&f, mFileBuffer, 8);
mFileBuffer += 8;
AI_LSWAP8(f);
return f;
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
inline uint32_t LWOImporter::GetU4() { inline uint32_t LWOImporter::GetU4() {
uint32_t f; uint32_t f;

View File

@ -159,7 +159,7 @@ bool LWOImporter::HandleTextures(aiMaterial *pcMat, const TextureList &in, aiTex
// The older LWOB format does not use indirect references to clips. // The older LWOB format does not use indirect references to clips.
// The file name of a texture is directly specified in the tex chunk. // The file name of a texture is directly specified in the tex chunk.
if (mIsLWO2) { if (mIsLWO2 || mIsLWO3) {
// find the corresponding clip (take the last one if multiple // find the corresponding clip (take the last one if multiple
// share the same index) // share the same index)
ClipList::iterator end = mClips.end(), candidate = end; ClipList::iterator end = mClips.end(), candidate = end;
@ -270,7 +270,7 @@ void LWOImporter::ConvertMaterial(const LWO::Surface &surf, aiMaterial *pcMat) {
aiShadingMode m; aiShadingMode m;
if (surf.mSpecularValue && surf.mGlossiness) { if (surf.mSpecularValue && surf.mGlossiness) {
float fGloss; float fGloss;
if (mIsLWO2) { if (mIsLWO2 || mIsLWO3) {
fGloss = std::pow(surf.mGlossiness * ai_real(10.0) + ai_real(2.0), ai_real(2.0)); fGloss = std::pow(surf.mGlossiness * ai_real(10.0) + ai_real(2.0), ai_real(2.0));
} else { } else {
if (16.0 >= surf.mGlossiness) if (16.0 >= surf.mGlossiness)
@ -688,6 +688,252 @@ void LWOImporter::LoadLWO2ShaderBlock(LE_NCONST IFF::SubChunkHeader * /*head*/,
surf.mShaders.push_back(shader); surf.mShaders.push_back(shader);
} }
// ------------------------------------------------------------------------------------------------
void LWOImporter::LoadNodalBlocks(unsigned int size) {
LE_NCONST uint8_t *const end = mFileBuffer + size;
while (true) {
if (mFileBuffer + 8 >= end)
break;
IFF::ChunkHeader head = IFF::LoadChunk(mFileBuffer);
int bufOffset = 0;
if (head.type == AI_IFF_FOURCC_FORM) { // not chunk, it's a form
mFileBuffer -= 8;
head = IFF::LoadForm(mFileBuffer);
bufOffset = 4;
}
if (mFileBuffer + head.length > end) {
throw DeadlyImportError("LWO3: cannot read length; LoadNodalBlocks");
}
int node_idx = 0;
uint8_t *const next = mFileBuffer + head.length;
mFileBuffer += bufOffset;
switch (head.type) {
case AI_LWO_NNDS:
node_idx++;
LoadNodes(head.length);
break;
}
mFileBuffer = next;
}
}
// ------------------------------------------------------------------------------------------------
void LWOImporter::LoadNodes(unsigned int size) {
LE_NCONST uint8_t *const end = mFileBuffer + size;
while (true) {
if (mFileBuffer + 8 >= end)
break;
IFF::ChunkHeader head = IFF::LoadChunk(mFileBuffer);
int bufOffset = 0;
if (head.type == AI_IFF_FOURCC_FORM) { // not chunk, it's a form
mFileBuffer -= 8;
head = IFF::LoadForm(mFileBuffer);
bufOffset = 4;
}
if (mFileBuffer + head.length > end) {
throw DeadlyImportError("LWO3: cannot read length; LoadNodes");
}
uint8_t *const next = mFileBuffer + head.length;
mFileBuffer += bufOffset;
switch (head.type) {
case AI_LWO_NTAG:
LoadNodeTag(head.length);
break;
}
mFileBuffer = next;
}
}
// ------------------------------------------------------------------------------------------------
void LWOImporter::LoadNodeTag(unsigned int size) {
LE_NCONST uint8_t *const end = mFileBuffer + size;
while (true) {
if (mFileBuffer + 8 >= end)
break;
IFF::ChunkHeader head = IFF::LoadChunk(mFileBuffer);
int bufOffset = 0;
if (head.type == AI_IFF_FOURCC_FORM) { // not chunk, it's a form
mFileBuffer -= 8;
head = IFF::LoadForm(mFileBuffer);
bufOffset = 4;
}
if (mFileBuffer + head.length > end) {
throw DeadlyImportError("LWO3: cannot read length; LoadNodeTag");
}
uint8_t *const next = mFileBuffer + head.length;
mFileBuffer += bufOffset;
switch (head.type) {
case AI_LWO_NDTA:
LoadNodeData(head.length);
break;
}
mFileBuffer = next;
}
}
// ------------------------------------------------------------------------------------------------
void LWOImporter::LoadNodeData(unsigned int size) {
LE_NCONST uint8_t *const end = mFileBuffer + size;
LWO::Surface &surf = mSurfaces->back();
while (true) {
if (mFileBuffer + 8 >= end)
break;
IFF::ChunkHeader head = IFF::LoadChunk(mFileBuffer);
int bufOffset = 0;
if (head.type == AI_IFF_FOURCC_FORM) { // not chunk, it's a form
mFileBuffer -= 8;
head = IFF::LoadForm(mFileBuffer);
bufOffset = 4;
}
if (mFileBuffer + head.length > end) {
throw DeadlyImportError("LWO3: INVALID LENGTH; LoadNodeData");
}
uint8_t *const next = mFileBuffer + head.length;
mFileBuffer += bufOffset;
switch (head.type) {
case AI_LWO_VERS:
case AI_LWO_ENUM:
case AI_LWO_IBGC:
case AI_LWO_IOPC:
case AI_LWO_IIMG:
case AI_LWO_TXTR:
case AI_LWO_IFAL:
case AI_LWO_ISCL:
case AI_LWO_IPOS:
case AI_LWO_IROT:
case AI_LWO_IBMP:
case AI_LWO_IUTD:
case AI_LWO_IVTD:
case AI_LWO_IPIX:
case AI_LWO_IMIP:
case AI_LWO_IMOD:
case AI_LWO_AMOD:
case AI_LWO_IINV:
case AI_LWO_INCR:
case AI_LWO_IAXS:
case AI_LWO_IFOT:
case AI_LWO_ITIM:
case AI_LWO_IWRL:
case AI_LWO_IUTI:
case AI_LWO_IUVI:
case AI_LWO_IINX:
case AI_LWO_IINY:
case AI_LWO_IINZ:
case AI_LWO_IREF:
case AI_LWO_IMST:
case AI_LWO_IMAP:
case AI_LWO_IUTL:
case AI_LWO_IVTL:
case AI_LWO_VPVL:
case AI_LWO_VPRM:
mFileBuffer = next;
break;
case AI_LWO_ENTR:
std::string attrName;
while (true) {
if (mFileBuffer + 8 >= next)
break;
IFF::ChunkHeader head1 = IFF::LoadChunk(mFileBuffer);
int bufOffset1 = 0;
if (head1.type == AI_IFF_FOURCC_FORM) { // not chunk, it's a form
mFileBuffer -= 8;
head1 = IFF::LoadForm(mFileBuffer);
bufOffset1 = 4;
}
if (mFileBuffer + head1.length > end) {
throw DeadlyImportError("LWO3: cannot read length;");
}
uint8_t *const next1 = mFileBuffer + head1.length;
mFileBuffer += bufOffset1;
switch (head1.type) {
case AI_LWO_FLAG:
case AI_LWO_TAG:
mFileBuffer = next1;
break;
case AI_LWO_NAME:
GetS0(attrName, head1.length);
break;
case AI_LWO_VALU:
mFileBuffer += 8;
std::string valueType;
GetS0(valueType, 8);
if (valueType == "int") {
static_cast<void>(GetU4());
} else if (valueType == "double") {
static_cast<void>(GetU8());
} else if (valueType == "vparam") {
mFileBuffer += 24;
float value = GetF8();
if (attrName == "Diffuse") {
surf.mDiffuseValue = value;
} else if (attrName == "Specular") {
surf.mSpecularValue = value;
} else if (attrName == "Transparency") {
surf.mTransparency = value;
} else if (attrName == "Glossiness") {
surf.mGlossiness = value;
} else if (attrName == "Luminosity") {
surf.mLuminosity = value;
} else if (attrName == "Color Highlight") {
surf.mColorHighlights = value;
} else if (attrName == "Refraction Index") {
surf.mIOR = value;
} else if (attrName == "Bump Height") {
surf.mBumpIntensity = value;
}
} else if (valueType == "vparam3") {
mFileBuffer += 24;
float value1, value2, value3;
value1 = GetF8();
value2 = GetF8();
value3 = GetF8();
if (attrName == "Color") {
surf.mColor.r = value1;
surf.mColor.g = value2;
surf.mColor.b = value3;
}
}
mFileBuffer = next1;
break;
}
}
break;
}
}
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void LWOImporter::LoadLWO2Surface(unsigned int size) { void LWOImporter::LoadLWO2Surface(unsigned int size) {
LE_NCONST uint8_t *const end = mFileBuffer + size; LE_NCONST uint8_t *const end = mFileBuffer + size;
@ -841,4 +1087,69 @@ void LWOImporter::LoadLWO2Surface(unsigned int size) {
} }
} }
void LWOImporter::LoadLWO3Surface(unsigned int size) {
mFileBuffer += 8;
LE_NCONST uint8_t *const end = mFileBuffer + size - 12;
mSurfaces->push_back(LWO::Surface());
LWO::Surface &surf = mSurfaces->back();
GetS0(surf.mName, size);
// check whether this surface was derived from any other surface
std::string derived;
GetS0(derived, (unsigned int)(end - mFileBuffer));
if (derived.length()) {
// yes, find this surface
for (SurfaceList::iterator it = mSurfaces->begin(), itEnd = mSurfaces->end() - 1; it != itEnd; ++it) {
if ((*it).mName == derived) {
// we have it ...
surf = *it;
derived.clear();
break;
}
}
if (derived.size()) {
ASSIMP_LOG_WARN("LWO3: Unable to find source surface: ", derived);
}
}
while (true) {
if (mFileBuffer + 8 >= end)
break;
IFF::ChunkHeader head = IFF::LoadChunk(mFileBuffer);
int bufOffset = 0;
if( head.type == AI_IFF_FOURCC_FORM ) { // not chunk, it's a form
mFileBuffer -= 8;
head = IFF::LoadForm(mFileBuffer);
bufOffset = 4;
}
if (mFileBuffer + head.length > end) {
throw DeadlyImportError("LWO3: cannot read length; LoadLWO3Surface");
}
uint8_t *const next = mFileBuffer + head.length;
mFileBuffer += bufOffset;
switch (head.type) {
case AI_LWO_NODS:
LoadNodalBlocks(head.length);
break;
// polygon sidedness
case AI_LWO_SIDE: {
AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, SIDE, 2);
surf.bDoubleSided = (3 == GetU2());
break;
}
// maximum smoothing angle
case AI_LWO_SMAN: {
AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, SMAN, 4);
surf.mMaximumSmoothAngle = std::fabs(GetF4());
break;
}
}
mFileBuffer = next;
}
}
#endif // !! ASSIMP_BUILD_NO_X_IMPORTER #endif // !! ASSIMP_BUILD_NO_X_IMPORTER

View File

@ -57,9 +57,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Assimp specific M3D configuration. Comment out these defines to remove functionality // Assimp specific M3D configuration. Comment out these defines to remove functionality
//#define ASSIMP_USE_M3D_READFILECB //#define ASSIMP_USE_M3D_READFILECB
// Share stb_image's PNG loader with other importers/exporters instead of bringing our own copy. #include "Common/StbCommon.h"
#define STBI_ONLY_PNG
#include <stb/stb_image.h>
#include "m3d.h" #include "m3d.h"

View File

@ -857,6 +857,9 @@ void MDLImporter::CalculateUVCoordinates_MDL5() {
const float fHeight = (float)iHeight; const float fHeight = (float)iHeight;
aiMesh *pcMesh = this->pScene->mMeshes[0]; aiMesh *pcMesh = this->pScene->mMeshes[0];
for (unsigned int i = 0; i < pcMesh->mNumVertices; ++i) { for (unsigned int i = 0; i < pcMesh->mNumVertices; ++i) {
if (!pcMesh->HasTextureCoords(0)) {
continue;
}
pcMesh->mTextureCoords[0][i].x /= fWidth; pcMesh->mTextureCoords[0][i].x /= fWidth;
pcMesh->mTextureCoords[0][i].y /= fHeight; pcMesh->mTextureCoords[0][i].y /= fHeight;
pcMesh->mTextureCoords[0][i].y = 1.0f - pcMesh->mTextureCoords[0][i].y; // DX to OGL pcMesh->mTextureCoords[0][i].y = 1.0f - pcMesh->mTextureCoords[0][i].y; // DX to OGL

View File

@ -493,7 +493,7 @@ void MDLImporter::ParseSkinLump_3DGS_MDL7(
size_t iLen2 = iLen + 1; size_t iLen2 = iLen + 1;
iLen2 = iLen2 > MAXLEN ? MAXLEN : iLen2; iLen2 = iLen2 > MAXLEN ? MAXLEN : iLen2;
memcpy(szFile.data, (const char *)szCurrent, iLen2); memcpy(szFile.data, (const char *)szCurrent, iLen2);
szFile.length = (ai_uint32)iLen; szFile.length = static_cast<ai_uint32>(iLen2);
szCurrent += iLen2; szCurrent += iLen2;

View File

@ -52,9 +52,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/scene.h> #include <assimp/scene.h>
#include <assimp/Importer.hpp> #include <assimp/Importer.hpp>
#include <fstream>
#include <iomanip> #include <iomanip>
#include <memory> #include <memory>
#include <sstream>
static const aiImporterDesc desc = { "MMD Importer", static const aiImporterDesc desc = { "MMD Importer",
"", "",
@ -102,26 +103,32 @@ const aiImporterDesc *MMDImporter::GetInfo() const {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// MMD import implementation // MMD import implementation
void MMDImporter::InternReadFile(const std::string &file, aiScene *pScene, void MMDImporter::InternReadFile(const std::string &file, aiScene *pScene,
IOSystem * /*pIOHandler*/) { IOSystem* pIOHandler) {
// Read file by istream
std::filebuf fb; auto streamCloser = [&](IOStream *pStream) {
if (!fb.open(file, std::ios::in | std::ios::binary)) { pIOHandler->Close(pStream);
};
static const std::string mode = "rb";
const std::unique_ptr<IOStream, decltype(streamCloser)> fileStream(pIOHandler->Open(file, mode), streamCloser);
if (fileStream == nullptr) {
throw DeadlyImportError("Failed to open file ", file, "."); throw DeadlyImportError("Failed to open file ", file, ".");
} }
std::istream fileStream(&fb); const size_t fileSize = fileStream->FileSize();
if (fileSize < sizeof(pmx::PmxModel))
// Get the file-size and validate it, throwing an exception when fails {
fileStream.seekg(0, fileStream.end);
size_t fileSize = static_cast<size_t>(fileStream.tellg());
fileStream.seekg(0, fileStream.beg);
if (fileSize < sizeof(pmx::PmxModel)) {
throw DeadlyImportError(file, " is too small."); throw DeadlyImportError(file, " is too small.");
} }
std::vector<char> contents(fileStream->FileSize());
fileStream->Read(contents.data(), 1, contents.size());
std::istringstream iss(std::string(contents.begin(), contents.end()));
pmx::PmxModel model; pmx::PmxModel model;
model.Read(&fileStream); model.Read(&iss);
CreateDataFromImport(&model, pScene); CreateDataFromImport(&model, pScene);
} }

View File

@ -47,6 +47,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/types.h> #include <assimp/types.h>
#include <map> #include <map>
#include <vector> #include <vector>
#include "Common/Maybe.h"
namespace Assimp { namespace Assimp {
namespace ObjFile { namespace ObjFile {
@ -183,15 +184,15 @@ struct Material {
aiColor3D transparent; aiColor3D transparent;
//! PBR Roughness //! PBR Roughness
ai_real roughness; Maybe<ai_real> roughness;
//! PBR Metallic //! PBR Metallic
ai_real metallic; Maybe<ai_real> metallic;
//! PBR Metallic //! PBR Metallic
aiColor3D sheen; Maybe<aiColor3D> sheen;
//! PBR Clearcoat Thickness //! PBR Clearcoat Thickness
ai_real clearcoat_thickness; Maybe<ai_real> clearcoat_thickness;
//! PBR Clearcoat Rougness //! PBR Clearcoat Rougness
ai_real clearcoat_roughness; Maybe<ai_real> clearcoat_roughness;
//! PBR Anisotropy //! PBR Anisotropy
ai_real anisotropy; ai_real anisotropy;
@ -206,11 +207,11 @@ struct Material {
illumination_model(1), illumination_model(1),
ior(ai_real(1.0)), ior(ai_real(1.0)),
transparent(ai_real(1.0), ai_real(1.0), ai_real(1.0)), transparent(ai_real(1.0), ai_real(1.0), ai_real(1.0)),
roughness(ai_real(1.0)), roughness(),
metallic(ai_real(0.0)), metallic(),
sheen(ai_real(1.0), ai_real(1.0), ai_real(1.0)), sheen(),
clearcoat_thickness(ai_real(0.0)), clearcoat_thickness(),
clearcoat_roughness(ai_real(0.0)), clearcoat_roughness(),
anisotropy(ai_real(0.0)), anisotropy(ai_real(0.0)),
bump_multiplier(ai_real(1.0)) { bump_multiplier(ai_real(1.0)) {
std::fill_n(clamp, static_cast<unsigned int>(TextureTypeCount), false); std::fill_n(clamp, static_cast<unsigned int>(TextureTypeCount), false);

View File

@ -616,11 +616,16 @@ void ObjFileImporter::createMaterials(const ObjFile::Model *pModel, aiScene *pSc
mat->AddProperty(&pCurrentMaterial->shineness, 1, AI_MATKEY_SHININESS); mat->AddProperty(&pCurrentMaterial->shineness, 1, AI_MATKEY_SHININESS);
mat->AddProperty(&pCurrentMaterial->alpha, 1, AI_MATKEY_OPACITY); mat->AddProperty(&pCurrentMaterial->alpha, 1, AI_MATKEY_OPACITY);
mat->AddProperty(&pCurrentMaterial->transparent, 1, AI_MATKEY_COLOR_TRANSPARENT); mat->AddProperty(&pCurrentMaterial->transparent, 1, AI_MATKEY_COLOR_TRANSPARENT);
mat->AddProperty(&pCurrentMaterial->roughness, 1, AI_MATKEY_ROUGHNESS_FACTOR); if (pCurrentMaterial->roughness)
mat->AddProperty(&pCurrentMaterial->metallic, 1, AI_MATKEY_METALLIC_FACTOR); mat->AddProperty(&pCurrentMaterial->roughness.Get(), 1, AI_MATKEY_ROUGHNESS_FACTOR);
mat->AddProperty(&pCurrentMaterial->sheen, 1, AI_MATKEY_SHEEN_COLOR_FACTOR); if (pCurrentMaterial->metallic)
mat->AddProperty(&pCurrentMaterial->clearcoat_thickness, 1, AI_MATKEY_CLEARCOAT_FACTOR); mat->AddProperty(&pCurrentMaterial->metallic.Get(), 1, AI_MATKEY_METALLIC_FACTOR);
mat->AddProperty(&pCurrentMaterial->clearcoat_roughness, 1, AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR); if (pCurrentMaterial->sheen)
mat->AddProperty(&pCurrentMaterial->sheen.Get(), 1, AI_MATKEY_SHEEN_COLOR_FACTOR);
if (pCurrentMaterial->clearcoat_thickness)
mat->AddProperty(&pCurrentMaterial->clearcoat_thickness.Get(), 1, AI_MATKEY_CLEARCOAT_FACTOR);
if (pCurrentMaterial->clearcoat_roughness)
mat->AddProperty(&pCurrentMaterial->clearcoat_roughness.Get(), 1, AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR);
mat->AddProperty(&pCurrentMaterial->anisotropy, 1, AI_MATKEY_ANISOTROPY_FACTOR); mat->AddProperty(&pCurrentMaterial->anisotropy, 1, AI_MATKEY_ANISOTROPY_FACTOR);
// Adding refraction index // Adding refraction index

View File

@ -205,7 +205,7 @@ void ObjFileMtlImporter::load() {
break; break;
case 's': case 's':
++m_DataIt; ++m_DataIt;
getColorRGBA(&m_pModel->m_pCurrentMaterial->sheen); getColorRGBA(m_pModel->m_pCurrentMaterial->sheen);
break; break;
case 'c': case 'c':
++m_DataIt; ++m_DataIt;
@ -238,6 +238,7 @@ void ObjFileMtlImporter::load() {
case 'a': // Anisotropy case 'a': // Anisotropy
{ {
++m_DataIt;
getFloatValue(m_pModel->m_pCurrentMaterial->anisotropy); getFloatValue(m_pModel->m_pCurrentMaterial->anisotropy);
m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine); m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
} break; } break;
@ -267,6 +268,12 @@ void ObjFileMtlImporter::getColorRGBA(aiColor3D *pColor) {
pColor->b = b; pColor->b = b;
} }
void ObjFileMtlImporter::getColorRGBA(Maybe<aiColor3D> &value) {
aiColor3D v;
getColorRGBA(&v);
value = Maybe<aiColor3D>(v);
}
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Loads the kind of illumination model. // Loads the kind of illumination model.
void ObjFileMtlImporter::getIlluminationModel(int &illum_model) { void ObjFileMtlImporter::getIlluminationModel(int &illum_model) {
@ -274,6 +281,7 @@ void ObjFileMtlImporter::getIlluminationModel(int &illum_model) {
illum_model = atoi(&m_buffer[0]); illum_model = atoi(&m_buffer[0]);
} }
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Loads a single float value. // Loads a single float value.
void ObjFileMtlImporter::getFloatValue(ai_real &value) { void ObjFileMtlImporter::getFloatValue(ai_real &value) {
@ -287,6 +295,15 @@ void ObjFileMtlImporter::getFloatValue(ai_real &value) {
value = (ai_real)fast_atof(&m_buffer[0]); value = (ai_real)fast_atof(&m_buffer[0]);
} }
void ObjFileMtlImporter::getFloatValue(Maybe<ai_real> &value) {
m_DataIt = CopyNextWord<DataArrayIt>(m_DataIt, m_DataItEnd, &m_buffer[0], BUFFERSIZE);
size_t len = std::strlen(&m_buffer[0]);
if (len)
value = Maybe<ai_real>(fast_atof(&m_buffer[0]));
else
value = Maybe<ai_real>();
}
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Creates a material from loaded data. // Creates a material from loaded data.
void ObjFileMtlImporter::createMaterial() { void ObjFileMtlImporter::createMaterial() {

View File

@ -43,6 +43,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/defs.h> #include <assimp/defs.h>
#include <string> #include <string>
#include <vector> #include <vector>
#include "Common/Maybe.h"
struct aiColor3D; struct aiColor3D;
struct aiString; struct aiString;
@ -81,10 +82,12 @@ private:
void load(); void load();
/// Get color data. /// Get color data.
void getColorRGBA(aiColor3D *pColor); void getColorRGBA(aiColor3D *pColor);
void getColorRGBA(Maybe<aiColor3D> &value);
/// Get illumination model from loaded data /// Get illumination model from loaded data
void getIlluminationModel(int &illum_model); void getIlluminationModel(int &illum_model);
/// Gets a float value from data. /// Gets a float value from data.
void getFloatValue(ai_real &value); void getFloatValue(ai_real &value);
void getFloatValue(Maybe<ai_real> &value);
/// Creates a new material from loaded data. /// Creates a new material from loaded data.
void createMaterial(); void createMaterial();
/// Get texture name from loaded data. /// Get texture name from loaded data.

View File

@ -55,7 +55,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace Assimp { namespace Assimp {
constexpr char ObjFileParser::DEFAULT_MATERIAL[]; constexpr const char ObjFileParser::DEFAULT_MATERIAL[];
ObjFileParser::ObjFileParser() : ObjFileParser::ObjFileParser() :
m_DataIt(), m_DataIt(),
@ -117,6 +117,7 @@ void ObjFileParser::parseFile(IOStreamBuffer<char> &streamBuffer) {
unsigned int processed = 0; unsigned int processed = 0;
size_t lastFilePos(0); size_t lastFilePos(0);
bool insideCstype = false;
std::vector<char> buffer; std::vector<char> buffer;
while (streamBuffer.getNextDataLine(buffer, '\\')) { while (streamBuffer.getNextDataLine(buffer, '\\')) {
m_DataIt = buffer.begin(); m_DataIt = buffer.begin();
@ -131,6 +132,18 @@ void ObjFileParser::parseFile(IOStreamBuffer<char> &streamBuffer) {
m_progress->UpdateFileRead(processed, progressTotal); m_progress->UpdateFileRead(processed, progressTotal);
} }
// handle cstype section end (http://paulbourke.net/dataformats/obj/)
if (insideCstype) {
switch (*m_DataIt) {
case 'e': {
std::string name;
getNameNoSpace(m_DataIt, m_DataItEnd, name);
insideCstype = name != "end";
} break;
}
goto pf_skip_line;
}
// parse line // parse line
switch (*m_DataIt) { switch (*m_DataIt) {
case 'v': // Parse a vertex texture coordinate case 'v': // Parse a vertex texture coordinate
@ -219,6 +232,14 @@ void ObjFileParser::parseFile(IOStreamBuffer<char> &streamBuffer) {
getObjectName(); getObjectName();
} break; } break;
case 'c': // handle cstype section start
{
std::string name;
getNameNoSpace(m_DataIt, m_DataItEnd, name);
insideCstype = name == "cstype";
goto pf_skip_line;
} break;
default: { default: {
pf_skip_line: pf_skip_line:
m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine); m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);

View File

@ -141,7 +141,7 @@ private:
// because the class contains pointer to allocated memory // because the class contains pointer to allocated memory
/// Default material name /// Default material name
static constexpr char DEFAULT_MATERIAL[] = AI_DEFAULT_MATERIAL_NAME; static constexpr const char DEFAULT_MATERIAL[] = AI_DEFAULT_MATERIAL_NAME;
//! Iterator to current position in buffer //! Iterator to current position in buffer
DataArrayIt m_DataIt; DataArrayIt m_DataIt;
//! Iterator to end position of buffer //! Iterator to end position of buffer

View File

@ -248,6 +248,7 @@ void OgreXmlSerializer::ReadMesh(MeshXml *mesh) {
} else if (currentName == nnBoneAssignments) { } else if (currentName == nnBoneAssignments) {
ReadBoneAssignments(currentNode, mesh->sharedVertexData); ReadBoneAssignments(currentNode, mesh->sharedVertexData);
} else if (currentName == nnSkeletonLink) { } else if (currentName == nnSkeletonLink) {
mesh->skeletonRef = currentNode.attribute("name").as_string();
} }
} }
@ -488,6 +489,15 @@ bool OgreXmlSerializer::ImportSkeleton(Assimp::IOSystem *pIOHandler, MeshXml *me
Skeleton *skeleton = new Skeleton(); Skeleton *skeleton = new Skeleton();
OgreXmlSerializer serializer(xmlParser.get()); OgreXmlSerializer serializer(xmlParser.get());
XmlNode root = xmlParser->getRootNode(); XmlNode root = xmlParser->getRootNode();
if (std::string(root.name()) != nnSkeleton) {
printf("\nSkeleton is not a valid root: %s\n", root.name());
for (auto &a : root.children()) {
if (std::string(a.name()) == nnSkeleton) {
root = a;
break;
}
}
}
serializer.ReadSkeleton(root, skeleton); serializer.ReadSkeleton(root, skeleton);
mesh->skeleton = skeleton; mesh->skeleton = skeleton;
return true; return true;
@ -537,7 +547,7 @@ XmlParserPtr OgreXmlSerializer::OpenXmlParser(Assimp::IOSystem *pIOHandler, cons
} }
void OgreXmlSerializer::ReadSkeleton(XmlNode &node, Skeleton *skeleton) { void OgreXmlSerializer::ReadSkeleton(XmlNode &node, Skeleton *skeleton) {
if (node.name() != nnSkeleton) { if (std::string(node.name()) != nnSkeleton) {
throw DeadlyImportError("Root node is <" + std::string(node.name()) + "> expecting <skeleton>"); throw DeadlyImportError("Root node is <" + std::string(node.name()) + "> expecting <skeleton>");
} }
@ -574,14 +584,14 @@ void OgreXmlSerializer::ReadAnimations(XmlNode &node, Skeleton *skeleton) {
anim->name = ReadAttribute<std::string>(currentNode, "name"); anim->name = ReadAttribute<std::string>(currentNode, "name");
anim->length = ReadAttribute<float>(currentNode, "length"); anim->length = ReadAttribute<float>(currentNode, "length");
for (XmlNode &currentChildNode : currentNode.children()) { for (XmlNode &currentChildNode : currentNode.children()) {
const std::string currentChildName = currentNode.name(); const std::string currentChildName = currentChildNode.name();
if (currentChildName == nnTracks) { if (currentChildName == nnTracks) {
ReadAnimationTracks(currentChildNode, anim); ReadAnimationTracks(currentChildNode, anim);
skeleton->animations.push_back(anim);
} else { } else {
throw DeadlyImportError("No <tracks> found in <animation> ", anim->name); throw DeadlyImportError("No <tracks> found in <animation> ", anim->name);
} }
} }
skeleton->animations.push_back(anim);
} }
} }
} }
@ -594,14 +604,14 @@ void OgreXmlSerializer::ReadAnimationTracks(XmlNode &node, Animation *dest) {
track.type = VertexAnimationTrack::VAT_TRANSFORM; track.type = VertexAnimationTrack::VAT_TRANSFORM;
track.boneName = ReadAttribute<std::string>(currentNode, "bone"); track.boneName = ReadAttribute<std::string>(currentNode, "bone");
for (XmlNode &currentChildNode : currentNode.children()) { for (XmlNode &currentChildNode : currentNode.children()) {
const std::string currentChildName = currentNode.name(); const std::string currentChildName = currentChildNode.name();
if (currentChildName == nnKeyFrames) { if (currentChildName == nnKeyFrames) {
ReadAnimationKeyFrames(currentChildNode, dest, &track); ReadAnimationKeyFrames(currentChildNode, dest, &track);
dest->tracks.push_back(track);
} else { } else {
throw DeadlyImportError("No <keyframes> found in <track> ", dest->name); throw DeadlyImportError("No <keyframes> found in <track> ", dest->name);
} }
} }
dest->tracks.push_back(track);
} }
} }
} }
@ -614,15 +624,15 @@ void OgreXmlSerializer::ReadAnimationKeyFrames(XmlNode &node, Animation *anim, V
if (currentName == nnKeyFrame) { if (currentName == nnKeyFrame) {
keyframe.timePos = ReadAttribute<float>(currentNode, "time"); keyframe.timePos = ReadAttribute<float>(currentNode, "time");
for (XmlNode &currentChildNode : currentNode.children()) { for (XmlNode &currentChildNode : currentNode.children()) {
const std::string currentChildName = currentNode.name(); const std::string currentChildName = currentChildNode.name();
if (currentChildName == nnTranslate) { if (currentChildName == nnTranslate) {
keyframe.position.x = ReadAttribute<float>(currentChildNode, anX); keyframe.position.x = ReadAttribute<float>(currentChildNode, anX);
keyframe.position.y = ReadAttribute<float>(currentChildNode, anY); keyframe.position.y = ReadAttribute<float>(currentChildNode, anY);
keyframe.position.z = ReadAttribute<float>(currentChildNode, anZ); keyframe.position.z = ReadAttribute<float>(currentChildNode, anZ);
} else if (currentChildName == nnRotate) { } else if (currentChildName == nnRotate) {
float angle = ReadAttribute<float>(currentChildNode, "angle"); float angle = ReadAttribute<float>(currentChildNode, "angle");
for (XmlNode &currentChildChildNode : currentNode.children()) { for (XmlNode &currentChildChildNode : currentChildNode.children()) {
const std::string currentChildChildName = currentNode.name(); const std::string currentChildChildName = currentChildChildNode.name();
if (currentChildChildName == nnAxis) { if (currentChildChildName == nnAxis) {
aiVector3D axis; aiVector3D axis;
axis.x = ReadAttribute<float>(currentChildChildNode, anX); axis.x = ReadAttribute<float>(currentChildChildNode, anX);
@ -695,12 +705,12 @@ void OgreXmlSerializer::ReadBones(XmlNode &node, Skeleton *skeleton) {
bone->id = ReadAttribute<uint16_t>(currentNode, "id"); bone->id = ReadAttribute<uint16_t>(currentNode, "id");
bone->name = ReadAttribute<std::string>(currentNode, "name"); bone->name = ReadAttribute<std::string>(currentNode, "name");
for (XmlNode &currentChildNode : currentNode.children()) { for (XmlNode &currentChildNode : currentNode.children()) {
const std::string currentChildName = currentNode.name(); const std::string currentChildName = currentChildNode.name();
if (currentChildName == nnRotation) { if (currentChildName == nnPosition) {
bone->position.x = ReadAttribute<float>(currentChildNode, anX); bone->position.x = ReadAttribute<float>(currentChildNode, anX);
bone->position.y = ReadAttribute<float>(currentChildNode, anY); bone->position.y = ReadAttribute<float>(currentChildNode, anY);
bone->position.z = ReadAttribute<float>(currentChildNode, anZ); bone->position.z = ReadAttribute<float>(currentChildNode, anZ);
} else if (currentChildName == nnScale) { } else if (currentChildName == nnRotation) {
float angle = ReadAttribute<float>(currentChildNode, "angle"); float angle = ReadAttribute<float>(currentChildNode, "angle");
for (XmlNode currentChildChildNode : currentChildNode.children()) { for (XmlNode currentChildChildNode : currentChildNode.children()) {
const std::string &currentChildChildName = currentChildChildNode.name(); const std::string &currentChildChildName = currentChildChildNode.name();

View File

@ -333,7 +333,7 @@ void X3DImporter::readHead(XmlNode &node) {
} }
mScene->mMetaData = aiMetadata::Alloc(static_cast<unsigned int>(metaArray.size())); mScene->mMetaData = aiMetadata::Alloc(static_cast<unsigned int>(metaArray.size()));
unsigned int i = 0; unsigned int i = 0;
for (auto currentMeta : metaArray) { for (const auto& currentMeta : metaArray) {
mScene->mMetaData->Set(i, currentMeta.name, aiString(currentMeta.value)); mScene->mMetaData->Set(i, currentMeta.name, aiString(currentMeta.value));
++i; ++i;
} }

View File

@ -53,9 +53,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endif #endif
using namespace Assimp; using namespace Assimp;
using namespace glTFCommon;
namespace glTF { namespace glTF {
using namespace glTFCommon;
#if _MSC_VER #if _MSC_VER
#pragma warning(push) #pragma warning(push)

View File

@ -82,9 +82,21 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// clang-format on // clang-format on
using namespace Assimp; using namespace Assimp;
using namespace glTFCommon;
namespace glTF2 { namespace glTF2 {
using glTFCommon::FindStringInContext;
using glTFCommon::FindNumberInContext;
using glTFCommon::FindUIntInContext;
using glTFCommon::FindArrayInContext;
using glTFCommon::FindObjectInContext;
using glTFCommon::FindExtensionInContext;
using glTFCommon::MemberOrDefault;
using glTFCommon::ReadMember;
using glTFCommon::FindMember;
using glTFCommon::FindObject;
using glTFCommon::FindUInt;
using glTFCommon::FindArray;
using glTFCommon::FindArray;
namespace { namespace {
@ -179,11 +191,11 @@ inline bool GetAttribVector(Mesh::Primitive &p, const char *attr, Mesh::Accessor
v = &(p.attributes.texcoord); v = &(p.attributes.texcoord);
} else if ((pos = Compare(attr, "COLOR"))) { } else if ((pos = Compare(attr, "COLOR"))) {
v = &(p.attributes.color); v = &(p.attributes.color);
} else if ((pos = Compare(attr, "JOINT"))) { } else if ((pos = Compare(attr, "JOINTS"))) {
v = &(p.attributes.joint); v = &(p.attributes.joint);
} else if ((pos = Compare(attr, "JOINTMATRIX"))) { } else if ((pos = Compare(attr, "JOINTMATRIX"))) {
v = &(p.attributes.jointmatrix); v = &(p.attributes.jointmatrix);
} else if ((pos = Compare(attr, "WEIGHT"))) { } else if ((pos = Compare(attr, "WEIGHTS"))) {
v = &(p.attributes.weight); v = &(p.attributes.weight);
} else } else
return false; return false;

View File

@ -42,23 +42,23 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#if !defined(ASSIMP_BUILD_NO_GLTF_IMPORTER) && !defined(ASSIMP_BUILD_NO_GLTF2_IMPORTER) #if !defined(ASSIMP_BUILD_NO_GLTF_IMPORTER) && !defined(ASSIMP_BUILD_NO_GLTF2_IMPORTER)
#include "AssetLib/glTF2/glTF2Importer.h" #include "AssetLib/glTF2/glTF2Importer.h"
#include "PostProcessing/MakeVerboseFormat.h"
#include "AssetLib/glTF2/glTF2Asset.h" #include "AssetLib/glTF2/glTF2Asset.h"
#include "PostProcessing/MakeVerboseFormat.h"
#if !defined(ASSIMP_BUILD_NO_EXPORT) #if !defined(ASSIMP_BUILD_NO_EXPORT)
#include "AssetLib/glTF2/glTF2AssetWriter.h" #include "AssetLib/glTF2/glTF2AssetWriter.h"
#endif #endif
#include <assimp/CreateAnimMesh.h> #include <assimp/CreateAnimMesh.h>
#include <assimp/DefaultIOSystem.h>
#include <assimp/StringComparison.h> #include <assimp/StringComparison.h>
#include <assimp/StringUtils.h> #include <assimp/StringUtils.h>
#include <assimp/ai_assert.h> #include <assimp/ai_assert.h>
#include <assimp/commonMetaData.h>
#include <assimp/importerdesc.h> #include <assimp/importerdesc.h>
#include <assimp/scene.h> #include <assimp/scene.h>
#include <assimp/DefaultLogger.hpp> #include <assimp/DefaultLogger.hpp>
#include <assimp/Importer.hpp> #include <assimp/Importer.hpp>
#include <assimp/commonMetaData.h>
#include <assimp/DefaultIOSystem.h>
#include <memory> #include <memory>
#include <unordered_map> #include <unordered_map>
@ -111,7 +111,7 @@ const aiImporterDesc *glTF2Importer::GetInfo() const {
return &desc; return &desc;
} }
bool glTF2Importer::CanRead(const std::string &filename, IOSystem *pIOHandler, bool checkSig ) const { bool glTF2Importer::CanRead(const std::string &filename, IOSystem *pIOHandler, bool checkSig) const {
const std::string extension = GetExtension(filename); const std::string extension = GetExtension(filename);
if (!checkSig && (extension != "gltf") && (extension != "glb")) { if (!checkSig && (extension != "gltf") && (extension != "glb")) {
return false; return false;
@ -127,16 +127,16 @@ bool glTF2Importer::CanRead(const std::string &filename, IOSystem *pIOHandler, b
static inline aiTextureMapMode ConvertWrappingMode(SamplerWrap gltfWrapMode) { static inline aiTextureMapMode ConvertWrappingMode(SamplerWrap gltfWrapMode) {
switch (gltfWrapMode) { switch (gltfWrapMode) {
case SamplerWrap::Mirrored_Repeat: case SamplerWrap::Mirrored_Repeat:
return aiTextureMapMode_Mirror; return aiTextureMapMode_Mirror;
case SamplerWrap::Clamp_To_Edge: case SamplerWrap::Clamp_To_Edge:
return aiTextureMapMode_Clamp; return aiTextureMapMode_Clamp;
case SamplerWrap::UNSET: case SamplerWrap::UNSET:
case SamplerWrap::Repeat: case SamplerWrap::Repeat:
default: default:
return aiTextureMapMode_Wrap; return aiTextureMapMode_Wrap;
} }
} }
@ -185,8 +185,9 @@ static void SetMaterialTextureProperty(std::vector<int> &embeddedTexIdxs, Asset
// coordinate of the actual meshes during import. // coordinate of the actual meshes during import.
const ai_real rcos(cos(-transform.mRotation)); const ai_real rcos(cos(-transform.mRotation));
const ai_real rsin(sin(-transform.mRotation)); const ai_real rsin(sin(-transform.mRotation));
transform.mTranslation.x = (static_cast<ai_real>( 0.5 ) * transform.mScaling.x) * (-rcos + rsin + 1) + prop.TextureTransformExt_t.offset[0]; transform.mTranslation.x = (static_cast<ai_real>(0.5) * transform.mScaling.x) * (-rcos + rsin + 1) + prop.TextureTransformExt_t.offset[0];
transform.mTranslation.y = ((static_cast<ai_real>( 0.5 ) * transform.mScaling.y) * (rsin + rcos - 1)) + 1 - transform.mScaling.y - prop.TextureTransformExt_t.offset[1];; transform.mTranslation.y = ((static_cast<ai_real>(0.5) * transform.mScaling.y) * (rsin + rcos - 1)) + 1 - transform.mScaling.y - prop.TextureTransformExt_t.offset[1];
;
mat->AddProperty(&transform, 1, _AI_MATKEY_UVTRANSFORM_BASE, texType, texSlot); mat->AddProperty(&transform, 1, _AI_MATKEY_UVTRANSFORM_BASE, texType, texSlot);
} }
@ -259,7 +260,10 @@ static aiMaterial *ImportMaterial(std::vector<int> &embeddedTexIdxs, Asset &r, M
SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.baseColorTexture, aimat, aiTextureType_DIFFUSE); SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.baseColorTexture, aimat, aiTextureType_DIFFUSE);
SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.baseColorTexture, aimat, aiTextureType_BASE_COLOR); SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.baseColorTexture, aimat, aiTextureType_BASE_COLOR);
// Keep AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE for backwards compatibility
SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.metallicRoughnessTexture, aimat, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE); SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.metallicRoughnessTexture, aimat, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE);
SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.metallicRoughnessTexture, aimat, aiTextureType_METALNESS);
SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.metallicRoughnessTexture, aimat, aiTextureType_DIFFUSE_ROUGHNESS);
aimat->AddProperty(&mat.pbrMetallicRoughness.metallicFactor, 1, AI_MATKEY_METALLIC_FACTOR); aimat->AddProperty(&mat.pbrMetallicRoughness.metallicFactor, 1, AI_MATKEY_METALLIC_FACTOR);
aimat->AddProperty(&mat.pbrMetallicRoughness.roughnessFactor, 1, AI_MATKEY_ROUGHNESS_FACTOR); aimat->AddProperty(&mat.pbrMetallicRoughness.roughnessFactor, 1, AI_MATKEY_ROUGHNESS_FACTOR);
@ -280,7 +284,7 @@ static aiMaterial *ImportMaterial(std::vector<int> &embeddedTexIdxs, Asset &r, M
aimat->AddProperty(&alphaMode, AI_MATKEY_GLTF_ALPHAMODE); aimat->AddProperty(&alphaMode, AI_MATKEY_GLTF_ALPHAMODE);
aimat->AddProperty(&mat.alphaCutoff, 1, AI_MATKEY_GLTF_ALPHACUTOFF); aimat->AddProperty(&mat.alphaCutoff, 1, AI_MATKEY_GLTF_ALPHACUTOFF);
//pbrSpecularGlossiness // pbrSpecularGlossiness
if (mat.pbrSpecularGlossiness.isPresent) { if (mat.pbrSpecularGlossiness.isPresent) {
PbrSpecularGlossiness &pbrSG = mat.pbrSpecularGlossiness.value; PbrSpecularGlossiness &pbrSG = mat.pbrSpecularGlossiness.value;
@ -305,7 +309,6 @@ static aiMaterial *ImportMaterial(std::vector<int> &embeddedTexIdxs, Asset &r, M
aimat->AddProperty(&shadingMode, 1, AI_MATKEY_SHADING_MODEL); aimat->AddProperty(&shadingMode, 1, AI_MATKEY_SHADING_MODEL);
// KHR_materials_sheen // KHR_materials_sheen
if (mat.materialSheen.isPresent) { if (mat.materialSheen.isPresent) {
MaterialSheen &sheen = mat.materialSheen.value; MaterialSheen &sheen = mat.materialSheen.value;
@ -378,7 +381,7 @@ void glTF2Importer::ImportMaterials(Asset &r) {
} }
} }
static inline void SetFaceAndAdvance1(aiFace*& face, unsigned int numVertices, unsigned int a) { static inline void SetFaceAndAdvance1(aiFace *&face, unsigned int numVertices, unsigned int a) {
if (a >= numVertices) { if (a >= numVertices) {
return; return;
} }
@ -388,7 +391,7 @@ static inline void SetFaceAndAdvance1(aiFace*& face, unsigned int numVertices, u
++face; ++face;
} }
static inline void SetFaceAndAdvance2(aiFace*& face, unsigned int numVertices, static inline void SetFaceAndAdvance2(aiFace *&face, unsigned int numVertices,
unsigned int a, unsigned int b) { unsigned int a, unsigned int b) {
if ((a >= numVertices) || (b >= numVertices)) { if ((a >= numVertices) || (b >= numVertices)) {
return; return;
@ -400,7 +403,7 @@ static inline void SetFaceAndAdvance2(aiFace*& face, unsigned int numVertices,
++face; ++face;
} }
static inline void SetFaceAndAdvance3(aiFace*& face, unsigned int numVertices, unsigned int a, static inline void SetFaceAndAdvance3(aiFace *&face, unsigned int numVertices, unsigned int a,
unsigned int b, unsigned int c) { unsigned int b, unsigned int c) {
if ((a >= numVertices) || (b >= numVertices) || (c >= numVertices)) { if ((a >= numVertices) || (b >= numVertices) || (c >= numVertices)) {
return; return;
@ -427,17 +430,16 @@ static inline bool CheckValidFacesIndices(aiFace *faces, unsigned nFaces, unsign
} }
#endif // ASSIMP_BUILD_DEBUG #endif // ASSIMP_BUILD_DEBUG
template<typename T> template <typename T>
aiColor4D* GetVertexColorsForType(Ref<Accessor> input) { aiColor4D *GetVertexColorsForType(Ref<Accessor> input) {
constexpr float max = std::numeric_limits<T>::max(); constexpr float max = std::numeric_limits<T>::max();
aiColor4t<T>* colors; aiColor4t<T> *colors;
input->ExtractData(colors); input->ExtractData(colors);
auto output = new aiColor4D[input->count]; auto output = new aiColor4D[input->count];
for (size_t i = 0; i < input->count; i++) { for (size_t i = 0; i < input->count; i++) {
output[i] = aiColor4D( output[i] = aiColor4D(
colors[i].r / max, colors[i].g / max, colors[i].r / max, colors[i].g / max,
colors[i].b / max, colors[i].a / max colors[i].b / max, colors[i].a / max);
);
} }
delete[] colors; delete[] colors;
return output; return output;
@ -471,21 +473,21 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
} }
switch (prim.mode) { switch (prim.mode) {
case PrimitiveMode_POINTS: case PrimitiveMode_POINTS:
aim->mPrimitiveTypes |= aiPrimitiveType_POINT; aim->mPrimitiveTypes |= aiPrimitiveType_POINT;
break; break;
case PrimitiveMode_LINES: case PrimitiveMode_LINES:
case PrimitiveMode_LINE_LOOP: case PrimitiveMode_LINE_LOOP:
case PrimitiveMode_LINE_STRIP: case PrimitiveMode_LINE_STRIP:
aim->mPrimitiveTypes |= aiPrimitiveType_LINE; aim->mPrimitiveTypes |= aiPrimitiveType_LINE;
break; break;
case PrimitiveMode_TRIANGLES: case PrimitiveMode_TRIANGLES:
case PrimitiveMode_TRIANGLE_STRIP: case PrimitiveMode_TRIANGLE_STRIP:
case PrimitiveMode_TRIANGLE_FAN: case PrimitiveMode_TRIANGLE_FAN:
aim->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE; aim->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
break; break;
} }
Mesh::Primitive::Attributes &attr = prim.attributes; Mesh::Primitive::Attributes &attr = prim.attributes;
@ -528,7 +530,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
for (size_t c = 0; c < attr.color.size() && c < AI_MAX_NUMBER_OF_COLOR_SETS; ++c) { for (size_t c = 0; c < attr.color.size() && c < AI_MAX_NUMBER_OF_COLOR_SETS; ++c) {
if (attr.color[c]->count != aim->mNumVertices) { if (attr.color[c]->count != aim->mNumVertices) {
DefaultLogger::get()->warn("Color stream size in mesh \"", mesh.name, DefaultLogger::get()->warn("Color stream size in mesh \"", mesh.name,
"\" does not match the vertex count"); "\" does not match the vertex count");
continue; continue;
} }
@ -551,7 +553,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
if (attr.texcoord[tc]->count != aim->mNumVertices) { if (attr.texcoord[tc]->count != aim->mNumVertices) {
DefaultLogger::get()->warn("Texcoord stream size in mesh \"", mesh.name, DefaultLogger::get()->warn("Texcoord stream size in mesh \"", mesh.name,
"\" does not match the vertex count"); "\" does not match the vertex count");
continue; continue;
} }
@ -604,7 +606,10 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
} }
} }
if (needTangents) { if (needTangents) {
if (target.tangent[0]->count != aim->mNumVertices) { if (!aiAnimMesh.HasNormals()) {
// prevent nullptr access to aiAnimMesh.mNormals below when no normals are available
ASSIMP_LOG_WARN("Bitangents of target ", i, " in mesh \"", mesh.name, "\" can't be computed, because mesh has no normals.");
} else if (target.tangent[0]->count != aim->mNumVertices) {
ASSIMP_LOG_WARN("Tangents of target ", i, " in mesh \"", mesh.name, "\" does not match the vertex count"); ASSIMP_LOG_WARN("Tangents of target ", i, " in mesh \"", mesh.name, "\" does not match the vertex count");
} else { } else {
Tangent *tangent = nullptr; Tangent *tangent = nullptr;
@ -644,77 +649,77 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
} }
switch (prim.mode) { switch (prim.mode) {
case PrimitiveMode_POINTS: { case PrimitiveMode_POINTS: {
nFaces = count; nFaces = count;
facePtr = faces = new aiFace[nFaces]; facePtr = faces = new aiFace[nFaces];
for (unsigned int i = 0; i < count; ++i) { for (unsigned int i = 0; i < count; ++i) {
SetFaceAndAdvance1(facePtr, aim->mNumVertices, data.GetUInt(i)); SetFaceAndAdvance1(facePtr, aim->mNumVertices, data.GetUInt(i));
}
break;
} }
break;
}
case PrimitiveMode_LINES: { case PrimitiveMode_LINES: {
nFaces = count / 2; nFaces = count / 2;
if (nFaces * 2 != count) { if (nFaces * 2 != count) {
ASSIMP_LOG_WARN("The number of vertices was not compatible with the LINES mode. Some vertices were dropped."); ASSIMP_LOG_WARN("The number of vertices was not compatible with the LINES mode. Some vertices were dropped.");
count = nFaces * 2; count = nFaces * 2;
}
facePtr = faces = new aiFace[nFaces];
for (unsigned int i = 0; i < count; i += 2) {
SetFaceAndAdvance2(facePtr, aim->mNumVertices, data.GetUInt(i), data.GetUInt(i + 1));
}
break;
} }
facePtr = faces = new aiFace[nFaces];
case PrimitiveMode_LINE_LOOP: for (unsigned int i = 0; i < count; i += 2) {
case PrimitiveMode_LINE_STRIP: { SetFaceAndAdvance2(facePtr, aim->mNumVertices, data.GetUInt(i), data.GetUInt(i + 1));
nFaces = count - ((prim.mode == PrimitiveMode_LINE_STRIP) ? 1 : 0);
facePtr = faces = new aiFace[nFaces];
SetFaceAndAdvance2(facePtr, aim->mNumVertices, data.GetUInt(0), data.GetUInt(1));
for (unsigned int i = 2; i < count; ++i) {
SetFaceAndAdvance2(facePtr, aim->mNumVertices, data.GetUInt(i - 1), data.GetUInt(i));
}
if (prim.mode == PrimitiveMode_LINE_LOOP) { // close the loop
SetFaceAndAdvance2(facePtr, aim->mNumVertices, data.GetUInt(static_cast<int>(count) - 1), faces[0].mIndices[0]);
}
break;
} }
break;
}
case PrimitiveMode_TRIANGLES: { case PrimitiveMode_LINE_LOOP:
nFaces = count / 3; case PrimitiveMode_LINE_STRIP: {
if (nFaces * 3 != count) { nFaces = count - ((prim.mode == PrimitiveMode_LINE_STRIP) ? 1 : 0);
ASSIMP_LOG_WARN("The number of vertices was not compatible with the TRIANGLES mode. Some vertices were dropped."); facePtr = faces = new aiFace[nFaces];
count = nFaces * 3; SetFaceAndAdvance2(facePtr, aim->mNumVertices, data.GetUInt(0), data.GetUInt(1));
} for (unsigned int i = 2; i < count; ++i) {
facePtr = faces = new aiFace[nFaces]; SetFaceAndAdvance2(facePtr, aim->mNumVertices, data.GetUInt(i - 1), data.GetUInt(i));
for (unsigned int i = 0; i < count; i += 3) { }
if (prim.mode == PrimitiveMode_LINE_LOOP) { // close the loop
SetFaceAndAdvance2(facePtr, aim->mNumVertices, data.GetUInt(static_cast<int>(count) - 1), faces[0].mIndices[0]);
}
break;
}
case PrimitiveMode_TRIANGLES: {
nFaces = count / 3;
if (nFaces * 3 != count) {
ASSIMP_LOG_WARN("The number of vertices was not compatible with the TRIANGLES mode. Some vertices were dropped.");
count = nFaces * 3;
}
facePtr = faces = new aiFace[nFaces];
for (unsigned int i = 0; i < count; i += 3) {
SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(i), data.GetUInt(i + 1), data.GetUInt(i + 2));
}
break;
}
case PrimitiveMode_TRIANGLE_STRIP: {
nFaces = count - 2;
facePtr = faces = new aiFace[nFaces];
for (unsigned int i = 0; i < nFaces; ++i) {
// The ordering is to ensure that the triangles are all drawn with the same orientation
if ((i + 1) % 2 == 0) {
// For even n, vertices n + 1, n, and n + 2 define triangle n
SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(i + 1), data.GetUInt(i), data.GetUInt(i + 2));
} else {
// For odd n, vertices n, n+1, and n+2 define triangle n
SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(i), data.GetUInt(i + 1), data.GetUInt(i + 2)); SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(i), data.GetUInt(i + 1), data.GetUInt(i + 2));
} }
break;
} }
case PrimitiveMode_TRIANGLE_STRIP: { break;
nFaces = count - 2; }
facePtr = faces = new aiFace[nFaces]; case PrimitiveMode_TRIANGLE_FAN:
for (unsigned int i = 0; i < nFaces; ++i) { nFaces = count - 2;
//The ordering is to ensure that the triangles are all drawn with the same orientation facePtr = faces = new aiFace[nFaces];
if ((i + 1) % 2 == 0) { SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(0), data.GetUInt(1), data.GetUInt(2));
//For even n, vertices n + 1, n, and n + 2 define triangle n for (unsigned int i = 1; i < nFaces; ++i) {
SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(i + 1), data.GetUInt(i), data.GetUInt(i + 2)); SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(0), data.GetUInt(i + 1), data.GetUInt(i + 2));
} else {
//For odd n, vertices n, n+1, and n+2 define triangle n
SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(i), data.GetUInt(i + 1), data.GetUInt(i + 2));
}
}
break;
} }
case PrimitiveMode_TRIANGLE_FAN: break;
nFaces = count - 2;
facePtr = faces = new aiFace[nFaces];
SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(0), data.GetUInt(1), data.GetUInt(2));
for (unsigned int i = 1; i < nFaces; ++i) {
SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(0), data.GetUInt(i + 1), data.GetUInt(i + 2));
}
break;
} }
} else { // no indices provided so directly generate from counts } else { // no indices provided so directly generate from counts
@ -722,77 +727,77 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
unsigned int count = aim->mNumVertices; unsigned int count = aim->mNumVertices;
switch (prim.mode) { switch (prim.mode) {
case PrimitiveMode_POINTS: { case PrimitiveMode_POINTS: {
nFaces = count; nFaces = count;
facePtr = faces = new aiFace[nFaces]; facePtr = faces = new aiFace[nFaces];
for (unsigned int i = 0; i < count; ++i) { for (unsigned int i = 0; i < count; ++i) {
SetFaceAndAdvance1(facePtr, aim->mNumVertices, i); SetFaceAndAdvance1(facePtr, aim->mNumVertices, i);
}
break;
} }
break;
}
case PrimitiveMode_LINES: { case PrimitiveMode_LINES: {
nFaces = count / 2; nFaces = count / 2;
if (nFaces * 2 != count) { if (nFaces * 2 != count) {
ASSIMP_LOG_WARN("The number of vertices was not compatible with the LINES mode. Some vertices were dropped."); ASSIMP_LOG_WARN("The number of vertices was not compatible with the LINES mode. Some vertices were dropped.");
count = (unsigned int)nFaces * 2; count = (unsigned int)nFaces * 2;
}
facePtr = faces = new aiFace[nFaces];
for (unsigned int i = 0; i < count; i += 2) {
SetFaceAndAdvance2(facePtr, aim->mNumVertices, i, i + 1);
}
break;
} }
facePtr = faces = new aiFace[nFaces];
case PrimitiveMode_LINE_LOOP: for (unsigned int i = 0; i < count; i += 2) {
case PrimitiveMode_LINE_STRIP: { SetFaceAndAdvance2(facePtr, aim->mNumVertices, i, i + 1);
nFaces = count - ((prim.mode == PrimitiveMode_LINE_STRIP) ? 1 : 0);
facePtr = faces = new aiFace[nFaces];
SetFaceAndAdvance2(facePtr, aim->mNumVertices, 0, 1);
for (unsigned int i = 2; i < count; ++i) {
SetFaceAndAdvance2(facePtr, aim->mNumVertices, i - 1, i);
}
if (prim.mode == PrimitiveMode_LINE_LOOP) { // close the loop
SetFaceAndAdvance2(facePtr, aim->mNumVertices, count - 1, 0);
}
break;
} }
break;
}
case PrimitiveMode_TRIANGLES: { case PrimitiveMode_LINE_LOOP:
nFaces = count / 3; case PrimitiveMode_LINE_STRIP: {
if (nFaces * 3 != count) { nFaces = count - ((prim.mode == PrimitiveMode_LINE_STRIP) ? 1 : 0);
ASSIMP_LOG_WARN("The number of vertices was not compatible with the TRIANGLES mode. Some vertices were dropped."); facePtr = faces = new aiFace[nFaces];
count = (unsigned int)nFaces * 3; SetFaceAndAdvance2(facePtr, aim->mNumVertices, 0, 1);
} for (unsigned int i = 2; i < count; ++i) {
facePtr = faces = new aiFace[nFaces]; SetFaceAndAdvance2(facePtr, aim->mNumVertices, i - 1, i);
for (unsigned int i = 0; i < count; i += 3) { }
if (prim.mode == PrimitiveMode_LINE_LOOP) { // close the loop
SetFaceAndAdvance2(facePtr, aim->mNumVertices, count - 1, 0);
}
break;
}
case PrimitiveMode_TRIANGLES: {
nFaces = count / 3;
if (nFaces * 3 != count) {
ASSIMP_LOG_WARN("The number of vertices was not compatible with the TRIANGLES mode. Some vertices were dropped.");
count = (unsigned int)nFaces * 3;
}
facePtr = faces = new aiFace[nFaces];
for (unsigned int i = 0; i < count; i += 3) {
SetFaceAndAdvance3(facePtr, aim->mNumVertices, i, i + 1, i + 2);
}
break;
}
case PrimitiveMode_TRIANGLE_STRIP: {
nFaces = count - 2;
facePtr = faces = new aiFace[nFaces];
for (unsigned int i = 0; i < nFaces; ++i) {
// The ordering is to ensure that the triangles are all drawn with the same orientation
if ((i + 1) % 2 == 0) {
// For even n, vertices n + 1, n, and n + 2 define triangle n
SetFaceAndAdvance3(facePtr, aim->mNumVertices, i + 1, i, i + 2);
} else {
// For odd n, vertices n, n+1, and n+2 define triangle n
SetFaceAndAdvance3(facePtr, aim->mNumVertices, i, i + 1, i + 2); SetFaceAndAdvance3(facePtr, aim->mNumVertices, i, i + 1, i + 2);
} }
break;
} }
case PrimitiveMode_TRIANGLE_STRIP: { break;
nFaces = count - 2; }
facePtr = faces = new aiFace[nFaces]; case PrimitiveMode_TRIANGLE_FAN:
for (unsigned int i = 0; i < nFaces; ++i) { nFaces = count - 2;
//The ordering is to ensure that the triangles are all drawn with the same orientation facePtr = faces = new aiFace[nFaces];
if ((i + 1) % 2 == 0) { SetFaceAndAdvance3(facePtr, aim->mNumVertices, 0, 1, 2);
//For even n, vertices n + 1, n, and n + 2 define triangle n for (unsigned int i = 1; i < nFaces; ++i) {
SetFaceAndAdvance3(facePtr, aim->mNumVertices, i + 1, i, i + 2); SetFaceAndAdvance3(facePtr, aim->mNumVertices, 0, i + 1, i + 2);
} else {
//For odd n, vertices n, n+1, and n+2 define triangle n
SetFaceAndAdvance3(facePtr, aim->mNumVertices, i, i + 1, i + 2);
}
}
break;
} }
case PrimitiveMode_TRIANGLE_FAN: break;
nFaces = count - 2;
facePtr = faces = new aiFace[nFaces];
SetFaceAndAdvance3(facePtr, aim->mNumVertices, 0, 1, 2);
for (unsigned int i = 1; i < nFaces; ++i) {
SetFaceAndAdvance3(facePtr, aim->mNumVertices, 0, i + 1, i + 2);
}
break;
} }
} }
@ -876,15 +881,15 @@ void glTF2Importer::ImportLights(glTF2::Asset &r) {
aiLight *ail = mScene->mLights[i] = new aiLight(); aiLight *ail = mScene->mLights[i] = new aiLight();
switch (light.type) { switch (light.type) {
case Light::Directional: case Light::Directional:
ail->mType = aiLightSource_DIRECTIONAL; ail->mType = aiLightSource_DIRECTIONAL;
break; break;
case Light::Point: case Light::Point:
ail->mType = aiLightSource_POINT; ail->mType = aiLightSource_POINT;
break; break;
case Light::Spot: case Light::Spot:
ail->mType = aiLightSource_SPOT; ail->mType = aiLightSource_SPOT;
break; break;
} }
if (ail->mType != aiLightSource_POINT) { if (ail->mType != aiLightSource_POINT) {
@ -902,14 +907,14 @@ void glTF2Importer::ImportLights(glTF2::Asset &r) {
ail->mAttenuationLinear = 0.0; ail->mAttenuationLinear = 0.0;
ail->mAttenuationQuadratic = 0.0; ail->mAttenuationQuadratic = 0.0;
} else { } else {
//in PBR attenuation is calculated using inverse square law which can be expressed // in PBR attenuation is calculated using inverse square law which can be expressed
//using assimps equation: 1/(att0 + att1 * d + att2 * d*d) with the following parameters // using assimps equation: 1/(att0 + att1 * d + att2 * d*d) with the following parameters
//this is correct equation for the case when range (see // this is correct equation for the case when range (see
//https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual) // https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual)
//is not present. When range is not present it is assumed that it is infinite and so numerator is 1. // is not present. When range is not present it is assumed that it is infinite and so numerator is 1.
//When range is present then numerator might be any value in range [0,1] and then assimps equation // When range is present then numerator might be any value in range [0,1] and then assimps equation
//will not suffice. In this case range is added into metadata in ImportNode function // will not suffice. In this case range is added into metadata in ImportNode function
//and its up to implementation to read it when it wants to // and its up to implementation to read it when it wants to
ail->mAttenuationConstant = 0.0; ail->mAttenuationConstant = 0.0;
ail->mAttenuationLinear = 0.0; ail->mAttenuationLinear = 0.0;
ail->mAttenuationQuadratic = 1.0; ail->mAttenuationQuadratic = 1.0;
@ -1021,7 +1026,7 @@ void ParseExtensions(aiMetadata *metadata, const CustomExtension &extension) {
metadata->Add(extension.name, extension.mBoolValue.value); metadata->Add(extension.name, extension.mBoolValue.value);
} else if (extension.mValues.isPresent) { } else if (extension.mValues.isPresent) {
aiMetadata val; aiMetadata val;
for (auto const & subExtension : extension.mValues.value) { for (auto const &subExtension : extension.mValues.value) {
ParseExtensions(&val, subExtension); ParseExtensions(&val, subExtension);
} }
metadata->Add(extension.name, val); metadata->Add(extension.name, val);
@ -1030,7 +1035,7 @@ void ParseExtensions(aiMetadata *metadata, const CustomExtension &extension) {
void ParseExtras(aiMetadata *metadata, const CustomExtension &extension) { void ParseExtras(aiMetadata *metadata, const CustomExtension &extension) {
if (extension.mValues.isPresent) { if (extension.mValues.isPresent) {
for (auto const & subExtension : extension.mValues.value) { for (auto const &subExtension : extension.mValues.value) {
ParseExtensions(metadata, subExtension); ParseExtensions(metadata, subExtension);
} }
} }
@ -1068,11 +1073,10 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector<unsigned int> &
if (!node.meshes.empty()) { if (!node.meshes.empty()) {
// GLTF files contain at most 1 mesh per node. // GLTF files contain at most 1 mesh per node.
if (node.meshes.size() > 1) if (node.meshes.size() > 1) {
{
throw DeadlyImportError("GLTF: Invalid input, found ", node.meshes.size(), throw DeadlyImportError("GLTF: Invalid input, found ", node.meshes.size(),
" meshes in ", getContextForErrorMessages(node.id, node.name), " meshes in ", getContextForErrorMessages(node.id, node.name),
", but only 1 mesh per node allowed."); ", but only 1 mesh per node allowed.");
} }
int mesh_idx = node.meshes[0].GetIndex(); int mesh_idx = node.meshes[0].GetIndex();
int count = meshOffsets[mesh_idx + 1] - meshOffsets[mesh_idx]; int count = meshOffsets[mesh_idx + 1] - meshOffsets[mesh_idx];
@ -1083,7 +1087,7 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector<unsigned int> &
if (node.skin) { if (node.skin) {
for (int primitiveNo = 0; primitiveNo < count; ++primitiveNo) { for (int primitiveNo = 0; primitiveNo < count; ++primitiveNo) {
aiMesh *mesh = pScene->mMeshes[meshOffsets[mesh_idx] + primitiveNo]; aiMesh *mesh = pScene->mMeshes[meshOffsets[mesh_idx] + primitiveNo];
unsigned int numBones =static_cast<unsigned int>(node.skin->jointNames.size()); unsigned int numBones = static_cast<unsigned int>(node.skin->jointNames.size());
std::vector<std::vector<aiVertexWeight>> weighting(numBones); std::vector<std::vector<aiVertexWeight>> weighting(numBones);
BuildVertexWeightMapping(node.meshes[0]->primitives[primitiveNo], weighting); BuildVertexWeightMapping(node.meshes[0]->primitives[primitiveNo], weighting);
@ -1160,8 +1164,8 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector<unsigned int> &
if (node.light) { if (node.light) {
pScene->mLights[node.light.GetIndex()]->mName = ainode->mName; pScene->mLights[node.light.GetIndex()]->mName = ainode->mName;
//range is optional - see https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual // range is optional - see https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual
//it is added to meta data of parent node, because there is no other place to put it // it is added to meta data of parent node, because there is no other place to put it
if (node.light->range.isPresent) { if (node.light->range.isPresent) {
if (!ainode->mMetaData) { if (!ainode->mMetaData) {
ainode->mMetaData = aiMetadata::Alloc(1); ainode->mMetaData = aiMetadata::Alloc(1);
@ -1222,7 +1226,7 @@ struct AnimationSamplers {
Animation::Sampler *weight; Animation::Sampler *weight;
}; };
aiNodeAnim *CreateNodeAnim(glTF2::Asset&, Node &node, AnimationSamplers &samplers) { aiNodeAnim *CreateNodeAnim(glTF2::Asset &, Node &node, AnimationSamplers &samplers) {
aiNodeAnim *anim = new aiNodeAnim(); aiNodeAnim *anim = new aiNodeAnim();
try { try {
@ -1313,7 +1317,7 @@ aiNodeAnim *CreateNodeAnim(glTF2::Asset&, Node &node, AnimationSamplers &sampler
} }
} }
aiMeshMorphAnim *CreateMeshMorphAnim(glTF2::Asset&, Node &node, AnimationSamplers &samplers) { aiMeshMorphAnim *CreateMeshMorphAnim(glTF2::Asset &, Node &node, AnimationSamplers &samplers) {
auto *anim = new aiMeshMorphAnim(); auto *anim = new aiMeshMorphAnim();
try { try {
@ -1366,7 +1370,7 @@ std::unordered_map<unsigned int, AnimationSamplers> GatherSamplers(Animation &an
continue; continue;
} }
auto& animsampler = anim.samplers[channel.sampler]; auto &animsampler = anim.samplers[channel.sampler];
if (!animsampler.input) { if (!animsampler.input) {
ASSIMP_LOG_WARN("Animation ", anim.name, ": Missing sampler input. Skipping."); ASSIMP_LOG_WARN("Animation ", anim.name, ": Missing sampler input. Skipping.");
@ -1555,9 +1559,9 @@ void glTF2Importer::ImportEmbeddedTextures(glTF2::Asset &r) {
if (ext) { if (ext) {
if (strcmp(ext, "jpeg") == 0) { if (strcmp(ext, "jpeg") == 0) {
ext = "jpg"; ext = "jpg";
} else if(strcmp(ext, "ktx2") == 0) { //basisu: ktx remains } else if (strcmp(ext, "ktx2") == 0) { // basisu: ktx remains
ext = "kx2"; ext = "kx2";
} else if(strcmp(ext, "basis") == 0) { //basisu } else if (strcmp(ext, "basis") == 0) { // basisu
ext = "bu"; ext = "bu";
} }
@ -1570,7 +1574,7 @@ void glTF2Importer::ImportEmbeddedTextures(glTF2::Asset &r) {
} }
} }
void glTF2Importer::ImportCommonMetadata(glTF2::Asset& a) { void glTF2Importer::ImportCommonMetadata(glTF2::Asset &a) {
ASSIMP_LOG_DEBUG("Importing metadata"); ASSIMP_LOG_DEBUG("Importing metadata");
ai_assert(mScene->mMetaData == nullptr); ai_assert(mScene->mMetaData == nullptr);
const bool hasVersion = !a.asset.version.empty(); const bool hasVersion = !a.asset.version.empty();
@ -1604,7 +1608,7 @@ void glTF2Importer::InternReadFile(const std::string &pFile, aiScene *pScene, IO
this->mScene = pScene; this->mScene = pScene;
// read the asset file // read the asset file
glTF2::Asset asset(pIOHandler, static_cast<rapidjson::IRemoteSchemaDocumentProvider*>(mSchemaDocumentProvider)); glTF2::Asset asset(pIOHandler, static_cast<rapidjson::IRemoteSchemaDocumentProvider *>(mSchemaDocumentProvider));
asset.Load(pFile, GetExtension(pFile) == "glb"); asset.Load(pFile, GetExtension(pFile) == "glb");
if (asset.scene) { if (asset.scene) {
pScene->mName = asset.scene->name; pScene->mName = asset.scene->name;
@ -1631,7 +1635,7 @@ void glTF2Importer::InternReadFile(const std::string &pFile, aiScene *pScene, IO
} }
void glTF2Importer::SetupProperties(const Importer *pImp) { void glTF2Importer::SetupProperties(const Importer *pImp) {
mSchemaDocumentProvider = static_cast<rapidjson::IRemoteSchemaDocumentProvider*>(pImp->GetPropertyPointer(AI_CONFIG_IMPORT_SCHEMA_DOCUMENT_PROVIDER)); mSchemaDocumentProvider = static_cast<rapidjson::IRemoteSchemaDocumentProvider *>(pImp->GetPropertyPointer(AI_CONFIG_IMPORT_SCHEMA_DOCUMENT_PROVIDER));
} }
#endif // ASSIMP_BUILD_NO_GLTF_IMPORTER #endif // ASSIMP_BUILD_NO_GLTF_IMPORTER

View File

@ -78,6 +78,7 @@ SET( PUBLIC_HEADERS
${HEADER_PATH}/matrix4x4.h ${HEADER_PATH}/matrix4x4.h
${HEADER_PATH}/matrix4x4.inl ${HEADER_PATH}/matrix4x4.inl
${HEADER_PATH}/mesh.h ${HEADER_PATH}/mesh.h
${HEADER_PATH}/ObjMaterial.h
${HEADER_PATH}/pbrmaterial.h ${HEADER_PATH}/pbrmaterial.h
${HEADER_PATH}/GltfMaterial.h ${HEADER_PATH}/GltfMaterial.h
${HEADER_PATH}/postprocess.h ${HEADER_PATH}/postprocess.h
@ -149,7 +150,7 @@ SET( Core_SRCS
Common/Assimp.cpp Common/Assimp.cpp
) )
IF(MSVC) IF(MSVC OR (MINGW AND BUILD_SHARED_LIBS))
list(APPEND Core_SRCS "res/assimp.rc") list(APPEND Core_SRCS "res/assimp.rc")
ENDIF() ENDIF()
@ -166,6 +167,7 @@ SET( Logging_SRCS
SOURCE_GROUP(Logging FILES ${Logging_SRCS}) SOURCE_GROUP(Logging FILES ${Logging_SRCS})
SET( Common_SRCS SET( Common_SRCS
Common/StbCommon.h
Common/Compression.cpp Common/Compression.cpp
Common/Compression.h Common/Compression.h
Common/BaseImporter.cpp Common/BaseImporter.cpp
@ -181,6 +183,7 @@ SET( Common_SRCS
Common/DefaultIOSystem.cpp Common/DefaultIOSystem.cpp
Common/ZipArchiveIOSystem.cpp Common/ZipArchiveIOSystem.cpp
Common/PolyTools.h Common/PolyTools.h
Common/Maybe.h
Common/Importer.cpp Common/Importer.cpp
Common/IFF.h Common/IFF.h
Common/SGSpatialSort.cpp Common/SGSpatialSort.cpp
@ -1052,7 +1055,9 @@ ENDIF()
# Check dependencies for glTF importer with Open3DGC-compression. # Check dependencies for glTF importer with Open3DGC-compression.
# RT-extensions is used in "contrib/Open3DGC/o3dgcTimer.h" for collecting statistics. Pointed file # RT-extensions is used in "contrib/Open3DGC/o3dgcTimer.h" for collecting statistics. Pointed file
# has implementation for different platforms: WIN32, __MACH__ and other ("else" block). # has implementation for different platforms: WIN32, __MACH__ and other ("else" block).
FIND_PACKAGE(RT QUIET) IF (NOT WIN32)
FIND_PACKAGE(RT QUIET)
ENDIF ()
IF (NOT ASSIMP_HUNTER_ENABLED AND (RT_FOUND OR WIN32)) IF (NOT ASSIMP_HUNTER_ENABLED AND (RT_FOUND OR WIN32))
SET( ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC 1 ) SET( ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC 1 )
ADD_DEFINITIONS( -DASSIMP_IMPORTER_GLTF_USE_OPEN3DGC=1 ) ADD_DEFINITIONS( -DASSIMP_IMPORTER_GLTF_USE_OPEN3DGC=1 )
@ -1095,8 +1100,6 @@ if(MSVC10)
endif() endif()
endif() endif()
ADD_DEFINITIONS( -DASSIMP_BUILD_DLL_EXPORT )
IF( MSVC OR "${CMAKE_CXX_SIMULATE_ID}" MATCHES "MSVC") # clang with MSVC ABI IF( MSVC OR "${CMAKE_CXX_SIMULATE_ID}" MATCHES "MSVC") # clang with MSVC ABI
ADD_DEFINITIONS( -D_SCL_SECURE_NO_WARNINGS ) ADD_DEFINITIONS( -D_SCL_SECURE_NO_WARNINGS )
ADD_DEFINITIONS( -D_CRT_SECURE_NO_WARNINGS ) ADD_DEFINITIONS( -D_CRT_SECURE_NO_WARNINGS )
@ -1173,13 +1176,22 @@ ENDIF()
ADD_LIBRARY( assimp ${assimp_src} ) ADD_LIBRARY( assimp ${assimp_src} )
ADD_LIBRARY(assimp::assimp ALIAS assimp) ADD_LIBRARY(assimp::assimp ALIAS assimp)
# Add or remove dllexport tags depending on the library type.
IF (BUILD_SHARED_LIBS)
TARGET_COMPILE_DEFINITIONS(assimp PRIVATE ASSIMP_BUILD_DLL_EXPORT)
ELSE ()
TARGET_COMPILE_DEFINITIONS(assimp PRIVATE OPENDDL_STATIC_LIBARY)
ENDIF ()
TARGET_USE_COMMON_OUTPUT_DIRECTORY(assimp) TARGET_USE_COMMON_OUTPUT_DIRECTORY(assimp)
# enable warnings as errors ######################################## IF (ASSIMP_WARNINGS_AS_ERRORS)
IF (MSVC) MESSAGE(STATUS "Treating all warnings as errors (for assimp library only)")
TARGET_COMPILE_OPTIONS(assimp PRIVATE /WX) IF (MSVC)
ELSE() TARGET_COMPILE_OPTIONS(assimp PRIVATE /W4 /WX)
TARGET_COMPILE_OPTIONS(assimp PRIVATE -Werror) ELSE()
TARGET_COMPILE_OPTIONS(assimp PRIVATE -Wall -Werror)
ENDIF()
ENDIF() ENDIF()
# adds C_FLAGS required to compile zip.c on old GCC 4.x compiler # adds C_FLAGS required to compile zip.c on old GCC 4.x compiler
@ -1262,6 +1274,16 @@ if( MSVC )
set(LIBRARY_SUFFIX "${ASSIMP_LIBRARY_SUFFIX}-${MSVC_PREFIX}-mt" CACHE STRING "the suffix for the assimp windows library") set(LIBRARY_SUFFIX "${ASSIMP_LIBRARY_SUFFIX}-${MSVC_PREFIX}-mt" CACHE STRING "the suffix for the assimp windows library")
endif() endif()
if (MINGW)
set(LIBRARY_SUFFIX "-${ASSIMP_SOVERSION}" CACHE STRING "the suffix for the assimp MinGW shared library")
SET_TARGET_PROPERTIES( assimp PROPERTIES
ARCHIVE_OUTPUT_NAME assimp
)
if (NOT BUILD_SHARED_LIBS)
TARGET_LINK_LIBRARIES ( assimp -static-libgcc -static-libstdc++ -Wl,-Bstatic -lstdc++ -lwinpthread ) # winpthread is for libminizip.
endif ()
endif()
if (${CMAKE_SYSTEM_NAME} MATCHES "WindowsStore") if (${CMAKE_SYSTEM_NAME} MATCHES "WindowsStore")
target_compile_definitions(assimp PUBLIC WindowsStore) target_compile_definitions(assimp PUBLIC WindowsStore)
TARGET_LINK_LIBRARIES(assimp advapi32) TARGET_LINK_LIBRARIES(assimp advapi32)
@ -1273,12 +1295,6 @@ SET_TARGET_PROPERTIES( assimp PROPERTIES
OUTPUT_NAME assimp${LIBRARY_SUFFIX} OUTPUT_NAME assimp${LIBRARY_SUFFIX}
) )
if (WIN32 AND CMAKE_COMPILER_IS_GNUCXX AND BUILD_SHARED_LIBS)
set_target_properties(assimp PROPERTIES
SUFFIX "-${ASSIMP_SOVERSION}${CMAKE_SHARED_LIBRARY_SUFFIX}"
)
endif()
if (APPLE) if (APPLE)
if (ASSIMP_BUILD_FRAMEWORK) if (ASSIMP_BUILD_FRAMEWORK)
SET_TARGET_PROPERTIES( assimp PROPERTIES SET_TARGET_PROPERTIES( assimp PROPERTIES

View File

@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2022, assimp team Copyright (c) 2006-2022, assimp team
All rights reserved. All rights reserved.
Redistribution and use of this software in source and binary forms, Redistribution and use of this software in source and binary forms,
@ -1282,18 +1280,13 @@ ASSIMP_API void aiQuaternionInterpolate(
# define STBI_ONLY_PNG # define STBI_ONLY_PNG
#endif #endif
// Ensure all symbols are linked correctly
#if ASSIMP_NEEDS_STB_IMAGE #if ASSIMP_NEEDS_STB_IMAGE
// Share stb_image's PNG loader with other importers/exporters instead of bringing our own copy.
# if _MSC_VER // "unreferenced function has been removed" (SSE2 detection routine in x64 builds) # define STBI_ONLY_PNG
# pragma warning(push) # ifdef ASSIMP_USE_STB_IMAGE_STATIC
# pragma warning(disable: 4505) # define STB_IMAGE_STATIC
# endif # endif
# define STB_IMAGE_IMPLEMENTATION # define STB_IMAGE_IMPLEMENTATION
# include "stb/stb_image.h" # include "Common/StbCommon.h"
# if _MSC_VER
# pragma warning(pop)
# endif
#endif #endif

View File

@ -63,7 +63,7 @@ inline int select_fseek(FILE *file, int64_t offset, int origin) {
#if defined _WIN32 && (!defined __GNUC__ || __MSVCRT_VERSION__ >= 0x0601) #if defined _WIN64 && (!defined __GNUC__ || __MSVCRT_VERSION__ >= 0x0601)
template <> template <>
inline size_t select_ftell<8>(FILE *file) { inline size_t select_ftell<8>(FILE *file) {
return (size_t)::_ftelli64(file); return (size_t)::_ftelli64(file);
@ -149,7 +149,7 @@ size_t DefaultIOStream::FileSize() const {
// //
// See here for details: // See here for details:
// https://www.securecoding.cert.org/confluence/display/seccode/FIO19-C.+Do+not+use+fseek()+and+ftell()+to+compute+the+size+of+a+regular+file // https://www.securecoding.cert.org/confluence/display/seccode/FIO19-C.+Do+not+use+fseek()+and+ftell()+to+compute+the+size+of+a+regular+file
#if defined _WIN32 && (!defined __GNUC__ || __MSVCRT_VERSION__ >= 0x0601) #if defined _WIN64 && (!defined __GNUC__ || __MSVCRT_VERSION__ >= 0x0601)
struct __stat64 fileStat; struct __stat64 fileStat;
//using fileno + fstat avoids having to handle the filename //using fileno + fstat avoids having to handle the filename
int err = _fstat64(_fileno(mFile), &fileStat); int err = _fstat64(_fileno(mFile), &fileStat);

View File

@ -300,10 +300,10 @@ private:
const char separator = getOsSeparator(); const char separator = getOsSeparator();
for (it = in.begin(); it != in.end(); ++it) { for (it = in.begin(); it != in.end(); ++it) {
int remaining = std::distance(in.end(), it); const size_t remaining = std::distance(in.end(), it);
// Exclude :// and \\, which remain untouched. // Exclude :// and \\, which remain untouched.
// https://sourceforge.net/tracker/?func=detail&aid=3031725&group_id=226462&atid=1067632 // https://sourceforge.net/tracker/?func=detail&aid=3031725&group_id=226462&atid=1067632
if (remaining >= 3 && !strncmp(&*it, "://", 3 )) { if (remaining >= 3u && !strncmp(&*it, "://", 3 )) {
it += 3; it += 3;
continue; continue;
} }

View File

@ -35,6 +35,17 @@ struct SubChunkHeader
uint16_t length; uint16_t length;
}; };
/////////////////////////////////////////////////////////////////////////////////
//! Describes an IFF form header
/////////////////////////////////////////////////////////////////////////////////
struct FormHeader
{
//! Length of the chunk data, in bytes
uint32_t length;
//! Type of the chunk header - FourCC
uint32_t type;
};
#define AI_IFF_FOURCC(a,b,c,d) ((uint32_t) (((uint8_t)a << 24u) | \ #define AI_IFF_FOURCC(a,b,c,d) ((uint32_t) (((uint8_t)a << 24u) | \
((uint8_t)b << 16u) | ((uint8_t)c << 8u) | ((uint8_t)d))) ((uint8_t)b << 16u) | ((uint8_t)c << 8u) | ((uint8_t)d)))
@ -77,6 +88,24 @@ inline SubChunkHeader LoadSubChunk(uint8_t*& outFile)
return head; return head;
} }
/////////////////////////////////////////////////////////////////////////////////
//! Load a chunk header
//! @param outFile Pointer to the file data - points to the chunk data afterwards
//! @return Copy of the chunk header
/////////////////////////////////////////////////////////////////////////////////
inline ChunkHeader LoadForm(uint8_t*& outFile)
{
ChunkHeader head;
outFile += 4;
::memcpy(&head.length, outFile, 4);
outFile += 4;
::memcpy(&head.type, outFile, 4);
AI_LSWAP4(head.length);
AI_LSWAP4(head.type);
return head;
}
///////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////
//! Read the file header and return the type of the file and its size //! Read the file header and return the type of the file and its size
//! @param outFile Pointer to the file data. The buffer must at //! @param outFile Pointer to the file data. The buffer must at

View File

@ -0,0 +1,29 @@
#pragma once
#include <assimp/ai_assert.h>
template <typename T>
struct Maybe {
private:
T _val;
bool _valid;
public:
Maybe() :
_valid(false) {}
explicit Maybe(const T &val) :
_val(val), _valid(true) {
}
operator bool() const {
return _valid;
}
const T &Get() const {
ai_assert(_valid);
return _val;
}
private:
Maybe &operator&() = delete;
};

View File

@ -0,0 +1,58 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
All rights reserved.
Redistribution and use of this software 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 the assimp team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the assimp team.
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.
----------------------------------------------------------------------
*/
#pragma once
#if _MSC_VER // "unreferenced function has been removed" (SSE2 detection routine in x64 builds)
#pragma warning(push)
#pragma warning(disable : 4505)
#else
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function"
#endif
#include "stb/stb_image.h"
#if _MSC_VER
#pragma warning(pop)
#else
#pragma GCC diagnostic pop
#endif

View File

@ -122,15 +122,15 @@ voidpf IOSystem2Unzip::open(voidpf opaque, const char *filename, int mode) {
voidpf IOSystem2Unzip::opendisk(voidpf opaque, voidpf stream, uint32_t number_disk, int mode) { voidpf IOSystem2Unzip::opendisk(voidpf opaque, voidpf stream, uint32_t number_disk, int mode) {
ZipFile *io_stream = (ZipFile *)stream; ZipFile *io_stream = (ZipFile *)stream;
voidpf ret = NULL; voidpf ret = NULL;
size_t i; int i;
char *disk_filename = (char*)malloc(io_stream->m_Filename.length() + 1); char *disk_filename = (char*)malloc(io_stream->m_Filename.length() + 1);
strncpy(disk_filename, io_stream->m_Filename.c_str(), io_stream->m_Filename.length() + 1); strncpy(disk_filename, io_stream->m_Filename.c_str(), io_stream->m_Filename.length() + 1);
for (i = io_stream->m_Filename.length() - 1; i >= 0; i -= 1) for (i = (int)io_stream->m_Filename.length() - 1; i >= 0; i -= 1)
{ {
if (disk_filename[i] != '.') if (disk_filename[i] != '.')
continue; continue;
snprintf(&disk_filename[i], io_stream->m_Filename.length() - i, ".z%02u", number_disk + 1); snprintf(&disk_filename[i], io_stream->m_Filename.length() - size_t(i), ".z%02u", number_disk + 1);
break; break;
} }

View File

@ -46,7 +46,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/material.h> #include <assimp/material.h>
// ------------------------------------------------------------------------------- // -------------------------------------------------------------------------------
const char *TextureTypeToString(aiTextureType in) { const char *aiTextureTypeToString(aiTextureType in) {
switch (in) { switch (in) {
case aiTextureType_NONE: case aiTextureType_NONE:
return "n/a"; return "n/a";

View File

@ -83,7 +83,7 @@ Other:
#include <sstream> #include <sstream>
#include <string> #include <string>
#include "stb/stb_image.h" #include "Common/StbCommon.h"
using namespace Assimp; using namespace Assimp;
@ -590,7 +590,7 @@ void PbrtExporter::WriteMaterial(int m) {
for (int i = 1; i <= aiTextureType_UNKNOWN; i++) { for (int i = 1; i <= aiTextureType_UNKNOWN; i++) {
int count = material->GetTextureCount(aiTextureType(i)); int count = material->GetTextureCount(aiTextureType(i));
if (count > 0) if (count > 0)
mOutput << TextureTypeToString(aiTextureType(i)) << ": " << count << " "; mOutput << aiTextureTypeToString(aiTextureType(i)) << ": " << count << " ";
} }
mOutput << "\n"; mOutput << "\n";

View File

@ -90,12 +90,14 @@ void ArmaturePopulate::Execute(aiScene *out) {
ai_assert(armature); ai_assert(armature);
#ifndef ASSIMP_BUILD_NO_ARMATUREPOPULATE_PROCESS
// set up bone armature id // set up bone armature id
bone->mArmature = armature; bone->mArmature = armature;
// set this bone node to be referenced properly // set this bone node to be referenced properly
ai_assert(bone_node); ai_assert(bone_node);
bone->mNode = bone_node; bone->mNode = bone_node;
#endif
} }
} }

View File

@ -415,7 +415,7 @@ void ComputeUVMappingProcess::Execute( aiScene* pScene)
if (!DefaultLogger::isNullLogger()) if (!DefaultLogger::isNullLogger())
{ {
ai_snprintf(buffer, 1024, "Found non-UV mapped texture (%s,%u). Mapping type: %s", ai_snprintf(buffer, 1024, "Found non-UV mapped texture (%s,%u). Mapping type: %s",
TextureTypeToString((aiTextureType)prop->mSemantic),prop->mIndex, aiTextureTypeToString((aiTextureType)prop->mSemantic),prop->mIndex,
MappingTypeToString(mapping)); MappingTypeToString(mapping));
ASSIMP_LOG_INFO(buffer); ASSIMP_LOG_INFO(buffer);

View File

@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2022, assimp team Copyright (c) 2006-2022, assimp team
All rights reserved. All rights reserved.
Redistribution and use of this software in source and binary forms, Redistribution and use of this software in source and binary forms,
@ -45,41 +43,38 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* for all imported meshes * for all imported meshes
*/ */
#ifndef ASSIMP_BUILD_NO_JOINVERTICES_PROCESS #ifndef ASSIMP_BUILD_NO_JOINVERTICES_PROCESS
#include "JoinVerticesProcess.h" #include "JoinVerticesProcess.h"
#include "ProcessHelper.h" #include "ProcessHelper.h"
#include <assimp/Vertex.h> #include <assimp/Vertex.h>
#include <assimp/TinyFormatter.h> #include <assimp/TinyFormatter.h>
#include <stdio.h> #include <stdio.h>
#include <unordered_set> #include <unordered_set>
#include <unordered_map>
using namespace Assimp; using namespace Assimp;
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer // Constructor to be privately used by Importer
JoinVerticesProcess::JoinVerticesProcess() JoinVerticesProcess::JoinVerticesProcess() {
{
// nothing to do here // nothing to do here
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Destructor, private as well // Destructor, private as well
JoinVerticesProcess::~JoinVerticesProcess() JoinVerticesProcess::~JoinVerticesProcess() {
{
// nothing to do here // nothing to do here
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field. // Returns whether the processing step is present in the given flag field.
bool JoinVerticesProcess::IsActive( unsigned int pFlags) const bool JoinVerticesProcess::IsActive( unsigned int pFlags) const {
{
return (pFlags & aiProcess_JoinIdenticalVertices) != 0; return (pFlags & aiProcess_JoinIdenticalVertices) != 0;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data. // Executes the post processing step on the given imported data.
void JoinVerticesProcess::Execute( aiScene* pScene) void JoinVerticesProcess::Execute( aiScene* pScene) {
{
ASSIMP_LOG_DEBUG("JoinVerticesProcess begin"); ASSIMP_LOG_DEBUG("JoinVerticesProcess begin");
// get the total number of vertices BEFORE the step is executed // get the total number of vertices BEFORE the step is executed
@ -92,27 +87,29 @@ void JoinVerticesProcess::Execute( aiScene* pScene)
// execute the step // execute the step
int iNumVertices = 0; int iNumVertices = 0;
for( unsigned int a = 0; a < pScene->mNumMeshes; a++) for( unsigned int a = 0; a < pScene->mNumMeshes; a++) {
iNumVertices += ProcessMesh( pScene->mMeshes[a],a); iNumVertices += ProcessMesh( pScene->mMeshes[a],a);
}
pScene->mFlags |= AI_SCENE_FLAGS_NON_VERBOSE_FORMAT;
// if logging is active, print detailed statistics // if logging is active, print detailed statistics
if (!DefaultLogger::isNullLogger()) { if (!DefaultLogger::isNullLogger()) {
if (iNumOldVertices == iNumVertices) { if (iNumOldVertices == iNumVertices) {
ASSIMP_LOG_DEBUG("JoinVerticesProcess finished "); ASSIMP_LOG_DEBUG("JoinVerticesProcess finished ");
} else { return;
ASSIMP_LOG_INFO("JoinVerticesProcess finished | Verts in: ", iNumOldVertices,
" out: ", iNumVertices, " | ~",
((iNumOldVertices - iNumVertices) / (float)iNumOldVertices) * 100.f );
} }
}
pScene->mFlags |= AI_SCENE_FLAGS_NON_VERBOSE_FORMAT; // Show statistics
ASSIMP_LOG_INFO("JoinVerticesProcess finished | Verts in: ", iNumOldVertices,
" out: ", iNumVertices, " | ~",
((iNumOldVertices - iNumVertices) / (float)iNumOldVertices) * 100.f );
}
} }
namespace { namespace {
bool areVerticesEqual(const Vertex &lhs, const Vertex &rhs, bool complex) bool areVerticesEqual(const Vertex &lhs, const Vertex &rhs, bool complex) {
{
// A little helper to find locally close vertices faster. // A little helper to find locally close vertices faster.
// Try to reuse the lookup table from the last step. // Try to reuse the lookup table from the last step.
const static float epsilon = 1e-5f; const static float epsilon = 1e-5f;
@ -171,8 +168,7 @@ void updateXMeshVertices(XMesh *pMesh, std::vector<Vertex> &uniqueVertices) {
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Position, if present (check made for aiAnimMesh) // Position, if present (check made for aiAnimMesh)
if (pMesh->mVertices) if (pMesh->mVertices) {
{
delete [] pMesh->mVertices; delete [] pMesh->mVertices;
pMesh->mVertices = new aiVector3D[pMesh->mNumVertices]; pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
for (unsigned int a = 0; a < pMesh->mNumVertices; a++) { for (unsigned int a = 0; a < pMesh->mNumVertices; a++) {
@ -181,8 +177,7 @@ void updateXMeshVertices(XMesh *pMesh, std::vector<Vertex> &uniqueVertices) {
} }
// Normals, if present // Normals, if present
if (pMesh->mNormals) if (pMesh->mNormals) {
{
delete [] pMesh->mNormals; delete [] pMesh->mNormals;
pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
for( unsigned int a = 0; a < pMesh->mNumVertices; a++) { for( unsigned int a = 0; a < pMesh->mNumVertices; a++) {
@ -190,8 +185,7 @@ void updateXMeshVertices(XMesh *pMesh, std::vector<Vertex> &uniqueVertices) {
} }
} }
// Tangents, if present // Tangents, if present
if (pMesh->mTangents) if (pMesh->mTangents) {
{
delete [] pMesh->mTangents; delete [] pMesh->mTangents;
pMesh->mTangents = new aiVector3D[pMesh->mNumVertices]; pMesh->mTangents = new aiVector3D[pMesh->mNumVertices];
for (unsigned int a = 0; a < pMesh->mNumVertices; a++) { for (unsigned int a = 0; a < pMesh->mNumVertices; a++) {
@ -199,8 +193,7 @@ void updateXMeshVertices(XMesh *pMesh, std::vector<Vertex> &uniqueVertices) {
} }
} }
// Bitangents as well // Bitangents as well
if (pMesh->mBitangents) if (pMesh->mBitangents) {
{
delete [] pMesh->mBitangents; delete [] pMesh->mBitangents;
pMesh->mBitangents = new aiVector3D[pMesh->mNumVertices]; pMesh->mBitangents = new aiVector3D[pMesh->mNumVertices];
for (unsigned int a = 0; a < pMesh->mNumVertices; a++) { for (unsigned int a = 0; a < pMesh->mNumVertices; a++) {
@ -208,8 +201,7 @@ void updateXMeshVertices(XMesh *pMesh, std::vector<Vertex> &uniqueVertices) {
} }
} }
// Vertex colors // Vertex colors
for (unsigned int a = 0; pMesh->HasVertexColors(a); a++) for (unsigned int a = 0; pMesh->HasVertexColors(a); a++) {
{
delete [] pMesh->mColors[a]; delete [] pMesh->mColors[a];
pMesh->mColors[a] = new aiColor4D[pMesh->mNumVertices]; pMesh->mColors[a] = new aiColor4D[pMesh->mNumVertices];
for( unsigned int b = 0; b < pMesh->mNumVertices; b++) { for( unsigned int b = 0; b < pMesh->mNumVertices; b++) {
@ -217,8 +209,7 @@ void updateXMeshVertices(XMesh *pMesh, std::vector<Vertex> &uniqueVertices) {
} }
} }
// Texture coords // Texture coords
for (unsigned int a = 0; pMesh->HasTextureCoords(a); a++) for (unsigned int a = 0; pMesh->HasTextureCoords(a); a++) {
{
delete [] pMesh->mTextureCoords[a]; delete [] pMesh->mTextureCoords[a];
pMesh->mTextureCoords[a] = new aiVector3D[pMesh->mNumVertices]; pMesh->mTextureCoords[a] = new aiVector3D[pMesh->mNumVertices];
for (unsigned int b = 0; b < pMesh->mNumVertices; b++) { for (unsigned int b = 0; b < pMesh->mNumVertices; b++) {
@ -226,12 +217,40 @@ void updateXMeshVertices(XMesh *pMesh, std::vector<Vertex> &uniqueVertices) {
} }
} }
} }
} // namespace } // namespace
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Unites identical vertices in the given mesh // Unites identical vertices in the given mesh
int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex) // combine hashes
{ inline void hash_combine(std::size_t &) {
// empty
}
template <typename T, typename... Rest>
inline void hash_combine(std::size_t& seed, const T& v, Rest... rest) {
std::hash<T> hasher;
seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
hash_combine(seed, rest...);
}
//template specialization for std::hash for Vertex
template<>
struct std::hash<Vertex> {
std::size_t operator()(Vertex const& v) const noexcept {
size_t seed = 0;
hash_combine(seed, v.position.x ,v.position.y,v.position.z);
return seed;
}
};
//template specialization for std::equal_to for Vertex
template<>
struct std::equal_to<Vertex> {
bool operator()(const Vertex &lhs, const Vertex &rhs) const {
return areVerticesEqual(lhs, rhs, false);
}
};
// now start the JoinVerticesProcess
int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex) {
static_assert( AI_MAX_NUMBER_OF_COLOR_SETS == 8, "AI_MAX_NUMBER_OF_COLOR_SETS == 8"); static_assert( AI_MAX_NUMBER_OF_COLOR_SETS == 8, "AI_MAX_NUMBER_OF_COLOR_SETS == 8");
static_assert( AI_MAX_NUMBER_OF_TEXTURECOORDS == 8, "AI_MAX_NUMBER_OF_TEXTURECOORDS == 8"); static_assert( AI_MAX_NUMBER_OF_TEXTURECOORDS == 8, "AI_MAX_NUMBER_OF_TEXTURECOORDS == 8");
@ -245,8 +264,7 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
// multiple meshes) // multiple meshes)
std::unordered_set<unsigned int> usedVertexIndices; std::unordered_set<unsigned int> usedVertexIndices;
usedVertexIndices.reserve(pMesh->mNumVertices); usedVertexIndices.reserve(pMesh->mNumVertices);
for( unsigned int a = 0; a < pMesh->mNumFaces; a++) for( unsigned int a = 0; a < pMesh->mNumFaces; a++) {
{
aiFace& face = pMesh->mFaces[a]; aiFace& face = pMesh->mFaces[a];
for( unsigned int b = 0; b < face.mNumIndices; b++) { for( unsigned int b = 0; b < face.mNumIndices; b++) {
usedVertexIndices.insert(face.mIndices[b]); usedVertexIndices.insert(face.mIndices[b]);
@ -292,7 +310,6 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
// Run an optimized code path if we don't have multiple UVs or vertex colors. // Run an optimized code path if we don't have multiple UVs or vertex colors.
// This should yield false in more than 99% of all imports ... // This should yield false in more than 99% of all imports ...
const bool complex = ( pMesh->GetNumColorChannels() > 0 || pMesh->GetNumUVChannels() > 1);
const bool hasAnimMeshes = pMesh->mNumAnimMeshes > 0; const bool hasAnimMeshes = pMesh->mNumAnimMeshes > 0;
// We'll never have more vertices afterwards. // We'll never have more vertices afterwards.
@ -303,72 +320,38 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
uniqueAnimatedVertices[animMeshIndex].reserve(pMesh->mNumVertices); uniqueAnimatedVertices[animMeshIndex].reserve(pMesh->mNumVertices);
} }
} }
// a map that maps a vertix to its new index
std::unordered_map<Vertex,int> vertex2Index;
// we can not end up with more vertices than we started with
vertex2Index.reserve(pMesh->mNumVertices);
// Now check each vertex if it brings something new to the table // Now check each vertex if it brings something new to the table
int newIndex = 0;
for( unsigned int a = 0; a < pMesh->mNumVertices; a++) { for( unsigned int a = 0; a < pMesh->mNumVertices; a++) {
// if the vertex is unused Do nothing
if (usedVertexIndices.find(a) == usedVertexIndices.end()) { if (usedVertexIndices.find(a) == usedVertexIndices.end()) {
continue; continue;
} }
// collect the vertex data // collect the vertex data
Vertex v(pMesh,a); Vertex v(pMesh,a);
// is the vertex already in the map?
// collect all vertices that are close enough to the given position auto it = vertex2Index.find(v);
vertexFinder->FindIdenticalPositions( v.position, verticesFound); // if the vertex is not in the map then it is a new vertex add it.
unsigned int matchIndex = 0xffffffff; if (it == vertex2Index.end()) {
// this is a new vertex give it a new index
// check all unique vertices close to the position if this vertex is already present among them vertex2Index[v] = newIndex;
for( unsigned int b = 0; b < verticesFound.size(); b++) { //keep track of its index and increment 1
const unsigned int vidx = verticesFound[b]; replaceIndex[a] = newIndex++;
const unsigned int uidx = replaceIndex[ vidx]; // add the vertex to the unique vertices
if( uidx & 0x80000000) uniqueVertices.push_back(v);
continue;
const Vertex& uv = uniqueVertices[ uidx];
if (!areVerticesEqual(v, uv, complex)) {
continue;
}
if (hasAnimMeshes) {
// If given vertex is animated, then it has to be preserver 1 to 1 (base mesh and animated mesh require same topology)
// NOTE: not doing this totaly breaks anim meshes as they don't have their own faces (they use pMesh->mFaces)
bool breaksAnimMesh = false;
for (unsigned int animMeshIndex = 0; animMeshIndex < pMesh->mNumAnimMeshes; animMeshIndex++) {
const Vertex& animatedUV = uniqueAnimatedVertices[animMeshIndex][ uidx];
Vertex aniMeshVertex(pMesh->mAnimMeshes[animMeshIndex], a);
if (!areVerticesEqual(aniMeshVertex, animatedUV, complex)) {
breaksAnimMesh = true;
break;
}
}
if (breaksAnimMesh) {
continue;
}
}
// we're still here -> this vertex perfectly matches our given vertex
matchIndex = uidx;
break;
}
// found a replacement vertex among the uniques?
if( matchIndex != 0xffffffff)
{
// store where to found the matching unique vertex
replaceIndex[a] = matchIndex | 0x80000000;
}
else
{
// no unique vertex matches it up to now -> so add it
replaceIndex[a] = (unsigned int)uniqueVertices.size();
uniqueVertices.push_back( v);
if (hasAnimMeshes) { if (hasAnimMeshes) {
for (unsigned int animMeshIndex = 0; animMeshIndex < pMesh->mNumAnimMeshes; animMeshIndex++) { for (unsigned int animMeshIndex = 0; animMeshIndex < pMesh->mNumAnimMeshes; animMeshIndex++) {
Vertex aniMeshVertex(pMesh->mAnimMeshes[animMeshIndex], a); Vertex aniMeshVertex(pMesh->mAnimMeshes[animMeshIndex], a);
uniqueAnimatedVertices[animMeshIndex].push_back(aniMeshVertex); uniqueAnimatedVertices[animMeshIndex].push_back(v);
} }
} }
} else{
// if the vertex is already there just find the replace index that is appropriate to it
replaceIndex[a] = it->second;
} }
} }
@ -394,8 +377,7 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
} }
// adjust the indices in all faces // adjust the indices in all faces
for( unsigned int a = 0; a < pMesh->mNumFaces; a++) for( unsigned int a = 0; a < pMesh->mNumFaces; a++) {
{
aiFace& face = pMesh->mFaces[a]; aiFace& face = pMesh->mFaces[a];
for( unsigned int b = 0; b < face.mNumIndices; b++) { for( unsigned int b = 0; b < face.mNumIndices; b++) {
face.mIndices[b] = replaceIndex[face.mIndices[b]] & ~0x80000000; face.mIndices[b] = replaceIndex[face.mIndices[b]] & ~0x80000000;

View File

@ -521,7 +521,7 @@ void ValidateDSProcess::Validate(const aiAnimation *pAnimation) {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void ValidateDSProcess::SearchForInvalidTextures(const aiMaterial *pMaterial, void ValidateDSProcess::SearchForInvalidTextures(const aiMaterial *pMaterial,
aiTextureType type) { aiTextureType type) {
const char *szType = TextureTypeToString(type); const char *szType = aiTextureTypeToString(type);
// **************************************************************************** // ****************************************************************************
// Search all keys of the material ... // Search all keys of the material ...

View File

@ -1,80 +1,37 @@
// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#include "revision.h" #include "revision.h"
#include "winres.h"
#define APSTUDIO_READONLY_SYMBOLS LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#define APSTUDIO_HIDDEN_SYMBOLS
#include "windows.h"
#undef APSTUDIO_HIDDEN_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// Deutsch (Deutschland) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_DEU)
#ifdef _WIN32
LANGUAGE LANG_GERMAN, SUBLANG_GERMAN
#pragma code_page(1252) #pragma code_page(1252)
#endif //_WIN32
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
VS_VERSION_INFO VERSIONINFO VS_VERSION_INFO VERSIONINFO
FILEVERSION VER_FILEVERSION FILEVERSION VER_FILEVERSION
PRODUCTVERSION VER_FILEVERSION PRODUCTVERSION VER_FILEVERSION
FILEFLAGSMASK 0x17L FILEFLAGSMASK VS_FF_DEBUG
#ifdef _DEBUG #ifdef _DEBUG
FILEFLAGS 0x1L FILEFLAGS VS_FF_DEBUG
#else
FILEFLAGS 0x0L
#endif #endif
FILEOS 0x4L FILEOS VOS_NT
FILETYPE 0x7L FILETYPE VFT_DLL
FILESUBTYPE 0x0L
BEGIN BEGIN
BLOCK "StringFileInfo" BLOCK "StringFileInfo"
BEGIN BEGIN
BLOCK "040704b0" BLOCK "000004B0"
BEGIN BEGIN
VALUE "Comments", "Licensed under a 3-clause BSD license" VALUE "Comments", "Licensed under a 3-clause BSD license"
VALUE "CompanyName", "assimp team" VALUE "CompanyName", "ASSIMP Team"
VALUE "FileDescription", "Open Asset Import Library" VALUE "FileDescription", "Open Asset Import Library"
VALUE "FileVersion", VER_FILEVERSION VALUE "FileVersion", VER_FILEVERSION_STR
VALUE "InternalName", "assimp " VALUE "InternalName", "assimp"
VALUE "LegalCopyright", "Copyright (C) 2006-2020" VALUE "LegalCopyright", VER_COPYRIGHT_STR
VALUE "OriginalFilename", VER_ORIGINAL_FILENAME_STR VALUE "OriginalFilename", VER_ORIGINAL_FILENAME_STR
VALUE "ProductName", "Open Asset Import Library" VALUE "ProductName", "Open Asset Import Library"
VALUE "ProductVersion", VER_FILEVERSION_STR VALUE "ProductVersion", VER_FILEVERSION_STR
,0
END END
END END
BLOCK "VarFileInfo" BLOCK "VarFileInfo"
BEGIN BEGIN
VALUE "Translation", 0x407, 1200 VALUE "Translation", 0x0, 65001
END END
END END
#endif // Deutsch (Deutschland) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED

View File

@ -1,14 +0,0 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by assimp.rc
// Next standard values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 101
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

View File

@ -10,7 +10,7 @@ endif()
set(draco_root "${CMAKE_CURRENT_SOURCE_DIR}") set(draco_root "${CMAKE_CURRENT_SOURCE_DIR}")
set(draco_src_root "${draco_root}/src/draco") set(draco_src_root "${draco_root}/src/draco")
set(draco_build "${CMAKE_BINARY_DIR}") set(draco_build "${Assimp_BINARY_DIR}")
if("${draco_root}" STREQUAL "${draco_build}") if("${draco_root}" STREQUAL "${draco_build}")
message( message(

View File

@ -1,14 +1,13 @@
######################################################################## ########################################################################
# Note: CMake support is community-based. The maintainers do not use CMake
# internally.
#
# CMake build script for Google Test. # CMake build script for Google Test.
# #
# To run the tests for Google Test itself on Linux, use 'make test' or # To run the tests for Google Test 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'. # For more options, run 'ctest --help'.
# BUILD_SHARED_LIBS is a standard CMake variable, but we declare it here to
# make it prominent in the GUI.
option(BUILD_SHARED_LIBS "Build shared libraries (DLLs)." OFF)
# When other libraries are using a shared version of runtime libraries, # When other libraries are using a shared version of runtime libraries,
# Google Test also has to use one. # Google Test also has to use one.
option( option(
@ -44,13 +43,45 @@ endif()
# as ${gtest_SOURCE_DIR} and to the root binary directory as # as ${gtest_SOURCE_DIR} and to the root binary directory as
# ${gtest_BINARY_DIR}. # ${gtest_BINARY_DIR}.
# Language "C" is required for find_package(Threads). # Language "C" is required for find_package(Threads).
project(gtest CXX C)
cmake_minimum_required(VERSION 3.10) # Project version:
if (CMAKE_VERSION VERSION_LESS 3.0)
project(gtest CXX C)
set(PROJECT_VERSION ${GOOGLETEST_VERSION})
else()
cmake_policy(SET CMP0048 NEW)
project(gtest VERSION ${GOOGLETEST_VERSION} LANGUAGES CXX C)
endif()
cmake_minimum_required(VERSION 2.8.12)
if (POLICY CMP0063) # Visibility
cmake_policy(SET CMP0063 NEW)
endif (POLICY CMP0063)
if (COMMAND set_up_hermetic_build) if (COMMAND set_up_hermetic_build)
set_up_hermetic_build() set_up_hermetic_build()
endif() endif()
# These commands only run if this is the main project
if(CMAKE_PROJECT_NAME STREQUAL "gtest" OR CMAKE_PROJECT_NAME STREQUAL "googletest-distribution")
# BUILD_SHARED_LIBS is a standard CMake variable, but we declare it here to
# make it prominent in the GUI.
option(BUILD_SHARED_LIBS "Build shared libraries (DLLs)." OFF)
else()
mark_as_advanced(
gtest_force_shared_crt
gtest_build_tests
gtest_build_samples
gtest_disable_pthreads
gtest_hide_internal_symbols)
endif()
if (gtest_hide_internal_symbols) if (gtest_hide_internal_symbols)
set(CMAKE_CXX_VISIBILITY_PRESET hidden) set(CMAKE_CXX_VISIBILITY_PRESET hidden)
set(CMAKE_VISIBILITY_INLINES_HIDDEN 1) set(CMAKE_VISIBILITY_INLINES_HIDDEN 1)
@ -61,24 +92,34 @@ include(cmake/internal_utils.cmake)
config_compiler_and_linker() # Defined in internal_utils.cmake. config_compiler_and_linker() # Defined in internal_utils.cmake.
# Where Google Test's .h files can be found. # Needed to set the namespace for both the export targets and the
include_directories( # alias libraries
${gtest_SOURCE_DIR}/include set(cmake_package_name GTest CACHE INTERNAL "")
${gtest_SOURCE_DIR})
# Where Google Test's libraries can be found. # Create the CMake package file descriptors.
link_directories(${gtest_BINARY_DIR}/src) if (INSTALL_GTEST)
include(CMakePackageConfigHelpers)
# Summary of tuple support for Microsoft Visual Studio: set(targets_export_name ${cmake_package_name}Targets CACHE INTERNAL "")
# Compiler version(MS) version(cmake) Support set(generated_dir "${CMAKE_CURRENT_BINARY_DIR}/generated" CACHE INTERNAL "")
# ---------- ----------- -------------- ----------------------------- set(cmake_files_install_dir "${CMAKE_INSTALL_LIBDIR}/cmake/${cmake_package_name}")
# <= VS 2010 <= 10 <= 1600 Use Google Tests's own tuple. set(version_file "${generated_dir}/${cmake_package_name}ConfigVersion.cmake")
# VS 2012 11 1700 std::tr1::tuple + _VARIADIC_MAX=10 write_basic_package_version_file(${version_file} VERSION ${GOOGLETEST_VERSION} COMPATIBILITY AnyNewerVersion)
# VS 2013 12 1800 std::tr1::tuple install(EXPORT ${targets_export_name}
if (MSVC AND MSVC_VERSION EQUAL 1700) NAMESPACE ${cmake_package_name}::
add_definitions(/D _VARIADIC_MAX=10) DESTINATION ${cmake_files_install_dir})
set(config_file "${generated_dir}/${cmake_package_name}Config.cmake")
configure_package_config_file("${gtest_SOURCE_DIR}/cmake/Config.cmake.in"
"${config_file}" INSTALL_DESTINATION ${cmake_files_install_dir})
install(FILES ${version_file} ${config_file}
DESTINATION ${cmake_files_install_dir})
endif() endif()
# Where Google Test's .h files can be found.
set(gtest_build_include_dirs
"${gtest_SOURCE_DIR}/include"
"${gtest_SOURCE_DIR}")
include_directories(${gtest_build_include_dirs})
######################################################################## ########################################################################
# #
# Defines the gtest & gtest_main libraries. User tests should link # Defines the gtest & gtest_main libraries. User tests should link
@ -88,24 +129,26 @@ endif()
# are used for other targets, to ensure that gtest can be compiled by a user # are used for other targets, to ensure that gtest can be compiled by a user
# aggressive about warnings. # aggressive about warnings.
cxx_library(gtest "${cxx_strict}" src/gtest-all.cc) cxx_library(gtest "${cxx_strict}" src/gtest-all.cc)
set_target_properties(gtest PROPERTIES VERSION ${GOOGLETEST_VERSION})
cxx_library(gtest_main "${cxx_strict}" src/gtest_main.cc) cxx_library(gtest_main "${cxx_strict}" src/gtest_main.cc)
target_link_libraries(gtest_main gtest) set_target_properties(gtest_main PROPERTIES VERSION ${GOOGLETEST_VERSION})
# If the CMake version supports it, attach header directory information # 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 # 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). # in via add_subdirectory() rather than being a standalone build).
if (DEFINED CMAKE_VERSION AND NOT "${CMAKE_VERSION}" VERSION_LESS "2.8.11") if (DEFINED CMAKE_VERSION AND NOT "${CMAKE_VERSION}" VERSION_LESS "2.8.11")
target_include_directories(gtest INTERFACE "${gtest_SOURCE_DIR}/include") target_include_directories(gtest SYSTEM INTERFACE
target_include_directories(gtest_main INTERFACE "${gtest_SOURCE_DIR}/include") "$<BUILD_INTERFACE:${gtest_build_include_dirs}>"
"$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/${CMAKE_INSTALL_INCLUDEDIR}>")
target_include_directories(gtest_main SYSTEM INTERFACE
"$<BUILD_INTERFACE:${gtest_build_include_dirs}>"
"$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/${CMAKE_INSTALL_INCLUDEDIR}>")
endif() endif()
target_link_libraries(gtest_main PUBLIC gtest)
######################################################################## ########################################################################
# #
# Install rules # Install rules
install(TARGETS gtest gtest_main install_project(gtest gtest_main)
DESTINATION lib)
install(DIRECTORY ${gtest_SOURCE_DIR}/include/gtest
DESTINATION include)
######################################################################## ########################################################################
# #
@ -147,33 +190,34 @@ if (gtest_build_tests)
############################################################ ############################################################
# C++ tests built with standard compiler flags. # C++ tests built with standard compiler flags.
cxx_test(gtest-death-test_test gtest_main) cxx_test(googletest-death-test-test gtest_main)
cxx_test(gtest_environment_test gtest) cxx_test(gtest_environment_test gtest)
cxx_test(gtest-filepath_test gtest_main) cxx_test(googletest-filepath-test gtest_main)
cxx_test(gtest-linked_ptr_test gtest_main) cxx_test(googletest-listener-test gtest_main)
cxx_test(gtest-listener_test gtest_main)
cxx_test(gtest_main_unittest gtest_main) cxx_test(gtest_main_unittest gtest_main)
cxx_test(gtest-message_test gtest_main) cxx_test(googletest-message-test gtest_main)
cxx_test(gtest_no_test_unittest gtest) cxx_test(gtest_no_test_unittest gtest)
cxx_test(gtest-options_test gtest_main) cxx_test(googletest-options-test gtest_main)
cxx_test(gtest-param-test_test gtest cxx_test(googletest-param-test-test gtest
test/gtest-param-test2_test.cc) test/googletest-param-test2-test.cc)
cxx_test(gtest-port_test gtest_main) cxx_test(googletest-port-test gtest_main)
cxx_test(gtest_pred_impl_unittest gtest_main) cxx_test(gtest_pred_impl_unittest gtest_main)
cxx_test(gtest_premature_exit_test gtest cxx_test(gtest_premature_exit_test gtest
test/gtest_premature_exit_test.cc) test/gtest_premature_exit_test.cc)
cxx_test(gtest-printers_test gtest_main) cxx_test(googletest-printers-test gtest_main)
cxx_test(gtest_prod_test gtest_main cxx_test(gtest_prod_test gtest_main
test/production.cc) test/production.cc)
cxx_test(gtest_repeat_test gtest) cxx_test(gtest_repeat_test gtest)
cxx_test(gtest_sole_header_test gtest_main) cxx_test(gtest_sole_header_test gtest_main)
cxx_test(gtest_stress_test gtest) cxx_test(gtest_stress_test gtest)
cxx_test(gtest-test-part_test gtest_main) cxx_test(googletest-test-part-test gtest_main)
cxx_test(gtest_throw_on_failure_ex_test gtest) cxx_test(gtest_throw_on_failure_ex_test gtest)
cxx_test(gtest-typed-test_test gtest_main cxx_test(gtest-typed-test_test gtest_main
test/gtest-typed-test2_test.cc) test/gtest-typed-test2_test.cc)
cxx_test(gtest_unittest gtest_main) cxx_test(gtest_unittest gtest_main)
cxx_test(gtest-unittest-api_test gtest) cxx_test(gtest-unittest-api_test gtest)
cxx_test(gtest_skip_in_environment_setup_test gtest_main)
cxx_test(gtest_skip_test gtest_main)
############################################################ ############################################################
# C++ tests built with non-standard compiler flags. # C++ tests built with non-standard compiler flags.
@ -190,10 +234,10 @@ if (gtest_build_tests)
cxx_test_with_flags(gtest-death-test_ex_nocatch_test cxx_test_with_flags(gtest-death-test_ex_nocatch_test
"${cxx_exception} -DGTEST_ENABLE_CATCH_EXCEPTIONS_=0" "${cxx_exception} -DGTEST_ENABLE_CATCH_EXCEPTIONS_=0"
gtest test/gtest-death-test_ex_test.cc) gtest test/googletest-death-test_ex_test.cc)
cxx_test_with_flags(gtest-death-test_ex_catch_test cxx_test_with_flags(gtest-death-test_ex_catch_test
"${cxx_exception} -DGTEST_ENABLE_CATCH_EXCEPTIONS_=1" "${cxx_exception} -DGTEST_ENABLE_CATCH_EXCEPTIONS_=1"
gtest test/gtest-death-test_ex_test.cc) gtest test/googletest-death-test_ex_test.cc)
cxx_test_with_flags(gtest_no_rtti_unittest "${cxx_no_rtti}" cxx_test_with_flags(gtest_no_rtti_unittest "${cxx_no_rtti}"
gtest_main_no_rtti test/gtest_unittest.cc) gtest_main_no_rtti test/gtest_unittest.cc)
@ -207,80 +251,73 @@ if (gtest_build_tests)
PROPERTIES PROPERTIES
COMPILE_DEFINITIONS "GTEST_LINKED_AS_SHARED_LIBRARY=1") COMPILE_DEFINITIONS "GTEST_LINKED_AS_SHARED_LIBRARY=1")
if (NOT MSVC OR MSVC_VERSION LESS 1600) # 1600 is Visual Studio 2010.
# Visual Studio 2010, 2012, and 2013 define symbols in std::tr1 that
# conflict with our own definitions. Therefore using our own tuple does not
# work on those compilers.
cxx_library(gtest_main_use_own_tuple "${cxx_use_own_tuple}"
src/gtest-all.cc src/gtest_main.cc)
cxx_test_with_flags(gtest-tuple_test "${cxx_use_own_tuple}"
gtest_main_use_own_tuple test/gtest-tuple_test.cc)
cxx_test_with_flags(gtest_use_own_tuple_test "${cxx_use_own_tuple}"
gtest_main_use_own_tuple
test/gtest-param-test_test.cc test/gtest-param-test2_test.cc)
endif()
############################################################ ############################################################
# Python tests. # Python tests.
cxx_executable(gtest_break_on_failure_unittest_ test gtest) cxx_executable(googletest-break-on-failure-unittest_ test gtest)
py_test(gtest_break_on_failure_unittest) py_test(googletest-break-on-failure-unittest)
py_test(gtest_skip_check_output_test)
py_test(gtest_skip_environment_check_output_test)
# Visual Studio .NET 2003 does not support STL with exceptions disabled. # Visual Studio .NET 2003 does not support STL with exceptions disabled.
if (NOT MSVC OR MSVC_VERSION GREATER 1310) # 1310 is Visual Studio .NET 2003 if (NOT MSVC OR MSVC_VERSION GREATER 1310) # 1310 is Visual Studio .NET 2003
cxx_executable_with_flags( cxx_executable_with_flags(
gtest_catch_exceptions_no_ex_test_ googletest-catch-exceptions-no-ex-test_
"${cxx_no_exception}" "${cxx_no_exception}"
gtest_main_no_exception gtest_main_no_exception
test/gtest_catch_exceptions_test_.cc) test/googletest-catch-exceptions-test_.cc)
endif() endif()
cxx_executable_with_flags( cxx_executable_with_flags(
gtest_catch_exceptions_ex_test_ googletest-catch-exceptions-ex-test_
"${cxx_exception}" "${cxx_exception}"
gtest_main gtest_main
test/gtest_catch_exceptions_test_.cc) test/googletest-catch-exceptions-test_.cc)
py_test(gtest_catch_exceptions_test) py_test(googletest-catch-exceptions-test)
cxx_executable(gtest_color_test_ test gtest) cxx_executable(googletest-color-test_ test gtest)
py_test(gtest_color_test) py_test(googletest-color-test)
cxx_executable(gtest_env_var_test_ test gtest) cxx_executable(googletest-env-var-test_ test gtest)
py_test(gtest_env_var_test) py_test(googletest-env-var-test)
cxx_executable(gtest_filter_unittest_ test gtest) cxx_executable(googletest-filter-unittest_ test gtest)
py_test(gtest_filter_unittest) py_test(googletest-filter-unittest)
cxx_executable(gtest_help_test_ test gtest_main) cxx_executable(gtest_help_test_ test gtest_main)
py_test(gtest_help_test) py_test(gtest_help_test)
cxx_executable(gtest_list_tests_unittest_ test gtest) cxx_executable(googletest-list-tests-unittest_ test gtest)
py_test(gtest_list_tests_unittest) py_test(googletest-list-tests-unittest)
cxx_executable(gtest_output_test_ test gtest) cxx_executable(googletest-output-test_ test gtest)
py_test(gtest_output_test) py_test(googletest-output-test --no_stacktrace_support)
cxx_executable(gtest_shuffle_test_ test gtest) cxx_executable(googletest-shuffle-test_ test gtest)
py_test(gtest_shuffle_test) py_test(googletest-shuffle-test)
# MSVC 7.1 does not support STL with exceptions disabled. # MSVC 7.1 does not support STL with exceptions disabled.
if (NOT MSVC OR MSVC_VERSION GREATER 1310) if (NOT MSVC OR MSVC_VERSION GREATER 1310)
cxx_executable(gtest_throw_on_failure_test_ test gtest_no_exception) cxx_executable(googletest-throw-on-failure-test_ test gtest_no_exception)
set_target_properties(gtest_throw_on_failure_test_ set_target_properties(googletest-throw-on-failure-test_
PROPERTIES PROPERTIES
COMPILE_FLAGS "${cxx_no_exception}") COMPILE_FLAGS "${cxx_no_exception}")
py_test(gtest_throw_on_failure_test) py_test(googletest-throw-on-failure-test)
endif() endif()
cxx_executable(gtest_uninitialized_test_ test gtest) cxx_executable(googletest-uninitialized-test_ test gtest)
py_test(gtest_uninitialized_test) py_test(googletest-uninitialized-test)
cxx_executable(gtest_list_output_unittest_ test gtest)
py_test(gtest_list_output_unittest)
cxx_executable(gtest_xml_outfile1_test_ test gtest_main) cxx_executable(gtest_xml_outfile1_test_ test gtest_main)
cxx_executable(gtest_xml_outfile2_test_ test gtest_main) cxx_executable(gtest_xml_outfile2_test_ test gtest_main)
py_test(gtest_xml_outfiles_test) py_test(gtest_xml_outfiles_test)
py_test(googletest-json-outfiles-test)
cxx_executable(gtest_xml_output_unittest_ test gtest) cxx_executable(gtest_xml_output_unittest_ test gtest)
py_test(gtest_xml_output_unittest) py_test(gtest_xml_output_unittest --no_stacktrace_support)
py_test(googletest-json-output-unittest --no_stacktrace_support)
endif() endif()

View File

@ -1,181 +1,156 @@
### Generic Build Instructions
### Generic Build Instructions ### #### Setup
#### Setup #### To build GoogleTest and your tests that use it, you need to tell your build
system where to find its headers and source files. The exact way to do it
depends on which build system you use, and is usually straightforward.
To build Google Test and your tests that use it, you need to tell your ### Build with CMake
build system where to find its headers and source files. The exact
way to do it depends on which build system you use, and is usually
straightforward.
#### Build #### GoogleTest comes with a CMake build script
([CMakeLists.txt](https://github.com/google/googletest/blob/master/CMakeLists.txt))
that can be used on a wide range of platforms ("C" stands for cross-platform.).
If you don't have CMake installed already, you can download it for free from
<http://www.cmake.org/>.
Suppose you put Google Test in directory `${GTEST_DIR}`. To build it, CMake works by generating native makefiles or build projects that can be used in
create a library build target (or a project as called by Visual Studio the compiler environment of your choice. You can either build GoogleTest as a
and Xcode) to compile standalone project or it can be incorporated into an existing CMake build for
another project.
${GTEST_DIR}/src/gtest-all.cc #### Standalone CMake Project
with `${GTEST_DIR}/include` in the system header search path and `${GTEST_DIR}` When building GoogleTest as a standalone project, the typical workflow starts
in the normal header search path. Assuming a Linux-like system and gcc, with
something like the following will do:
g++ -isystem ${GTEST_DIR}/include -I${GTEST_DIR} \ ```
-pthread -c ${GTEST_DIR}/src/gtest-all.cc git clone https://github.com/google/googletest.git -b release-1.10.0
ar -rv libgtest.a gtest-all.o cd googletest # Main directory of the cloned repository.
mkdir build # Create a directory to hold the build output.
cd build
cmake .. # Generate native build scripts for GoogleTest.
```
(We need `-pthread` as Google Test uses threads.) The above command also includes GoogleMock by default. And so, if you want to
build only GoogleTest, you should replace the last command with
Next, you should compile your test source file with ```
`${GTEST_DIR}/include` in the system header search path, and link it cmake .. -DBUILD_GMOCK=OFF
with gtest and any other necessary libraries: ```
g++ -isystem ${GTEST_DIR}/include -pthread path/to/your_test.cc libgtest.a \ If you are on a \*nix system, you should now see a Makefile in the current
-o your_test directory. Just type `make` to build GoogleTest. And then you can simply install
GoogleTest if you are a system administrator.
As an example, the make/ directory contains a Makefile that you can ```
use to build Google Test on systems where GNU make is available make
(e.g. Linux, Mac OS X, and Cygwin). It doesn't try to build Google sudo make install # Install in /usr/local/ by default
Test's own tests. Instead, it just builds the Google Test library and ```
a sample test. You can use it as a starting point for your own build
script.
If the default settings are correct for your environment, the If you use Windows and have Visual Studio installed, a `gtest.sln` file and
following commands should succeed: several `.vcproj` files will be created. You can then build them using Visual
Studio.
cd ${GTEST_DIR}/make
make
./sample1_unittest
If you see errors, try to tweak the contents of `make/Makefile` to make
them go away. There are instructions in `make/Makefile` on how to do
it.
### Using CMake ###
Google Test comes with a CMake build script (
[CMakeLists.txt](CMakeLists.txt)) that can be used on a wide range of platforms ("C" stands for
cross-platform.). If you don't have CMake installed already, you can
download it for free from <http://www.cmake.org/>.
CMake works by generating native makefiles or build projects that can
be used in the compiler environment of your choice. The typical
workflow starts with:
mkdir mybuild # Create a directory to hold the build output.
cd mybuild
cmake ${GTEST_DIR} # Generate native build scripts.
If you want to build Google Test's samples, you should replace the
last command with
cmake -Dgtest_build_samples=ON ${GTEST_DIR}
If you are on a \*nix system, you should now see a Makefile in the
current directory. Just type 'make' to build gtest.
If you use Windows and have Visual Studio installed, a `gtest.sln` file
and several `.vcproj` files will be created. You can then build them
using Visual Studio.
On Mac OS X with Xcode installed, a `.xcodeproj` file will be generated. On Mac OS X with Xcode installed, a `.xcodeproj` file will be generated.
### Legacy Build Scripts ### #### Incorporating Into An Existing CMake Project
Before settling on CMake, we have been providing hand-maintained build If you want to use GoogleTest in a project which already uses CMake, the easiest
projects/scripts for Visual Studio, Xcode, and Autotools. While we way is to get installed libraries and headers.
continue to provide them for convenience, they are not actively
maintained any more. We highly recommend that you follow the
instructions in the previous two sections to integrate Google Test
with your existing build system.
If you still need to use the legacy build scripts, here's how: * Import GoogleTest by using `find_package` (or `pkg_check_modules`). For
example, if `find_package(GTest CONFIG REQUIRED)` succeeds, you can use the
libraries as `GTest::gtest`, `GTest::gmock`.
The msvc\ folder contains two solutions with Visual C++ projects. And a more robust and flexible approach is to build GoogleTest as part of that
Open the `gtest.sln` or `gtest-md.sln` file using Visual Studio, and you project directly. This is done by making the GoogleTest source code available to
are ready to build Google Test the same way you build any Visual the main build and adding it using CMake's `add_subdirectory()` command. This
Studio project. Files that have names ending with -md use DLL has the significant advantage that the same compiler and linker settings are
versions of Microsoft runtime libraries (the /MD or the /MDd compiler used between GoogleTest and the rest of your project, so issues associated with
option). Files without that suffix use static versions of the runtime using incompatible libraries (eg debug/release), etc. are avoided. This is
libraries (the /MT or the /MTd option). Please note that one must use particularly useful on Windows. Making GoogleTest's source code available to the
the same option to compile both gtest and the test code. If you use main build can be done a few different ways:
Visual Studio 2005 or above, we recommend the -md version as /MD is
the default for new projects in these versions of Visual Studio.
On Mac OS X, open the `gtest.xcodeproj` in the `xcode/` folder using * Download the GoogleTest source code manually and place it at a known
Xcode. Build the "gtest" target. The universal binary framework will location. This is the least flexible approach and can make it more difficult
end up in your selected build directory (selected in the Xcode to use with continuous integration systems, etc.
"Preferences..." -> "Building" pane and defaults to xcode/build). * Embed the GoogleTest source code as a direct copy in the main project's
Alternatively, at the command line, enter: source tree. This is often the simplest approach, but is also the hardest to
keep up to date. Some organizations may not permit this method.
* Add GoogleTest as a git submodule or equivalent. This may not always be
possible or appropriate. Git submodules, for example, have their own set of
advantages and drawbacks.
* Use CMake to download GoogleTest as part of the build's configure step. This
approach doesn't have the limitations of the other methods.
xcodebuild The last of the above methods is implemented with a small piece of CMake code
that downloads and pulls the GoogleTest code into the main build.
This will build the "Release" configuration of gtest.framework in your Just add to your `CMakeLists.txt`:
default build location. See the "xcodebuild" man page for more
information about building different configurations and building in
different locations.
If you wish to use the Google Test Xcode project with Xcode 4.x and ```cmake
above, you need to either: include(FetchContent)
FetchContent_Declare(
googletest
# Specify the commit you depend on and update it regularly.
URL https://github.com/google/googletest/archive/609281088cfefc76f9d0ce82e1ff6c30cc3591e5.zip
)
# For Windows: Prevent overriding the parent project's compiler/linker settings
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)
* update the SDK configuration options in xcode/Config/General.xconfig. # Now simply link against gtest or gtest_main as needed. Eg
Comment options `SDKROOT`, `MACOS_DEPLOYMENT_TARGET`, and `GCC_VERSION`. If add_executable(example example.cpp)
you choose this route you lose the ability to target earlier versions target_link_libraries(example gtest_main)
of MacOS X. add_test(NAME example_test COMMAND example)
* Install an SDK for an earlier version. This doesn't appear to be ```
supported by Apple, but has been reported to work
(http://stackoverflow.com/questions/5378518).
### Tweaking Google Test ### Note that this approach requires CMake 3.14 or later due to its use of the
`FetchContent_MakeAvailable()` command.
Google Test can be used in diverse environments. The default ##### Visual Studio Dynamic vs Static Runtimes
configuration may not work (or may not work well) out of the box in
some environments. However, you can easily tweak Google Test by
defining control macros on the compiler command line. Generally,
these macros are named like `GTEST_XYZ` and you define them to either 1
or 0 to enable or disable a certain feature.
We list the most frequently used macros below. For a complete list, By default, new Visual Studio projects link the C runtimes dynamically but
see file [include/gtest/internal/gtest-port.h](include/gtest/internal/gtest-port.h). GoogleTest links them statically. This will generate an error that looks
something like the following: gtest.lib(gtest-all.obj) : error LNK2038: mismatch
detected for 'RuntimeLibrary': value 'MTd_StaticDebug' doesn't match value
'MDd_DynamicDebug' in main.obj
### Choosing a TR1 Tuple Library ### GoogleTest already has a CMake option for this: `gtest_force_shared_crt`
Some Google Test features require the C++ Technical Report 1 (TR1) Enabling this option will make gtest link the runtimes dynamically too, and
tuple library, which is not yet available with all compilers. The match the project in which it is included.
good news is that Google Test implements a subset of TR1 tuple that's
enough for its own need, and will automatically use this when the
compiler doesn't provide TR1 tuple.
Usually you don't need to care about which tuple library Google Test #### C++ Standard Version
uses. However, if your project already uses TR1 tuple, you need to
tell Google Test to use the same TR1 tuple library the rest of your
project uses, or the two tuple implementations will clash. To do
that, add
-DGTEST_USE_OWN_TR1_TUPLE=0 An environment that supports C++11 is required in order to successfully build
GoogleTest. One way to ensure this is to specify the standard in the top-level
project, for example by using the `set(CMAKE_CXX_STANDARD 11)` command. If this
is not feasible, for example in a C project using GoogleTest for validation,
then it can be specified by adding it to the options for cmake via the
`DCMAKE_CXX_FLAGS` option.
to the compiler flags while compiling Google Test and your tests. If ### Tweaking GoogleTest
you want to force Google Test to use its own tuple library, just add
-DGTEST_USE_OWN_TR1_TUPLE=1 GoogleTest can be used in diverse environments. The default configuration may
not work (or may not work well) out of the box in some environments. However,
you can easily tweak GoogleTest by defining control macros on the compiler
command line. Generally, these macros are named like `GTEST_XYZ` and you define
them to either 1 or 0 to enable or disable a certain feature.
to the compiler flags instead. We list the most frequently used macros below. For a complete list, see file
[include/gtest/internal/gtest-port.h](https://github.com/google/googletest/blob/master/googletest/include/gtest/internal/gtest-port.h).
If you don't want Google Test to use tuple at all, add ### Multi-threaded Tests
-DGTEST_HAS_TR1_TUPLE=0 GoogleTest is thread-safe where the pthread library is available. After
`#include "gtest/gtest.h"`, you can check the
`GTEST_IS_THREADSAFE` macro to see whether this is the case (yes if the macro is
`#defined` to 1, no if it's undefined.).
and all features using tuple will be disabled. If GoogleTest doesn't correctly detect whether pthread is available in your
environment, you can force it with
### Multi-threaded Tests ###
Google Test is thread-safe where the pthread library is available.
After `#include "gtest/gtest.h"`, you can check the `GTEST_IS_THREADSAFE`
macro to see whether this is the case (yes if the macro is `#defined` to
1, no if it's undefined.).
If Google Test doesn't correctly detect whether pthread is available
in your environment, you can force it with
-DGTEST_HAS_PTHREAD=1 -DGTEST_HAS_PTHREAD=1
@ -183,26 +158,24 @@ or
-DGTEST_HAS_PTHREAD=0 -DGTEST_HAS_PTHREAD=0
When Google Test uses pthread, you may need to add flags to your When GoogleTest uses pthread, you may need to add flags to your compiler and/or
compiler and/or linker to select the pthread library, or you'll get linker to select the pthread library, or you'll get link errors. If you use the
link errors. If you use the CMake script or the deprecated Autotools CMake script, this is taken care of for you. If you use your own build script,
script, this is taken care of for you. If you use your own build you'll need to read your compiler and linker's manual to figure out what flags
script, you'll need to read your compiler and linker's manual to to add.
figure out what flags to add.
### As a Shared Library (DLL) ### ### As a Shared Library (DLL)
Google Test is compact, so most users can build and link it as a GoogleTest is compact, so most users can build and link it as a static library
static library for the simplicity. You can choose to use Google Test for the simplicity. You can choose to use GoogleTest as a shared library (known
as a shared library (known as a DLL on Windows) if you prefer. as a DLL on Windows) if you prefer.
To compile *gtest* as a shared library, add To compile *gtest* as a shared library, add
-DGTEST_CREATE_SHARED_LIBRARY=1 -DGTEST_CREATE_SHARED_LIBRARY=1
to the compiler flags. You'll also need to tell the linker to produce to the compiler flags. You'll also need to tell the linker to produce a shared
a shared library instead - consult your linker's manual for how to do library instead - consult your linker's manual for how to do it.
it.
To compile your *tests* that use the gtest shared library, add To compile your *tests* that use the gtest shared library, add
@ -210,31 +183,28 @@ To compile your *tests* that use the gtest shared library, add
to the compiler flags. to the compiler flags.
Note: while the above steps aren't technically necessary today when Note: while the above steps aren't technically necessary today when using some
using some compilers (e.g. GCC), they may become necessary in the compilers (e.g. GCC), they may become necessary in the future, if we decide to
future, if we decide to improve the speed of loading the library (see improve the speed of loading the library (see
<http://gcc.gnu.org/wiki/Visibility> for details). Therefore you are <http://gcc.gnu.org/wiki/Visibility> for details). Therefore you are recommended
recommended to always add the above flags when using Google Test as a to always add the above flags when using GoogleTest as a shared library.
shared library. Otherwise a future release of Google Test may break Otherwise a future release of GoogleTest may break your build script.
your build script.
### Avoiding Macro Name Clashes ### ### Avoiding Macro Name Clashes
In C++, macros don't obey namespaces. Therefore two libraries that In C++, macros don't obey namespaces. Therefore two libraries that both define a
both define a macro of the same name will clash if you `#include` both macro of the same name will clash if you `#include` both definitions. In case a
definitions. In case a Google Test macro clashes with another GoogleTest macro clashes with another library, you can force GoogleTest to
library, you can force Google Test to rename its macro to avoid the rename its macro to avoid the conflict.
conflict.
Specifically, if both Google Test and some other code define macro Specifically, if both GoogleTest and some other code define macro FOO, you can
FOO, you can add add
-DGTEST_DONT_DEFINE_FOO=1 -DGTEST_DONT_DEFINE_FOO=1
to the compiler flags to tell Google Test to change the macro's name to the compiler flags to tell GoogleTest to change the macro's name from `FOO`
from `FOO` to `GTEST_FOO`. Currently `FOO` can be `FAIL`, `SUCCEED`, to `GTEST_FOO`. Currently `FOO` can be `FAIL`, `SUCCEED`, or `TEST`. For
or `TEST`. For example, with `-DGTEST_DONT_DEFINE_TEST=1`, you'll example, with `-DGTEST_DONT_DEFINE_TEST=1`, you'll need to write
need to write
GTEST_TEST(SomeTest, DoesThis) { ... } GTEST_TEST(SomeTest, DoesThis) { ... }
@ -243,38 +213,3 @@ instead of
TEST(SomeTest, DoesThis) { ... } TEST(SomeTest, DoesThis) { ... }
in order to define a test. in order to define a test.
## Developing Google Test ##
This section discusses how to make your own changes to Google Test.
### Testing Google Test Itself ###
To make sure your changes work as intended and don't break existing
functionality, you'll want to compile and run Google Test's own tests.
For that you can use CMake:
mkdir mybuild
cd mybuild
cmake -Dgtest_build_tests=ON ${GTEST_DIR}
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 -Dgtest_build_tests=ON ${GTEST_DIR}
Next, you can build Google Test and all of its own tests. On \*nix,
this is usually done by 'make'. To run the tests, do
make test
All tests should pass.
Normally you don't need to worry about regenerating the source files,
unless you need to modify them. In that case, you should modify the
corresponding .pump files instead and run the pump.py Python script to
regenerate them. You can find pump.py in the [scripts/](scripts/) directory.
Read the [Pump manual](docs/PumpManual.md) for how to use it.

View File

@ -0,0 +1,9 @@
@PACKAGE_INIT@
include(CMakeFindDependencyMacro)
if (@GTEST_HAS_PTHREAD@)
set(THREADS_PREFER_PTHREAD_FLAG @THREADS_PREFER_PTHREAD_FLAG@)
find_dependency(Threads)
endif()
include("${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake")
check_required_components("@project_name@")

View File

@ -0,0 +1,9 @@
libdir=@CMAKE_INSTALL_FULL_LIBDIR@
includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
Name: gtest
Description: GoogleTest (without main() function)
Version: @PROJECT_VERSION@
URL: https://github.com/google/googletest
Libs: -L${libdir} -lgtest @CMAKE_THREAD_LIBS_INIT@
Cflags: -I${includedir} @GTEST_HAS_PTHREAD_MACRO@

View File

@ -0,0 +1,10 @@
libdir=@CMAKE_INSTALL_FULL_LIBDIR@
includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
Name: gtest_main
Description: GoogleTest (with main() function)
Version: @PROJECT_VERSION@
URL: https://github.com/google/googletest
Requires: gtest = @PROJECT_VERSION@
Libs: -L${libdir} -lgtest_main @CMAKE_THREAD_LIBS_INIT@
Cflags: -I${includedir} @GTEST_HAS_PTHREAD_MACRO@

View File

@ -12,6 +12,10 @@
# Test and Google Mock's option() definitions, and thus must be # Test and Google Mock's option() definitions, and thus must be
# called *after* the options have been defined. # called *after* the options have been defined.
if (POLICY CMP0054)
cmake_policy(SET CMP0054 NEW)
endif (POLICY CMP0054)
# Tweaks CMake's default compiler/linker settings to suit Google Test's needs. # Tweaks CMake's default compiler/linker settings to suit Google Test's needs.
# #
# This must be a macro(), as inside a function string() can only # This must be a macro(), as inside a function string() can only
@ -20,8 +24,10 @@ macro(fix_default_compiler_settings_)
if (MSVC) if (MSVC)
# For MSVC, CMake sets certain flags to defaults we want to override. # For MSVC, CMake sets certain flags to defaults we want to override.
# This replacement code is taken from sample in the CMake Wiki at # This replacement code is taken from sample in the CMake Wiki at
# http://www.cmake.org/Wiki/CMake_FAQ#Dynamic_Replace. # https://gitlab.kitware.com/cmake/community/wikis/FAQ#dynamic-replace.
foreach (flag_var foreach (flag_var
CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE
CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO
CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
if (NOT BUILD_SHARED_LIBS AND NOT gtest_force_shared_crt) if (NOT BUILD_SHARED_LIBS AND NOT gtest_force_shared_crt)
@ -38,6 +44,11 @@ macro(fix_default_compiler_settings_)
# We prefer more strict warning checking for building Google Test. # We prefer more strict warning checking for building Google Test.
# Replaces /W3 with /W4 in defaults. # Replaces /W3 with /W4 in defaults.
string(REPLACE "/W3" "/W4" ${flag_var} "${${flag_var}}") string(REPLACE "/W3" "/W4" ${flag_var} "${${flag_var}}")
# Prevent D9025 warning for targets that have exception handling
# turned off (/EHs-c- flag). Where required, exceptions are explicitly
# re-enabled using the cxx_exception_flags variable.
string(REPLACE "/EHsc" "" ${flag_var} "${${flag_var}}")
endforeach() endforeach()
endif() endif()
endmacro() endmacro()
@ -46,52 +57,43 @@ endmacro()
# Google Mock. You can tweak these definitions to suit your need. A # Google Mock. You can tweak these definitions to suit your need. A
# variable's value is empty before it's explicitly assigned to. # variable's value is empty before it's explicitly assigned to.
macro(config_compiler_and_linker) macro(config_compiler_and_linker)
if (NOT gtest_disable_pthreads) # Note: pthreads on MinGW is not supported, even if available
# instead, we use windows threading primitives
unset(GTEST_HAS_PTHREAD)
if (NOT gtest_disable_pthreads AND NOT MINGW)
# Defines CMAKE_USE_PTHREADS_INIT and CMAKE_THREAD_LIBS_INIT. # Defines CMAKE_USE_PTHREADS_INIT and CMAKE_THREAD_LIBS_INIT.
find_package(Threads) find_package(Threads)
if (CMAKE_USE_PTHREADS_INIT)
set(GTEST_HAS_PTHREAD ON)
endif()
endif() endif()
fix_default_compiler_settings_() fix_default_compiler_settings_()
if (MSVC) if (MSVC)
# Newlines inside flags variables break CMake's NMake generator. # Newlines inside flags variables break CMake's NMake generator.
# TODO(vladl@google.com): Add -RTCs and -RTCu to debug builds. # TODO(vladl@google.com): Add -RTCs and -RTCu to debug builds.
set(cxx_base_flags "-GS -W4 -WX -wd4251 -wd4275 -nologo -J -Zi") set(cxx_base_flags "-GS -W4 -WX -wd4251 -wd4275 -nologo -J")
if (MSVC_VERSION LESS 1400) # 1400 is Visual Studio 2005
# Suppress spurious warnings MSVC 7.1 sometimes issues.
# Forcing value to bool.
set(cxx_base_flags "${cxx_base_flags} -wd4800")
# Copy constructor and assignment operator could not be generated.
set(cxx_base_flags "${cxx_base_flags} -wd4511 -wd4512")
# Compatibility warnings not applicable to Google Test.
# Resolved overload was found by argument-dependent lookup.
set(cxx_base_flags "${cxx_base_flags} -wd4675")
endif()
if (MSVC_VERSION LESS 1500) # 1500 is Visual Studio 2008
# Conditional expression is constant.
# When compiling with /W4, we get several instances of C4127
# (Conditional expression is constant). In our code, we disable that
# warning on a case-by-case basis. However, on Visual Studio 2005,
# the warning fires on std::list. Therefore on that compiler and earlier,
# we disable the warning project-wide.
set(cxx_base_flags "${cxx_base_flags} -wd4127")
endif()
if (NOT (MSVC_VERSION LESS 1700)) # 1700 is Visual Studio 2012.
# Suppress "unreachable code" warning on VS 2012 and later.
# http://stackoverflow.com/questions/3232669 explains the issue.
set(cxx_base_flags "${cxx_base_flags} -wd4702")
endif()
if (NOT (MSVC_VERSION GREATER 1900)) # 1900 is Visual Studio 2015
# BigObj required for tests.
set(cxx_base_flags "${cxx_base_flags} -bigobj")
endif()
set(cxx_base_flags "${cxx_base_flags} -D_UNICODE -DUNICODE -DWIN32 -D_WIN32") set(cxx_base_flags "${cxx_base_flags} -D_UNICODE -DUNICODE -DWIN32 -D_WIN32")
set(cxx_base_flags "${cxx_base_flags} -DSTRICT -DWIN32_LEAN_AND_MEAN") set(cxx_base_flags "${cxx_base_flags} -DSTRICT -DWIN32_LEAN_AND_MEAN")
set(cxx_exception_flags "-EHsc -D_HAS_EXCEPTIONS=1") set(cxx_exception_flags "-EHsc -D_HAS_EXCEPTIONS=1")
set(cxx_no_exception_flags "-D_HAS_EXCEPTIONS=0") set(cxx_no_exception_flags "-EHs-c- -D_HAS_EXCEPTIONS=0")
set(cxx_no_rtti_flags "-GR-") set(cxx_no_rtti_flags "-GR-")
# Suppress "unreachable code" warning
# http://stackoverflow.com/questions/3232669 explains the issue.
set(cxx_base_flags "${cxx_base_flags} -wd4702")
# Ensure MSVC treats source files as UTF-8 encoded.
set(cxx_base_flags "${cxx_base_flags} -utf-8")
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
set(cxx_base_flags "-Wall -Wshadow -Werror -Wconversion")
set(cxx_exception_flags "-fexceptions")
set(cxx_no_exception_flags "-fno-exceptions")
set(cxx_strict_flags "-W -Wpointer-arith -Wreturn-type -Wcast-qual -Wwrite-strings -Wswitch -Wunused-parameter -Wcast-align -Wchar-subscripts -Winline -Wredundant-decls")
set(cxx_no_rtti_flags "-fno-rtti")
elseif (CMAKE_COMPILER_IS_GNUCXX) elseif (CMAKE_COMPILER_IS_GNUCXX)
set(cxx_base_flags "-Wall -Wshadow") set(cxx_base_flags "-Wall -Wshadow -Werror")
if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.0.0)
set(cxx_base_flags "${cxx_base_flags} -Wno-error=dangling-else")
endif()
set(cxx_exception_flags "-fexceptions") set(cxx_exception_flags "-fexceptions")
set(cxx_no_exception_flags "-fno-exceptions") set(cxx_no_exception_flags "-fno-exceptions")
# Until version 4.3.2, GCC doesn't define a macro to indicate # Until version 4.3.2, GCC doesn't define a macro to indicate
@ -123,19 +125,20 @@ macro(config_compiler_and_linker)
set(cxx_no_rtti_flags "") set(cxx_no_rtti_flags "")
endif() endif()
if (CMAKE_USE_PTHREADS_INIT) # The pthreads library is available and allowed. # The pthreads library is available and allowed?
set(cxx_base_flags "${cxx_base_flags} -DGTEST_HAS_PTHREAD=1") if (DEFINED GTEST_HAS_PTHREAD)
set(GTEST_HAS_PTHREAD_MACRO "-DGTEST_HAS_PTHREAD=1")
else() else()
set(cxx_base_flags "${cxx_base_flags} -DGTEST_HAS_PTHREAD=0") set(GTEST_HAS_PTHREAD_MACRO "-DGTEST_HAS_PTHREAD=0")
endif() endif()
set(cxx_base_flags "${cxx_base_flags} ${GTEST_HAS_PTHREAD_MACRO}")
# For building gtest's own tests and samples. # For building gtest's own tests and samples.
set(cxx_exception "${CMAKE_CXX_FLAGS} ${cxx_base_flags} ${cxx_exception_flags}") set(cxx_exception "${cxx_base_flags} ${cxx_exception_flags}")
set(cxx_no_exception set(cxx_no_exception
"${CMAKE_CXX_FLAGS} ${cxx_base_flags} ${cxx_no_exception_flags}") "${CMAKE_CXX_FLAGS} ${cxx_base_flags} ${cxx_no_exception_flags}")
set(cxx_default "${cxx_exception}") set(cxx_default "${cxx_exception}")
set(cxx_no_rtti "${cxx_default} ${cxx_no_rtti_flags}") set(cxx_no_rtti "${cxx_default} ${cxx_no_rtti_flags}")
set(cxx_use_own_tuple "${cxx_default} -DGTEST_USE_OWN_TR1_TUPLE=1")
# For building the gtest libraries. # For building the gtest libraries.
set(cxx_strict "${cxx_default} ${cxx_strict_flags}") set(cxx_strict "${cxx_default} ${cxx_strict_flags}")
@ -147,16 +150,50 @@ function(cxx_library_with_type name type cxx_flags)
# type can be either STATIC or SHARED to denote a static or shared library. # type can be either STATIC or SHARED to denote a static or shared library.
# ARGN refers to additional arguments after 'cxx_flags'. # ARGN refers to additional arguments after 'cxx_flags'.
add_library(${name} ${type} ${ARGN}) add_library(${name} ${type} ${ARGN})
add_library(${cmake_package_name}::${name} ALIAS ${name})
set_target_properties(${name} set_target_properties(${name}
PROPERTIES PROPERTIES
COMPILE_FLAGS "${cxx_flags}") COMPILE_FLAGS "${cxx_flags}")
# Generate debug library name with a postfix.
set_target_properties(${name}
PROPERTIES
DEBUG_POSTFIX "d")
# Set the output directory for build artifacts
set_target_properties(${name}
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
PDB_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
# make PDBs match library name
get_target_property(pdb_debug_postfix ${name} DEBUG_POSTFIX)
set_target_properties(${name}
PROPERTIES
PDB_NAME "${name}"
PDB_NAME_DEBUG "${name}${pdb_debug_postfix}"
COMPILE_PDB_NAME "${name}"
COMPILE_PDB_NAME_DEBUG "${name}${pdb_debug_postfix}")
if (BUILD_SHARED_LIBS OR type STREQUAL "SHARED") if (BUILD_SHARED_LIBS OR type STREQUAL "SHARED")
set_target_properties(${name} set_target_properties(${name}
PROPERTIES PROPERTIES
COMPILE_DEFINITIONS "GTEST_CREATE_SHARED_LIBRARY=1") COMPILE_DEFINITIONS "GTEST_CREATE_SHARED_LIBRARY=1")
if (NOT "${CMAKE_VERSION}" VERSION_LESS "2.8.11")
target_compile_definitions(${name} INTERFACE
$<INSTALL_INTERFACE:GTEST_LINKED_AS_SHARED_LIBRARY=1>)
endif()
endif() endif()
if (CMAKE_USE_PTHREADS_INIT) if (DEFINED GTEST_HAS_PTHREAD)
target_link_libraries(${name} ${CMAKE_THREAD_LIBS_INIT}) if ("${CMAKE_VERSION}" VERSION_LESS "3.1.0")
set(threads_spec ${CMAKE_THREAD_LIBS_INIT})
else()
set(threads_spec Threads::Threads)
endif()
target_link_libraries(${name} PUBLIC ${threads_spec})
endif()
if (NOT "${CMAKE_VERSION}" VERSION_LESS "3.8")
target_compile_features(${name} PUBLIC cxx_std_11)
endif() endif()
endfunction() endfunction()
@ -178,6 +215,10 @@ endfunction()
# is built from the given source files with the given compiler flags. # is built from the given source files with the given compiler flags.
function(cxx_executable_with_flags name cxx_flags libs) function(cxx_executable_with_flags name cxx_flags libs)
add_executable(${name} ${ARGN}) add_executable(${name} ${ARGN})
if (MSVC)
# BigObj required for tests.
set(cxx_flags "${cxx_flags} -bigobj")
endif()
if (cxx_flags) if (cxx_flags)
set_target_properties(${name} set_target_properties(${name}
PROPERTIES PROPERTIES
@ -206,7 +247,13 @@ function(cxx_executable name dir libs)
endfunction() endfunction()
# Sets PYTHONINTERP_FOUND and PYTHON_EXECUTABLE. # Sets PYTHONINTERP_FOUND and PYTHON_EXECUTABLE.
find_package(PythonInterp) if ("${CMAKE_VERSION}" VERSION_LESS "3.12.0")
find_package(PythonInterp)
else()
find_package(Python COMPONENTS Interpreter)
set(PYTHONINTERP_FOUND ${Python_Interpreter_FOUND})
set(PYTHON_EXECUTABLE ${Python_EXECUTABLE})
endif()
# cxx_test_with_flags(name cxx_flags libs srcs...) # cxx_test_with_flags(name cxx_flags libs srcs...)
# #
@ -214,7 +261,7 @@ find_package(PythonInterp)
# from the given source files with the given compiler flags. # from the given source files with the given compiler flags.
function(cxx_test_with_flags name cxx_flags libs) function(cxx_test_with_flags name cxx_flags libs)
cxx_executable_with_flags(${name} "${cxx_flags}" "${libs}" ${ARGN}) cxx_executable_with_flags(${name} "${cxx_flags}" "${libs}" ${ARGN})
add_test(${name} ${name}) add_test(NAME ${name} COMMAND "$<TARGET_FILE:${name}>")
endfunction() endfunction()
# cxx_test(name libs srcs...) # cxx_test(name libs srcs...)
@ -232,23 +279,66 @@ endfunction()
# creates a Python test with the given name whose main module is in # creates a Python test with the given name whose main module is in
# test/name.py. It does nothing if Python is not installed. # test/name.py. It does nothing if Python is not installed.
function(py_test name) function(py_test name)
# We are not supporting Python tests on Linux yet as they consider
# all Linux environments to be google3 and try to use google3 features.
if (PYTHONINTERP_FOUND) if (PYTHONINTERP_FOUND)
# ${CMAKE_BINARY_DIR} is known at configuration time, so we can if ("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" VERSION_GREATER 3.1)
# directly bind it from cmake. ${CTEST_CONFIGURATION_TYPE} is known if (CMAKE_CONFIGURATION_TYPES)
# only at ctest runtime (by calling ctest -c <Configuration>), so # Multi-configuration build generators as for Visual Studio save
# we have to escape $ to delay variable substitution here. # output in a subdirectory of CMAKE_CURRENT_BINARY_DIR (Debug,
if (${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} GREATER 3.1) # Release etc.), so we have to provide it here.
add_test( add_test(NAME ${name}
NAME ${name} COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test/${name}.py
--build_dir=${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG> ${ARGN})
else (CMAKE_CONFIGURATION_TYPES)
# Single-configuration build generators like Makefile generators
# don't have subdirs below CMAKE_CURRENT_BINARY_DIR.
add_test(NAME ${name}
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test/${name}.py
--build_dir=${CMAKE_CURRENT_BINARY_DIR} ${ARGN})
endif (CMAKE_CONFIGURATION_TYPES)
else()
# ${CMAKE_CURRENT_BINARY_DIR} is known at configuration time, so we can
# directly bind it from cmake. ${CTEST_CONFIGURATION_TYPE} is known
# only at ctest runtime (by calling ctest -c <Configuration>), so
# we have to escape $ to delay variable substitution here.
add_test(NAME ${name}
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test/${name}.py COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test/${name}.py
--build_dir=${CMAKE_CURRENT_BINARY_DIR}/$<CONFIGURATION>) --build_dir=${CMAKE_CURRENT_BINARY_DIR}/\${CTEST_CONFIGURATION_TYPE} ${ARGN})
else (${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} GREATER 3.1) endif()
add_test( endif(PYTHONINTERP_FOUND)
${name} endfunction()
${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test/${name}.py
--build_dir=${CMAKE_CURRENT_BINARY_DIR}/\${CTEST_CONFIGURATION_TYPE}) # install_project(targets...)
endif (${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} GREATER 3.1) #
# Installs the specified targets and configures the associated pkgconfig files.
function(install_project)
if(INSTALL_GTEST)
install(DIRECTORY "${PROJECT_SOURCE_DIR}/include/"
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
# Install the project targets.
install(TARGETS ${ARGN}
EXPORT ${targets_export_name}
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}")
if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
# Install PDBs
foreach(t ${ARGN})
get_target_property(t_pdb_name ${t} COMPILE_PDB_NAME)
get_target_property(t_pdb_name_debug ${t} COMPILE_PDB_NAME_DEBUG)
get_target_property(t_pdb_output_directory ${t} PDB_OUTPUT_DIRECTORY)
install(FILES
"${t_pdb_output_directory}/\${CMAKE_INSTALL_CONFIG_NAME}/$<$<CONFIG:Debug>:${t_pdb_name_debug}>$<$<NOT:$<CONFIG:Debug>>:${t_pdb_name}>.pdb"
DESTINATION ${CMAKE_INSTALL_LIBDIR}
OPTIONAL)
endforeach()
endif()
# Configure and install pkgconfig files.
foreach(t ${ARGN})
set(configured_pc "${generated_dir}/${t}.pc")
configure_file("${PROJECT_SOURCE_DIR}/cmake/${t}.pc.in"
"${configured_pc}" @ONLY)
install(FILES "${configured_pc}"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
endforeach()
endif() endif()
endfunction() endfunction()

View File

@ -0,0 +1,21 @@
# libgtest.la - a libtool library file
# Generated by libtool (GNU libtool) 2.4.6
# Please DO NOT delete this file!
# It is necessary for linking the library.
# Names of this library.
library_names='libgtest.so'
# Is this an already installed library?
installed=yes
# Should we warn about portability when linking against -modules?
shouldnotlink=no
# Files to dlopen/dlpreopen
dlopen=''
dlpreopen=''
# Directory that this library needs to be installed in:
libdir='@CMAKE_INSTALL_FULL_LIBDIR@'

View File

@ -0,0 +1,4 @@
# Content Moved
We are working on updates to the GoogleTest documentation, which has moved to
the top-level [docs](../../docs) directory.

View File

@ -26,17 +26,17 @@
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// //
// Author: wan@google.com (Zhanyong Wan) // The Google C++ Testing and Mocking Framework (Google Test)
//
// The Google C++ Testing Framework (Google Test)
// //
// This header file defines the public API for death tests. It is // This header file defines the public API for death tests. It is
// #included by gtest.h so a user doesn't need to include this // #included by gtest.h so a user doesn't need to include this
// directly. // directly.
// GOOGLETEST_CM0001 DO NOT DELETE
#ifndef GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ #ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_
#define GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ #define GOOGLETEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_
#include "gtest/internal/gtest-death-test-internal.h" #include "gtest/internal/gtest-death-test-internal.h"
@ -97,12 +97,17 @@ GTEST_API_ bool InDeathTestChild();
// //
// ASSERT_EXIT(client.HangUpServer(), KilledBySIGHUP, "Hanging up!"); // ASSERT_EXIT(client.HangUpServer(), KilledBySIGHUP, "Hanging up!");
// //
// The final parameter to each of these macros is a matcher applied to any data
// the sub-process wrote to stderr. For compatibility with existing tests, a
// bare string is interpreted as a regular expression matcher.
//
// On the regular expressions used in death tests: // On the regular expressions used in death tests:
// //
// GOOGLETEST_CM0005 DO NOT DELETE
// On POSIX-compliant systems (*nix), we use the <regex.h> library, // On POSIX-compliant systems (*nix), we use the <regex.h> library,
// which uses the POSIX extended regex syntax. // which uses the POSIX extended regex syntax.
// //
// On other platforms (e.g. Windows), we only support a simple regex // On other platforms (e.g. Windows or Mac), we only support a simple regex
// syntax implemented as part of Google Test. This limited // syntax implemented as part of Google Test. This limited
// implementation should be enough most of the time when writing // implementation should be enough most of the time when writing
// death tests; though it lacks many features you can find in PCRE // death tests; though it lacks many features you can find in PCRE
@ -160,29 +165,28 @@ GTEST_API_ bool InDeathTestChild();
// is rarely a problem as people usually don't put the test binary // is rarely a problem as people usually don't put the test binary
// directory in PATH. // directory in PATH.
// //
// TODO(wan@google.com): make thread-safe death tests search the PATH.
// Asserts that a given statement causes the program to exit, with an // Asserts that a given `statement` causes the program to exit, with an
// integer exit status that satisfies predicate, and emitting error output // integer exit status that satisfies `predicate`, and emitting error output
// that matches regex. // that matches `matcher`.
# define ASSERT_EXIT(statement, predicate, regex) \ # define ASSERT_EXIT(statement, predicate, matcher) \
GTEST_DEATH_TEST_(statement, predicate, regex, GTEST_FATAL_FAILURE_) GTEST_DEATH_TEST_(statement, predicate, matcher, GTEST_FATAL_FAILURE_)
// Like ASSERT_EXIT, but continues on to successive tests in the // Like `ASSERT_EXIT`, but continues on to successive tests in the
// test case, if any: // test suite, if any:
# define EXPECT_EXIT(statement, predicate, regex) \ # define EXPECT_EXIT(statement, predicate, matcher) \
GTEST_DEATH_TEST_(statement, predicate, regex, GTEST_NONFATAL_FAILURE_) GTEST_DEATH_TEST_(statement, predicate, matcher, GTEST_NONFATAL_FAILURE_)
// Asserts that a given statement causes the program to exit, either by // Asserts that a given `statement` causes the program to exit, either by
// explicitly exiting with a nonzero exit code or being killed by a // explicitly exiting with a nonzero exit code or being killed by a
// signal, and emitting error output that matches regex. // signal, and emitting error output that matches `matcher`.
# define ASSERT_DEATH(statement, regex) \ # define ASSERT_DEATH(statement, matcher) \
ASSERT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, regex) ASSERT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, matcher)
// Like ASSERT_DEATH, but continues on to successive tests in the // Like `ASSERT_DEATH`, but continues on to successive tests in the
// test case, if any: // test suite, if any:
# define EXPECT_DEATH(statement, regex) \ # define EXPECT_DEATH(statement, matcher) \
EXPECT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, regex) EXPECT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, matcher)
// Two predicate classes that can be used in {ASSERT,EXPECT}_EXIT*: // Two predicate classes that can be used in {ASSERT,EXPECT}_EXIT*:
@ -190,17 +194,17 @@ GTEST_API_ bool InDeathTestChild();
class GTEST_API_ ExitedWithCode { class GTEST_API_ ExitedWithCode {
public: public:
explicit ExitedWithCode(int exit_code); explicit ExitedWithCode(int exit_code);
ExitedWithCode(const ExitedWithCode&) = default;
void operator=(const ExitedWithCode& other) = delete;
bool operator()(int exit_status) const; bool operator()(int exit_status) const;
private: private:
// No implementation - assignment is unsupported.
void operator=(const ExitedWithCode& other);
const int exit_code_; const int exit_code_;
}; };
# if !GTEST_OS_WINDOWS # if !GTEST_OS_WINDOWS && !GTEST_OS_FUCHSIA
// Tests that an exit code describes an exit due to termination by a // Tests that an exit code describes an exit due to termination by a
// given signal. // given signal.
// GOOGLETEST_CM0006 DO NOT DELETE
class GTEST_API_ KilledBySignal { class GTEST_API_ KilledBySignal {
public: public:
explicit KilledBySignal(int signum); explicit KilledBySignal(int signum);
@ -226,7 +230,7 @@ class GTEST_API_ KilledBySignal {
// return 12; // return 12;
// } // }
// //
// TEST(TestCase, TestDieOr12WorksInDgbAndOpt) { // TEST(TestSuite, TestDieOr12WorksInDgbAndOpt) {
// int sideeffect = 0; // int sideeffect = 0;
// // Only asserts in dbg. // // Only asserts in dbg.
// EXPECT_DEBUG_DEATH(DieInDebugOr12(&sideeffect), "death"); // EXPECT_DEBUG_DEATH(DieInDebugOr12(&sideeffect), "death");
@ -272,6 +276,54 @@ class GTEST_API_ KilledBySignal {
# endif // NDEBUG for EXPECT_DEBUG_DEATH # endif // NDEBUG for EXPECT_DEBUG_DEATH
#endif // GTEST_HAS_DEATH_TEST #endif // GTEST_HAS_DEATH_TEST
// This macro is used for implementing macros such as
// EXPECT_DEATH_IF_SUPPORTED and ASSERT_DEATH_IF_SUPPORTED on systems where
// death tests are not supported. Those macros must compile on such systems
// if and only if EXPECT_DEATH and ASSERT_DEATH compile with the same parameters
// on systems that support death tests. This allows one to write such a macro on
// a system that does not support death tests and be sure that it will compile
// on a death-test supporting system. It is exposed publicly so that systems
// that have death-tests with stricter requirements than GTEST_HAS_DEATH_TEST
// can write their own equivalent of EXPECT_DEATH_IF_SUPPORTED and
// ASSERT_DEATH_IF_SUPPORTED.
//
// Parameters:
// statement - A statement that a macro such as EXPECT_DEATH would test
// for program termination. This macro has to make sure this
// statement is compiled but not executed, to ensure that
// EXPECT_DEATH_IF_SUPPORTED compiles with a certain
// parameter if and only if EXPECT_DEATH compiles with it.
// regex - A regex that a macro such as EXPECT_DEATH would use to test
// the output of statement. This parameter has to be
// compiled but not evaluated by this macro, to ensure that
// this macro only accepts expressions that a macro such as
// EXPECT_DEATH would accept.
// terminator - Must be an empty statement for EXPECT_DEATH_IF_SUPPORTED
// and a return statement for ASSERT_DEATH_IF_SUPPORTED.
// This ensures that ASSERT_DEATH_IF_SUPPORTED will not
// compile inside functions where ASSERT_DEATH doesn't
// compile.
//
// The branch that has an always false condition is used to ensure that
// statement and regex are compiled (and thus syntactically correct) but
// never executed. The unreachable code macro protects the terminator
// statement from generating an 'unreachable code' warning in case
// statement unconditionally returns or throws. The Message constructor at
// the end allows the syntax of streaming additional messages into the
// macro, for compilational compatibility with EXPECT_DEATH/ASSERT_DEATH.
# define GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, terminator) \
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
if (::testing::internal::AlwaysTrue()) { \
GTEST_LOG_(WARNING) \
<< "Death tests are not supported on this platform.\n" \
<< "Statement '" #statement "' cannot be verified."; \
} else if (::testing::internal::AlwaysFalse()) { \
::testing::internal::RE::PartialMatch(".*", (regex)); \
GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
terminator; \
} else \
::testing::Message()
// EXPECT_DEATH_IF_SUPPORTED(statement, regex) and // EXPECT_DEATH_IF_SUPPORTED(statement, regex) and
// ASSERT_DEATH_IF_SUPPORTED(statement, regex) expand to real death tests if // ASSERT_DEATH_IF_SUPPORTED(statement, regex) expand to real death tests if
// death tests are supported; otherwise they just issue a warning. This is // death tests are supported; otherwise they just issue a warning. This is
@ -284,11 +336,11 @@ class GTEST_API_ KilledBySignal {
ASSERT_DEATH(statement, regex) ASSERT_DEATH(statement, regex)
#else #else
# define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \ # define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \
GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, ) GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, )
# define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \ # define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \
GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, return) GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, return)
#endif #endif
} // namespace testing } // namespace testing
#endif // GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ #endif // GOOGLETEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_

View File

@ -0,0 +1,930 @@
// Copyright 2007, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// The Google C++ Testing and Mocking Framework (Google Test)
//
// This file implements just enough of the matcher interface to allow
// EXPECT_DEATH and friends to accept a matcher argument.
#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_MATCHERS_H_
#define GOOGLETEST_INCLUDE_GTEST_GTEST_MATCHERS_H_
#include <atomic>
#include <memory>
#include <ostream>
#include <string>
#include <type_traits>
#include "gtest/gtest-printers.h"
#include "gtest/internal/gtest-internal.h"
#include "gtest/internal/gtest-port.h"
// MSVC warning C5046 is new as of VS2017 version 15.8.
#if defined(_MSC_VER) && _MSC_VER >= 1915
#define GTEST_MAYBE_5046_ 5046
#else
#define GTEST_MAYBE_5046_
#endif
GTEST_DISABLE_MSC_WARNINGS_PUSH_(
4251 GTEST_MAYBE_5046_ /* class A needs to have dll-interface to be used by
clients of class B */
/* Symbol involving type with internal linkage not defined */)
namespace testing {
// To implement a matcher Foo for type T, define:
// 1. a class FooMatcherMatcher that implements the matcher interface:
// using is_gtest_matcher = void;
// bool MatchAndExplain(const T&, std::ostream*);
// (MatchResultListener* can also be used instead of std::ostream*)
// void DescribeTo(std::ostream*);
// void DescribeNegationTo(std::ostream*);
//
// 2. a factory function that creates a Matcher<T> object from a
// FooMatcherMatcher.
class MatchResultListener {
public:
// Creates a listener object with the given underlying ostream. The
// listener does not own the ostream, and does not dereference it
// in the constructor or destructor.
explicit MatchResultListener(::std::ostream* os) : stream_(os) {}
virtual ~MatchResultListener() = 0; // Makes this class abstract.
// Streams x to the underlying ostream; does nothing if the ostream
// is NULL.
template <typename T>
MatchResultListener& operator<<(const T& x) {
if (stream_ != nullptr) *stream_ << x;
return *this;
}
// Returns the underlying ostream.
::std::ostream* stream() { return stream_; }
// Returns true if and only if the listener is interested in an explanation
// of the match result. A matcher's MatchAndExplain() method can use
// this information to avoid generating the explanation when no one
// intends to hear it.
bool IsInterested() const { return stream_ != nullptr; }
private:
::std::ostream* const stream_;
GTEST_DISALLOW_COPY_AND_ASSIGN_(MatchResultListener);
};
inline MatchResultListener::~MatchResultListener() {
}
// An instance of a subclass of this knows how to describe itself as a
// matcher.
class GTEST_API_ MatcherDescriberInterface {
public:
virtual ~MatcherDescriberInterface() {}
// Describes this matcher to an ostream. The function should print
// a verb phrase that describes the property a value matching this
// matcher should have. The subject of the verb phrase is the value
// being matched. For example, the DescribeTo() method of the Gt(7)
// matcher prints "is greater than 7".
virtual void DescribeTo(::std::ostream* os) const = 0;
// Describes the negation of this matcher to an ostream. For
// example, if the description of this matcher is "is greater than
// 7", the negated description could be "is not greater than 7".
// You are not required to override this when implementing
// MatcherInterface, but it is highly advised so that your matcher
// can produce good error messages.
virtual void DescribeNegationTo(::std::ostream* os) const {
*os << "not (";
DescribeTo(os);
*os << ")";
}
};
// The implementation of a matcher.
template <typename T>
class MatcherInterface : public MatcherDescriberInterface {
public:
// Returns true if and only if the matcher matches x; also explains the
// match result to 'listener' if necessary (see the next paragraph), in
// the form of a non-restrictive relative clause ("which ...",
// "whose ...", etc) that describes x. For example, the
// MatchAndExplain() method of the Pointee(...) matcher should
// generate an explanation like "which points to ...".
//
// Implementations of MatchAndExplain() should add an explanation of
// the match result *if and only if* they can provide additional
// information that's not already present (or not obvious) in the
// print-out of x and the matcher's description. Whether the match
// succeeds is not a factor in deciding whether an explanation is
// needed, as sometimes the caller needs to print a failure message
// when the match succeeds (e.g. when the matcher is used inside
// Not()).
//
// For example, a "has at least 10 elements" matcher should explain
// what the actual element count is, regardless of the match result,
// as it is useful information to the reader; on the other hand, an
// "is empty" matcher probably only needs to explain what the actual
// size is when the match fails, as it's redundant to say that the
// size is 0 when the value is already known to be empty.
//
// You should override this method when defining a new matcher.
//
// It's the responsibility of the caller (Google Test) to guarantee
// that 'listener' is not NULL. This helps to simplify a matcher's
// implementation when it doesn't care about the performance, as it
// can talk to 'listener' without checking its validity first.
// However, in order to implement dummy listeners efficiently,
// listener->stream() may be NULL.
virtual bool MatchAndExplain(T x, MatchResultListener* listener) const = 0;
// Inherits these methods from MatcherDescriberInterface:
// virtual void DescribeTo(::std::ostream* os) const = 0;
// virtual void DescribeNegationTo(::std::ostream* os) const;
};
namespace internal {
struct AnyEq {
template <typename A, typename B>
bool operator()(const A& a, const B& b) const { return a == b; }
};
struct AnyNe {
template <typename A, typename B>
bool operator()(const A& a, const B& b) const { return a != b; }
};
struct AnyLt {
template <typename A, typename B>
bool operator()(const A& a, const B& b) const { return a < b; }
};
struct AnyGt {
template <typename A, typename B>
bool operator()(const A& a, const B& b) const { return a > b; }
};
struct AnyLe {
template <typename A, typename B>
bool operator()(const A& a, const B& b) const { return a <= b; }
};
struct AnyGe {
template <typename A, typename B>
bool operator()(const A& a, const B& b) const { return a >= b; }
};
// A match result listener that ignores the explanation.
class DummyMatchResultListener : public MatchResultListener {
public:
DummyMatchResultListener() : MatchResultListener(nullptr) {}
private:
GTEST_DISALLOW_COPY_AND_ASSIGN_(DummyMatchResultListener);
};
// A match result listener that forwards the explanation to a given
// ostream. The difference between this and MatchResultListener is
// that the former is concrete.
class StreamMatchResultListener : public MatchResultListener {
public:
explicit StreamMatchResultListener(::std::ostream* os)
: MatchResultListener(os) {}
private:
GTEST_DISALLOW_COPY_AND_ASSIGN_(StreamMatchResultListener);
};
struct SharedPayloadBase {
std::atomic<int> ref{1};
void Ref() { ref.fetch_add(1, std::memory_order_relaxed); }
bool Unref() { return ref.fetch_sub(1, std::memory_order_acq_rel) == 1; }
};
template <typename T>
struct SharedPayload : SharedPayloadBase {
explicit SharedPayload(const T& v) : value(v) {}
explicit SharedPayload(T&& v) : value(std::move(v)) {}
static void Destroy(SharedPayloadBase* shared) {
delete static_cast<SharedPayload*>(shared);
}
T value;
};
// An internal class for implementing Matcher<T>, which will derive
// from it. We put functionalities common to all Matcher<T>
// specializations here to avoid code duplication.
template <typename T>
class MatcherBase : private MatcherDescriberInterface {
public:
// Returns true if and only if the matcher matches x; also explains the
// match result to 'listener'.
bool MatchAndExplain(const T& x, MatchResultListener* listener) const {
GTEST_CHECK_(vtable_ != nullptr);
return vtable_->match_and_explain(*this, x, listener);
}
// Returns true if and only if this matcher matches x.
bool Matches(const T& x) const {
DummyMatchResultListener dummy;
return MatchAndExplain(x, &dummy);
}
// Describes this matcher to an ostream.
void DescribeTo(::std::ostream* os) const final {
GTEST_CHECK_(vtable_ != nullptr);
vtable_->describe(*this, os, false);
}
// Describes the negation of this matcher to an ostream.
void DescribeNegationTo(::std::ostream* os) const final {
GTEST_CHECK_(vtable_ != nullptr);
vtable_->describe(*this, os, true);
}
// Explains why x matches, or doesn't match, the matcher.
void ExplainMatchResultTo(const T& x, ::std::ostream* os) const {
StreamMatchResultListener listener(os);
MatchAndExplain(x, &listener);
}
// Returns the describer for this matcher object; retains ownership
// of the describer, which is only guaranteed to be alive when
// this matcher object is alive.
const MatcherDescriberInterface* GetDescriber() const {
if (vtable_ == nullptr) return nullptr;
return vtable_->get_describer(*this);
}
protected:
MatcherBase() : vtable_(nullptr) {}
// Constructs a matcher from its implementation.
template <typename U>
explicit MatcherBase(const MatcherInterface<U>* impl) {
Init(impl);
}
template <typename M, typename = typename std::remove_reference<
M>::type::is_gtest_matcher>
MatcherBase(M&& m) { // NOLINT
Init(std::forward<M>(m));
}
MatcherBase(const MatcherBase& other)
: vtable_(other.vtable_), buffer_(other.buffer_) {
if (IsShared()) buffer_.shared->Ref();
}
MatcherBase& operator=(const MatcherBase& other) {
if (this == &other) return *this;
Destroy();
vtable_ = other.vtable_;
buffer_ = other.buffer_;
if (IsShared()) buffer_.shared->Ref();
return *this;
}
MatcherBase(MatcherBase&& other)
: vtable_(other.vtable_), buffer_(other.buffer_) {
other.vtable_ = nullptr;
}
MatcherBase& operator=(MatcherBase&& other) {
if (this == &other) return *this;
Destroy();
vtable_ = other.vtable_;
buffer_ = other.buffer_;
other.vtable_ = nullptr;
return *this;
}
~MatcherBase() override { Destroy(); }
private:
struct VTable {
bool (*match_and_explain)(const MatcherBase&, const T&,
MatchResultListener*);
void (*describe)(const MatcherBase&, std::ostream*, bool negation);
// Returns the captured object if it implements the interface, otherwise
// returns the MatcherBase itself.
const MatcherDescriberInterface* (*get_describer)(const MatcherBase&);
// Called on shared instances when the reference count reaches 0.
void (*shared_destroy)(SharedPayloadBase*);
};
bool IsShared() const {
return vtable_ != nullptr && vtable_->shared_destroy != nullptr;
}
// If the implementation uses a listener, call that.
template <typename P>
static auto MatchAndExplainImpl(const MatcherBase& m, const T& value,
MatchResultListener* listener)
-> decltype(P::Get(m).MatchAndExplain(value, listener->stream())) {
return P::Get(m).MatchAndExplain(value, listener->stream());
}
template <typename P>
static auto MatchAndExplainImpl(const MatcherBase& m, const T& value,
MatchResultListener* listener)
-> decltype(P::Get(m).MatchAndExplain(value, listener)) {
return P::Get(m).MatchAndExplain(value, listener);
}
template <typename P>
static void DescribeImpl(const MatcherBase& m, std::ostream* os,
bool negation) {
if (negation) {
P::Get(m).DescribeNegationTo(os);
} else {
P::Get(m).DescribeTo(os);
}
}
template <typename P>
static const MatcherDescriberInterface* GetDescriberImpl(
const MatcherBase& m) {
// If the impl is a MatcherDescriberInterface, then return it.
// Otherwise use MatcherBase itself.
// This allows us to implement the GetDescriber() function without support
// from the impl, but some users really want to get their impl back when
// they call GetDescriber().
// We use std::get on a tuple as a workaround of not having `if constexpr`.
return std::get<(
std::is_convertible<decltype(&P::Get(m)),
const MatcherDescriberInterface*>::value
? 1
: 0)>(std::make_tuple(&m, &P::Get(m)));
}
template <typename P>
const VTable* GetVTable() {
static constexpr VTable kVTable = {&MatchAndExplainImpl<P>,
&DescribeImpl<P>, &GetDescriberImpl<P>,
P::shared_destroy};
return &kVTable;
}
union Buffer {
// Add some types to give Buffer some common alignment/size use cases.
void* ptr;
double d;
int64_t i;
// And add one for the out-of-line cases.
SharedPayloadBase* shared;
};
void Destroy() {
if (IsShared() && buffer_.shared->Unref()) {
vtable_->shared_destroy(buffer_.shared);
}
}
template <typename M>
static constexpr bool IsInlined() {
return sizeof(M) <= sizeof(Buffer) && alignof(M) <= alignof(Buffer) &&
std::is_trivially_copy_constructible<M>::value &&
std::is_trivially_destructible<M>::value;
}
template <typename M, bool = MatcherBase::IsInlined<M>()>
struct ValuePolicy {
static const M& Get(const MatcherBase& m) {
// When inlined along with Init, need to be explicit to avoid violating
// strict aliasing rules.
const M *ptr = static_cast<const M*>(
static_cast<const void*>(&m.buffer_));
return *ptr;
}
static void Init(MatcherBase& m, M impl) {
::new (static_cast<void*>(&m.buffer_)) M(impl);
}
static constexpr auto shared_destroy = nullptr;
};
template <typename M>
struct ValuePolicy<M, false> {
using Shared = SharedPayload<M>;
static const M& Get(const MatcherBase& m) {
return static_cast<Shared*>(m.buffer_.shared)->value;
}
template <typename Arg>
static void Init(MatcherBase& m, Arg&& arg) {
m.buffer_.shared = new Shared(std::forward<Arg>(arg));
}
static constexpr auto shared_destroy = &Shared::Destroy;
};
template <typename U, bool B>
struct ValuePolicy<const MatcherInterface<U>*, B> {
using M = const MatcherInterface<U>;
using Shared = SharedPayload<std::unique_ptr<M>>;
static const M& Get(const MatcherBase& m) {
return *static_cast<Shared*>(m.buffer_.shared)->value;
}
static void Init(MatcherBase& m, M* impl) {
m.buffer_.shared = new Shared(std::unique_ptr<M>(impl));
}
static constexpr auto shared_destroy = &Shared::Destroy;
};
template <typename M>
void Init(M&& m) {
using MM = typename std::decay<M>::type;
using Policy = ValuePolicy<MM>;
vtable_ = GetVTable<Policy>();
Policy::Init(*this, std::forward<M>(m));
}
const VTable* vtable_;
Buffer buffer_;
};
} // namespace internal
// A Matcher<T> is a copyable and IMMUTABLE (except by assignment)
// object that can check whether a value of type T matches. The
// implementation of Matcher<T> is just a std::shared_ptr to const
// MatcherInterface<T>. Don't inherit from Matcher!
template <typename T>
class Matcher : public internal::MatcherBase<T> {
public:
// Constructs a null matcher. Needed for storing Matcher objects in STL
// containers. A default-constructed matcher is not yet initialized. You
// cannot use it until a valid value has been assigned to it.
explicit Matcher() {} // NOLINT
// Constructs a matcher from its implementation.
explicit Matcher(const MatcherInterface<const T&>* impl)
: internal::MatcherBase<T>(impl) {}
template <typename U>
explicit Matcher(
const MatcherInterface<U>* impl,
typename std::enable_if<!std::is_same<U, const U&>::value>::type* =
nullptr)
: internal::MatcherBase<T>(impl) {}
template <typename M, typename = typename std::remove_reference<
M>::type::is_gtest_matcher>
Matcher(M&& m) : internal::MatcherBase<T>(std::forward<M>(m)) {} // NOLINT
// Implicit constructor here allows people to write
// EXPECT_CALL(foo, Bar(5)) instead of EXPECT_CALL(foo, Bar(Eq(5))) sometimes
Matcher(T value); // NOLINT
};
// The following two specializations allow the user to write str
// instead of Eq(str) and "foo" instead of Eq("foo") when a std::string
// matcher is expected.
template <>
class GTEST_API_ Matcher<const std::string&>
: public internal::MatcherBase<const std::string&> {
public:
Matcher() {}
explicit Matcher(const MatcherInterface<const std::string&>* impl)
: internal::MatcherBase<const std::string&>(impl) {}
template <typename M, typename = typename std::remove_reference<
M>::type::is_gtest_matcher>
Matcher(M&& m) // NOLINT
: internal::MatcherBase<const std::string&>(std::forward<M>(m)) {}
// Allows the user to write str instead of Eq(str) sometimes, where
// str is a std::string object.
Matcher(const std::string& s); // NOLINT
// Allows the user to write "foo" instead of Eq("foo") sometimes.
Matcher(const char* s); // NOLINT
};
template <>
class GTEST_API_ Matcher<std::string>
: public internal::MatcherBase<std::string> {
public:
Matcher() {}
explicit Matcher(const MatcherInterface<const std::string&>* impl)
: internal::MatcherBase<std::string>(impl) {}
explicit Matcher(const MatcherInterface<std::string>* impl)
: internal::MatcherBase<std::string>(impl) {}
template <typename M, typename = typename std::remove_reference<
M>::type::is_gtest_matcher>
Matcher(M&& m) // NOLINT
: internal::MatcherBase<std::string>(std::forward<M>(m)) {}
// Allows the user to write str instead of Eq(str) sometimes, where
// str is a string object.
Matcher(const std::string& s); // NOLINT
// Allows the user to write "foo" instead of Eq("foo") sometimes.
Matcher(const char* s); // NOLINT
};
#if GTEST_INTERNAL_HAS_STRING_VIEW
// The following two specializations allow the user to write str
// instead of Eq(str) and "foo" instead of Eq("foo") when a absl::string_view
// matcher is expected.
template <>
class GTEST_API_ Matcher<const internal::StringView&>
: public internal::MatcherBase<const internal::StringView&> {
public:
Matcher() {}
explicit Matcher(const MatcherInterface<const internal::StringView&>* impl)
: internal::MatcherBase<const internal::StringView&>(impl) {}
template <typename M, typename = typename std::remove_reference<
M>::type::is_gtest_matcher>
Matcher(M&& m) // NOLINT
: internal::MatcherBase<const internal::StringView&>(std::forward<M>(m)) {
}
// Allows the user to write str instead of Eq(str) sometimes, where
// str is a std::string object.
Matcher(const std::string& s); // NOLINT
// Allows the user to write "foo" instead of Eq("foo") sometimes.
Matcher(const char* s); // NOLINT
// Allows the user to pass absl::string_views or std::string_views directly.
Matcher(internal::StringView s); // NOLINT
};
template <>
class GTEST_API_ Matcher<internal::StringView>
: public internal::MatcherBase<internal::StringView> {
public:
Matcher() {}
explicit Matcher(const MatcherInterface<const internal::StringView&>* impl)
: internal::MatcherBase<internal::StringView>(impl) {}
explicit Matcher(const MatcherInterface<internal::StringView>* impl)
: internal::MatcherBase<internal::StringView>(impl) {}
template <typename M, typename = typename std::remove_reference<
M>::type::is_gtest_matcher>
Matcher(M&& m) // NOLINT
: internal::MatcherBase<internal::StringView>(std::forward<M>(m)) {}
// Allows the user to write str instead of Eq(str) sometimes, where
// str is a std::string object.
Matcher(const std::string& s); // NOLINT
// Allows the user to write "foo" instead of Eq("foo") sometimes.
Matcher(const char* s); // NOLINT
// Allows the user to pass absl::string_views or std::string_views directly.
Matcher(internal::StringView s); // NOLINT
};
#endif // GTEST_INTERNAL_HAS_STRING_VIEW
// Prints a matcher in a human-readable format.
template <typename T>
std::ostream& operator<<(std::ostream& os, const Matcher<T>& matcher) {
matcher.DescribeTo(&os);
return os;
}
// The PolymorphicMatcher class template makes it easy to implement a
// polymorphic matcher (i.e. a matcher that can match values of more
// than one type, e.g. Eq(n) and NotNull()).
//
// To define a polymorphic matcher, a user should provide an Impl
// class that has a DescribeTo() method and a DescribeNegationTo()
// method, and define a member function (or member function template)
//
// bool MatchAndExplain(const Value& value,
// MatchResultListener* listener) const;
//
// See the definition of NotNull() for a complete example.
template <class Impl>
class PolymorphicMatcher {
public:
explicit PolymorphicMatcher(const Impl& an_impl) : impl_(an_impl) {}
// Returns a mutable reference to the underlying matcher
// implementation object.
Impl& mutable_impl() { return impl_; }
// Returns an immutable reference to the underlying matcher
// implementation object.
const Impl& impl() const { return impl_; }
template <typename T>
operator Matcher<T>() const {
return Matcher<T>(new MonomorphicImpl<const T&>(impl_));
}
private:
template <typename T>
class MonomorphicImpl : public MatcherInterface<T> {
public:
explicit MonomorphicImpl(const Impl& impl) : impl_(impl) {}
void DescribeTo(::std::ostream* os) const override { impl_.DescribeTo(os); }
void DescribeNegationTo(::std::ostream* os) const override {
impl_.DescribeNegationTo(os);
}
bool MatchAndExplain(T x, MatchResultListener* listener) const override {
return impl_.MatchAndExplain(x, listener);
}
private:
const Impl impl_;
};
Impl impl_;
};
// Creates a matcher from its implementation.
// DEPRECATED: Especially in the generic code, prefer:
// Matcher<T>(new MyMatcherImpl<const T&>(...));
//
// MakeMatcher may create a Matcher that accepts its argument by value, which
// leads to unnecessary copies & lack of support for non-copyable types.
template <typename T>
inline Matcher<T> MakeMatcher(const MatcherInterface<T>* impl) {
return Matcher<T>(impl);
}
// Creates a polymorphic matcher from its implementation. This is
// easier to use than the PolymorphicMatcher<Impl> constructor as it
// doesn't require you to explicitly write the template argument, e.g.
//
// MakePolymorphicMatcher(foo);
// vs
// PolymorphicMatcher<TypeOfFoo>(foo);
template <class Impl>
inline PolymorphicMatcher<Impl> MakePolymorphicMatcher(const Impl& impl) {
return PolymorphicMatcher<Impl>(impl);
}
namespace internal {
// Implements a matcher that compares a given value with a
// pre-supplied value using one of the ==, <=, <, etc, operators. The
// two values being compared don't have to have the same type.
//
// The matcher defined here is polymorphic (for example, Eq(5) can be
// used to match an int, a short, a double, etc). Therefore we use
// a template type conversion operator in the implementation.
//
// The following template definition assumes that the Rhs parameter is
// a "bare" type (i.e. neither 'const T' nor 'T&').
template <typename D, typename Rhs, typename Op>
class ComparisonBase {
public:
explicit ComparisonBase(const Rhs& rhs) : rhs_(rhs) {}
using is_gtest_matcher = void;
template <typename Lhs>
bool MatchAndExplain(const Lhs& lhs, std::ostream*) const {
return Op()(lhs, Unwrap(rhs_));
}
void DescribeTo(std::ostream* os) const {
*os << D::Desc() << " ";
UniversalPrint(Unwrap(rhs_), os);
}
void DescribeNegationTo(std::ostream* os) const {
*os << D::NegatedDesc() << " ";
UniversalPrint(Unwrap(rhs_), os);
}
private:
template <typename T>
static const T& Unwrap(const T& v) {
return v;
}
template <typename T>
static const T& Unwrap(std::reference_wrapper<T> v) {
return v;
}
Rhs rhs_;
};
template <typename Rhs>
class EqMatcher : public ComparisonBase<EqMatcher<Rhs>, Rhs, AnyEq> {
public:
explicit EqMatcher(const Rhs& rhs)
: ComparisonBase<EqMatcher<Rhs>, Rhs, AnyEq>(rhs) { }
static const char* Desc() { return "is equal to"; }
static const char* NegatedDesc() { return "isn't equal to"; }
};
template <typename Rhs>
class NeMatcher : public ComparisonBase<NeMatcher<Rhs>, Rhs, AnyNe> {
public:
explicit NeMatcher(const Rhs& rhs)
: ComparisonBase<NeMatcher<Rhs>, Rhs, AnyNe>(rhs) { }
static const char* Desc() { return "isn't equal to"; }
static const char* NegatedDesc() { return "is equal to"; }
};
template <typename Rhs>
class LtMatcher : public ComparisonBase<LtMatcher<Rhs>, Rhs, AnyLt> {
public:
explicit LtMatcher(const Rhs& rhs)
: ComparisonBase<LtMatcher<Rhs>, Rhs, AnyLt>(rhs) { }
static const char* Desc() { return "is <"; }
static const char* NegatedDesc() { return "isn't <"; }
};
template <typename Rhs>
class GtMatcher : public ComparisonBase<GtMatcher<Rhs>, Rhs, AnyGt> {
public:
explicit GtMatcher(const Rhs& rhs)
: ComparisonBase<GtMatcher<Rhs>, Rhs, AnyGt>(rhs) { }
static const char* Desc() { return "is >"; }
static const char* NegatedDesc() { return "isn't >"; }
};
template <typename Rhs>
class LeMatcher : public ComparisonBase<LeMatcher<Rhs>, Rhs, AnyLe> {
public:
explicit LeMatcher(const Rhs& rhs)
: ComparisonBase<LeMatcher<Rhs>, Rhs, AnyLe>(rhs) { }
static const char* Desc() { return "is <="; }
static const char* NegatedDesc() { return "isn't <="; }
};
template <typename Rhs>
class GeMatcher : public ComparisonBase<GeMatcher<Rhs>, Rhs, AnyGe> {
public:
explicit GeMatcher(const Rhs& rhs)
: ComparisonBase<GeMatcher<Rhs>, Rhs, AnyGe>(rhs) { }
static const char* Desc() { return "is >="; }
static const char* NegatedDesc() { return "isn't >="; }
};
template <typename T, typename = typename std::enable_if<
std::is_constructible<std::string, T>::value>::type>
using StringLike = T;
// Implements polymorphic matchers MatchesRegex(regex) and
// ContainsRegex(regex), which can be used as a Matcher<T> as long as
// T can be converted to a string.
class MatchesRegexMatcher {
public:
MatchesRegexMatcher(const RE* regex, bool full_match)
: regex_(regex), full_match_(full_match) {}
#if GTEST_INTERNAL_HAS_STRING_VIEW
bool MatchAndExplain(const internal::StringView& s,
MatchResultListener* listener) const {
return MatchAndExplain(std::string(s), listener);
}
#endif // GTEST_INTERNAL_HAS_STRING_VIEW
// Accepts pointer types, particularly:
// const char*
// char*
// const wchar_t*
// wchar_t*
template <typename CharType>
bool MatchAndExplain(CharType* s, MatchResultListener* listener) const {
return s != nullptr && MatchAndExplain(std::string(s), listener);
}
// Matches anything that can convert to std::string.
//
// This is a template, not just a plain function with const std::string&,
// because absl::string_view has some interfering non-explicit constructors.
template <class MatcheeStringType>
bool MatchAndExplain(const MatcheeStringType& s,
MatchResultListener* /* listener */) const {
const std::string& s2(s);
return full_match_ ? RE::FullMatch(s2, *regex_)
: RE::PartialMatch(s2, *regex_);
}
void DescribeTo(::std::ostream* os) const {
*os << (full_match_ ? "matches" : "contains") << " regular expression ";
UniversalPrinter<std::string>::Print(regex_->pattern(), os);
}
void DescribeNegationTo(::std::ostream* os) const {
*os << "doesn't " << (full_match_ ? "match" : "contain")
<< " regular expression ";
UniversalPrinter<std::string>::Print(regex_->pattern(), os);
}
private:
const std::shared_ptr<const RE> regex_;
const bool full_match_;
};
} // namespace internal
// Matches a string that fully matches regular expression 'regex'.
// The matcher takes ownership of 'regex'.
inline PolymorphicMatcher<internal::MatchesRegexMatcher> MatchesRegex(
const internal::RE* regex) {
return MakePolymorphicMatcher(internal::MatchesRegexMatcher(regex, true));
}
template <typename T = std::string>
PolymorphicMatcher<internal::MatchesRegexMatcher> MatchesRegex(
const internal::StringLike<T>& regex) {
return MatchesRegex(new internal::RE(std::string(regex)));
}
// Matches a string that contains regular expression 'regex'.
// The matcher takes ownership of 'regex'.
inline PolymorphicMatcher<internal::MatchesRegexMatcher> ContainsRegex(
const internal::RE* regex) {
return MakePolymorphicMatcher(internal::MatchesRegexMatcher(regex, false));
}
template <typename T = std::string>
PolymorphicMatcher<internal::MatchesRegexMatcher> ContainsRegex(
const internal::StringLike<T>& regex) {
return ContainsRegex(new internal::RE(std::string(regex)));
}
// Creates a polymorphic matcher that matches anything equal to x.
// Note: if the parameter of Eq() were declared as const T&, Eq("foo")
// wouldn't compile.
template <typename T>
inline internal::EqMatcher<T> Eq(T x) { return internal::EqMatcher<T>(x); }
// Constructs a Matcher<T> from a 'value' of type T. The constructed
// matcher matches any value that's equal to 'value'.
template <typename T>
Matcher<T>::Matcher(T value) { *this = Eq(value); }
// Creates a monomorphic matcher that matches anything with type Lhs
// and equal to rhs. A user may need to use this instead of Eq(...)
// in order to resolve an overloading ambiguity.
//
// TypedEq<T>(x) is just a convenient short-hand for Matcher<T>(Eq(x))
// or Matcher<T>(x), but more readable than the latter.
//
// We could define similar monomorphic matchers for other comparison
// operations (e.g. TypedLt, TypedGe, and etc), but decided not to do
// it yet as those are used much less than Eq() in practice. A user
// can always write Matcher<T>(Lt(5)) to be explicit about the type,
// for example.
template <typename Lhs, typename Rhs>
inline Matcher<Lhs> TypedEq(const Rhs& rhs) { return Eq(rhs); }
// Creates a polymorphic matcher that matches anything >= x.
template <typename Rhs>
inline internal::GeMatcher<Rhs> Ge(Rhs x) {
return internal::GeMatcher<Rhs>(x);
}
// Creates a polymorphic matcher that matches anything > x.
template <typename Rhs>
inline internal::GtMatcher<Rhs> Gt(Rhs x) {
return internal::GtMatcher<Rhs>(x);
}
// Creates a polymorphic matcher that matches anything <= x.
template <typename Rhs>
inline internal::LeMatcher<Rhs> Le(Rhs x) {
return internal::LeMatcher<Rhs>(x);
}
// Creates a polymorphic matcher that matches anything < x.
template <typename Rhs>
inline internal::LtMatcher<Rhs> Lt(Rhs x) {
return internal::LtMatcher<Rhs>(x);
}
// Creates a polymorphic matcher that matches anything != x.
template <typename Rhs>
inline internal::NeMatcher<Rhs> Ne(Rhs x) {
return internal::NeMatcher<Rhs>(x);
}
} // namespace testing
GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 5046
#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_MATCHERS_H_

View File

@ -26,10 +26,9 @@
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// //
// Author: wan@google.com (Zhanyong Wan) // The Google C++ Testing and Mocking Framework (Google Test)
//
// The Google C++ Testing Framework (Google Test)
// //
// This header file defines the Message class. // This header file defines the Message class.
// //
@ -43,13 +42,20 @@
// to CHANGE WITHOUT NOTICE. Therefore DO NOT DEPEND ON IT in a user // to CHANGE WITHOUT NOTICE. Therefore DO NOT DEPEND ON IT in a user
// program! // program!
#ifndef GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ // GOOGLETEST_CM0001 DO NOT DELETE
#define GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_
#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_MESSAGE_H_
#define GOOGLETEST_INCLUDE_GTEST_GTEST_MESSAGE_H_
#include <limits> #include <limits>
#include <memory>
#include <sstream>
#include "gtest/internal/gtest-port.h" #include "gtest/internal/gtest-port.h"
GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
/* class A needs to have dll-interface to be used by clients of class B */)
// Ensures that there is at least one operator<< in the global namespace. // Ensures that there is at least one operator<< in the global namespace.
// See Message& operator<<(...) below for why. // See Message& operator<<(...) below for why.
void operator<<(const testing::internal::Secret&, int); void operator<<(const testing::internal::Secret&, int);
@ -102,14 +108,6 @@ class GTEST_API_ Message {
*ss_ << str; *ss_ << str;
} }
#if GTEST_OS_SYMBIAN
// Streams a value (either a pointer or not) to this object.
template <typename T>
inline Message& operator <<(const T& value) {
StreamHelper(typename internal::is_pointer<T>::type(), value);
return *this;
}
#else
// Streams a non-pointer value to this object. // Streams a non-pointer value to this object.
template <typename T> template <typename T>
inline Message& operator <<(const T& val) { inline Message& operator <<(const T& val) {
@ -147,14 +145,13 @@ class GTEST_API_ Message {
// as "(null)". // as "(null)".
template <typename T> template <typename T>
inline Message& operator <<(T* const& pointer) { // NOLINT inline Message& operator <<(T* const& pointer) { // NOLINT
if (pointer == NULL) { if (pointer == nullptr) {
*ss_ << "(null)"; *ss_ << "(null)";
} else { } else {
*ss_ << pointer; *ss_ << pointer;
} }
return *this; return *this;
} }
#endif // GTEST_OS_SYMBIAN
// Since the basic IO manipulators are overloaded for both narrow // Since the basic IO manipulators are overloaded for both narrow
// and wide streams, we have to provide this specialized definition // and wide streams, we have to provide this specialized definition
@ -183,12 +180,6 @@ class GTEST_API_ Message {
Message& operator <<(const ::std::wstring& wstr); Message& operator <<(const ::std::wstring& wstr);
#endif // GTEST_HAS_STD_WSTRING #endif // GTEST_HAS_STD_WSTRING
#if GTEST_HAS_GLOBAL_WSTRING
// Converts the given wide string to a narrow string using the UTF-8
// encoding, and streams the result to this Message object.
Message& operator <<(const ::wstring& wstr);
#endif // GTEST_HAS_GLOBAL_WSTRING
// Gets the text streamed to this object so far as an std::string. // Gets the text streamed to this object so far as an std::string.
// Each '\0' character in the buffer is replaced with "\\0". // Each '\0' character in the buffer is replaced with "\\0".
// //
@ -196,32 +187,8 @@ class GTEST_API_ Message {
std::string GetString() const; std::string GetString() const;
private: private:
#if GTEST_OS_SYMBIAN
// These are needed as the Nokia Symbian Compiler cannot decide between
// const T& and const T* in a function template. The Nokia compiler _can_
// decide between class template specializations for T and T*, so a
// tr1::type_traits-like is_pointer works, and we can overload on that.
template <typename T>
inline void StreamHelper(internal::true_type /*is_pointer*/, T* pointer) {
if (pointer == NULL) {
*ss_ << "(null)";
} else {
*ss_ << pointer;
}
}
template <typename T>
inline void StreamHelper(internal::false_type /*is_pointer*/,
const T& value) {
// See the comments in Message& operator <<(const T&) above for why
// we need this using statement.
using ::operator <<;
*ss_ << value;
}
#endif // GTEST_OS_SYMBIAN
// We'll hold the text streamed to this object here. // We'll hold the text streamed to this object here.
const internal::scoped_ptr< ::std::stringstream> ss_; const std::unique_ptr< ::std::stringstream> ss_;
// We declare (but don't implement) this to prevent the compiler // We declare (but don't implement) this to prevent the compiler
// from implementing the assignment operator. // from implementing the assignment operator.
@ -247,4 +214,6 @@ std::string StreamableToString(const T& streamable) {
} // namespace internal } // namespace internal
} // namespace testing } // namespace testing
#endif // GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251
#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_MESSAGE_H_

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -26,17 +26,21 @@
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Author: wan@google.com (Zhanyong Wan)
// //
// Utilities for testing Google Test itself and code that uses Google Test // Utilities for testing Google Test itself and code that uses Google Test
// (e.g. frameworks built on top of Google Test). // (e.g. frameworks built on top of Google Test).
#ifndef GTEST_INCLUDE_GTEST_GTEST_SPI_H_ // GOOGLETEST_CM0004 DO NOT DELETE
#define GTEST_INCLUDE_GTEST_GTEST_SPI_H_
#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_SPI_H_
#define GOOGLETEST_INCLUDE_GTEST_GTEST_SPI_H_
#include "gtest/gtest.h" #include "gtest/gtest.h"
GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
/* class A needs to have dll-interface to be used by clients of class B */)
namespace testing { namespace testing {
// This helper class can be used to mock out Google Test failure reporting // This helper class can be used to mock out Google Test failure reporting
@ -68,14 +72,15 @@ class GTEST_API_ ScopedFakeTestPartResultReporter
TestPartResultArray* result); TestPartResultArray* result);
// The d'tor restores the previous test part result reporter. // The d'tor restores the previous test part result reporter.
virtual ~ScopedFakeTestPartResultReporter(); ~ScopedFakeTestPartResultReporter() override;
// Appends the TestPartResult object to the TestPartResultArray // Appends the TestPartResult object to the TestPartResultArray
// received in the constructor. // received in the constructor.
// //
// This method is from the TestPartResultReporterInterface // This method is from the TestPartResultReporterInterface
// interface. // interface.
virtual void ReportTestPartResult(const TestPartResult& result); void ReportTestPartResult(const TestPartResult& result) override;
private: private:
void Init(); void Init();
@ -97,13 +102,12 @@ class GTEST_API_ SingleFailureChecker {
public: public:
// The constructor remembers the arguments. // The constructor remembers the arguments.
SingleFailureChecker(const TestPartResultArray* results, SingleFailureChecker(const TestPartResultArray* results,
TestPartResult::Type type, TestPartResult::Type type, const std::string& substr);
const string& substr);
~SingleFailureChecker(); ~SingleFailureChecker();
private: private:
const TestPartResultArray* const results_; const TestPartResultArray* const results_;
const TestPartResult::Type type_; const TestPartResult::Type type_;
const string substr_; const std::string substr_;
GTEST_DISALLOW_COPY_AND_ASSIGN_(SingleFailureChecker); GTEST_DISALLOW_COPY_AND_ASSIGN_(SingleFailureChecker);
}; };
@ -112,6 +116,8 @@ class GTEST_API_ SingleFailureChecker {
} // namespace testing } // namespace testing
GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251
// A set of macros for testing Google Test assertions or code that's expected // A set of macros for testing Google Test assertions or code that's expected
// to generate Google Test fatal failures. It verifies that the given // to generate Google Test fatal failures. It verifies that the given
// statement will cause exactly one fatal Google Test failure with 'substr' // statement will cause exactly one fatal Google Test failure with 'substr'
@ -229,4 +235,4 @@ class GTEST_API_ SingleFailureChecker {
}\ }\
} while (::testing::internal::AlwaysFalse()) } while (::testing::internal::AlwaysFalse())
#endif // GTEST_INCLUDE_GTEST_GTEST_SPI_H_ #endif // GOOGLETEST_INCLUDE_GTEST_GTEST_SPI_H_

View File

@ -27,17 +27,19 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// //
// Author: mheule@google.com (Markus Heule) // GOOGLETEST_CM0001 DO NOT DELETE
//
#ifndef GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ #ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_TEST_PART_H_
#define GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ #define GOOGLETEST_INCLUDE_GTEST_GTEST_TEST_PART_H_
#include <iosfwd> #include <iosfwd>
#include <vector> #include <vector>
#include "gtest/internal/gtest-internal.h" #include "gtest/internal/gtest-internal.h"
#include "gtest/internal/gtest-string.h" #include "gtest/internal/gtest-string.h"
GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
/* class A needs to have dll-interface to be used by clients of class B */)
namespace testing { namespace testing {
// A copyable object representing the result of a test part (i.e. an // A copyable object representing the result of a test part (i.e. an
@ -51,22 +53,20 @@ class GTEST_API_ TestPartResult {
enum Type { enum Type {
kSuccess, // Succeeded. kSuccess, // Succeeded.
kNonFatalFailure, // Failed but the test can continue. kNonFatalFailure, // Failed but the test can continue.
kFatalFailure // Failed and the test should be terminated. kFatalFailure, // Failed and the test should be terminated.
kSkip // Skipped.
}; };
// C'tor. TestPartResult does NOT have a default constructor. // C'tor. TestPartResult does NOT have a default constructor.
// Always use this constructor (with parameters) to create a // Always use this constructor (with parameters) to create a
// TestPartResult object. // TestPartResult object.
TestPartResult(Type a_type, TestPartResult(Type a_type, const char* a_file_name, int a_line_number,
const char* a_file_name,
int a_line_number,
const char* a_message) const char* a_message)
: type_(a_type), : type_(a_type),
file_name_(a_file_name == NULL ? "" : a_file_name), file_name_(a_file_name == nullptr ? "" : a_file_name),
line_number_(a_line_number), line_number_(a_line_number),
summary_(ExtractSummary(a_message)), summary_(ExtractSummary(a_message)),
message_(a_message) { message_(a_message) {}
}
// Gets the outcome of the test part. // Gets the outcome of the test part.
Type type() const { return type_; } Type type() const { return type_; }
@ -74,7 +74,7 @@ class GTEST_API_ TestPartResult {
// Gets the name of the source file where the test part took place, or // Gets the name of the source file where the test part took place, or
// NULL if it's unknown. // NULL if it's unknown.
const char* file_name() const { const char* file_name() const {
return file_name_.empty() ? NULL : file_name_.c_str(); return file_name_.empty() ? nullptr : file_name_.c_str();
} }
// Gets the line in the source file where the test part took place, // Gets the line in the source file where the test part took place,
@ -87,18 +87,21 @@ class GTEST_API_ TestPartResult {
// Gets the message associated with the test part. // Gets the message associated with the test part.
const char* message() const { return message_.c_str(); } const char* message() const { return message_.c_str(); }
// Returns true iff the test part passed. // Returns true if and only if the test part was skipped.
bool skipped() const { return type_ == kSkip; }
// Returns true if and only if the test part passed.
bool passed() const { return type_ == kSuccess; } bool passed() const { return type_ == kSuccess; }
// Returns true iff the test part failed. // Returns true if and only if the test part non-fatally failed.
bool failed() const { return type_ != kSuccess; }
// Returns true iff the test part non-fatally failed.
bool nonfatally_failed() const { return type_ == kNonFatalFailure; } bool nonfatally_failed() const { return type_ == kNonFatalFailure; }
// Returns true iff the test part fatally failed. // Returns true if and only if the test part fatally failed.
bool fatally_failed() const { return type_ == kFatalFailure; } bool fatally_failed() const { return type_ == kFatalFailure; }
// Returns true if and only if the test part failed.
bool failed() const { return fatally_failed() || nonfatally_failed(); }
private: private:
Type type_; Type type_;
@ -143,7 +146,7 @@ class GTEST_API_ TestPartResultArray {
}; };
// This interface knows how to report a test part result. // This interface knows how to report a test part result.
class TestPartResultReporterInterface { class GTEST_API_ TestPartResultReporterInterface {
public: public:
virtual ~TestPartResultReporterInterface() {} virtual ~TestPartResultReporterInterface() {}
@ -162,8 +165,8 @@ class GTEST_API_ HasNewFatalFailureHelper
: public TestPartResultReporterInterface { : public TestPartResultReporterInterface {
public: public:
HasNewFatalFailureHelper(); HasNewFatalFailureHelper();
virtual ~HasNewFatalFailureHelper(); ~HasNewFatalFailureHelper() override;
virtual void ReportTestPartResult(const TestPartResult& result); void ReportTestPartResult(const TestPartResult& result) override;
bool has_new_fatal_failure() const { return has_new_fatal_failure_; } bool has_new_fatal_failure() const { return has_new_fatal_failure_; }
private: private:
bool has_new_fatal_failure_; bool has_new_fatal_failure_;
@ -176,4 +179,6 @@ class GTEST_API_ HasNewFatalFailureHelper
} // namespace testing } // namespace testing
#endif // GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251
#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_TEST_PART_H_

View File

@ -26,11 +26,11 @@
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Author: wan@google.com (Zhanyong Wan)
#ifndef GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_ // GOOGLETEST_CM0001 DO NOT DELETE
#define GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_
#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_
#define GOOGLETEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_
// This header implements typed tests and type-parameterized tests. // This header implements typed tests and type-parameterized tests.
@ -51,22 +51,22 @@ class FooTest : public testing::Test {
T value_; T value_;
}; };
// Next, associate a list of types with the test case, which will be // Next, associate a list of types with the test suite, which will be
// repeated for each type in the list. The typedef is necessary for // repeated for each type in the list. The typedef is necessary for
// the macro to parse correctly. // the macro to parse correctly.
typedef testing::Types<char, int, unsigned int> MyTypes; typedef testing::Types<char, int, unsigned int> MyTypes;
TYPED_TEST_CASE(FooTest, MyTypes); TYPED_TEST_SUITE(FooTest, MyTypes);
// If the type list contains only one type, you can write that type // If the type list contains only one type, you can write that type
// directly without Types<...>: // directly without Types<...>:
// TYPED_TEST_CASE(FooTest, int); // TYPED_TEST_SUITE(FooTest, int);
// Then, use TYPED_TEST() instead of TEST_F() to define as many typed // Then, use TYPED_TEST() instead of TEST_F() to define as many typed
// tests for this test case as you want. // tests for this test suite as you want.
TYPED_TEST(FooTest, DoesBlah) { TYPED_TEST(FooTest, DoesBlah) {
// Inside a test, refer to TypeParam to get the type parameter. // Inside a test, refer to the special name TypeParam to get the type
// Since we are inside a derived class template, C++ requires use to // parameter. Since we are inside a derived class template, C++ requires
// visit the members of FooTest via 'this'. // us to visit the members of FooTest via 'this'.
TypeParam n = this->value_; TypeParam n = this->value_;
// To visit static members of the fixture, add the TestFixture:: // To visit static members of the fixture, add the TestFixture::
@ -82,6 +82,24 @@ TYPED_TEST(FooTest, DoesBlah) {
TYPED_TEST(FooTest, HasPropertyA) { ... } TYPED_TEST(FooTest, HasPropertyA) { ... }
// TYPED_TEST_SUITE takes an optional third argument which allows to specify a
// class that generates custom test name suffixes based on the type. This should
// be a class which has a static template function GetName(int index) returning
// a string for each type. The provided integer index equals the index of the
// type in the provided type list. In many cases the index can be ignored.
//
// For example:
// class MyTypeNames {
// public:
// template <typename T>
// static std::string GetName(int) {
// if (std::is_same<T, char>()) return "char";
// if (std::is_same<T, int>()) return "int";
// if (std::is_same<T, unsigned int>()) return "unsignedInt";
// }
// };
// TYPED_TEST_SUITE(FooTest, MyTypes, MyTypeNames);
#endif // 0 #endif // 0
// Type-parameterized tests are abstract test patterns parameterized // Type-parameterized tests are abstract test patterns parameterized
@ -107,13 +125,13 @@ class FooTest : public testing::Test {
... ...
}; };
// Next, declare that you will define a type-parameterized test case // Next, declare that you will define a type-parameterized test suite
// (the _P suffix is for "parameterized" or "pattern", whichever you // (the _P suffix is for "parameterized" or "pattern", whichever you
// prefer): // prefer):
TYPED_TEST_CASE_P(FooTest); TYPED_TEST_SUITE_P(FooTest);
// Then, use TYPED_TEST_P() to define as many type-parameterized tests // Then, use TYPED_TEST_P() to define as many type-parameterized tests
// for this type-parameterized test case as you want. // for this type-parameterized test suite as you want.
TYPED_TEST_P(FooTest, DoesBlah) { TYPED_TEST_P(FooTest, DoesBlah) {
// Inside a test, refer to TypeParam to get the type parameter. // Inside a test, refer to TypeParam to get the type parameter.
TypeParam n = 0; TypeParam n = 0;
@ -124,10 +142,10 @@ TYPED_TEST_P(FooTest, HasPropertyA) { ... }
// Now the tricky part: you need to register all test patterns before // Now the tricky part: you need to register all test patterns before
// you can instantiate them. The first argument of the macro is the // you can instantiate them. The first argument of the macro is the
// test case name; the rest are the names of the tests in this test // test suite name; the rest are the names of the tests in this test
// case. // case.
REGISTER_TYPED_TEST_CASE_P(FooTest, REGISTER_TYPED_TEST_SUITE_P(FooTest,
DoesBlah, HasPropertyA); DoesBlah, HasPropertyA);
// Finally, you are free to instantiate the pattern with the types you // Finally, you are free to instantiate the pattern with the types you
// want. If you put the above code in a header file, you can #include // want. If you put the above code in a header file, you can #include
@ -135,129 +153,177 @@ REGISTER_TYPED_TEST_CASE_P(FooTest,
// //
// To distinguish different instances of the pattern, the first // To distinguish different instances of the pattern, the first
// argument to the INSTANTIATE_* macro is a prefix that will be added // argument to the INSTANTIATE_* macro is a prefix that will be added
// to the actual test case name. Remember to pick unique prefixes for // to the actual test suite name. Remember to pick unique prefixes for
// different instances. // different instances.
typedef testing::Types<char, int, unsigned int> MyTypes; typedef testing::Types<char, int, unsigned int> MyTypes;
INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes); INSTANTIATE_TYPED_TEST_SUITE_P(My, FooTest, MyTypes);
// If the type list contains only one type, you can write that type // If the type list contains only one type, you can write that type
// directly without Types<...>: // directly without Types<...>:
// INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, int); // INSTANTIATE_TYPED_TEST_SUITE_P(My, FooTest, int);
//
// Similar to the optional argument of TYPED_TEST_SUITE above,
// INSTANTIATE_TEST_SUITE_P takes an optional fourth argument which allows to
// generate custom names.
// INSTANTIATE_TYPED_TEST_SUITE_P(My, FooTest, MyTypes, MyTypeNames);
#endif // 0 #endif // 0
#include "gtest/internal/gtest-internal.h"
#include "gtest/internal/gtest-port.h" #include "gtest/internal/gtest-port.h"
#include "gtest/internal/gtest-type-util.h" #include "gtest/internal/gtest-type-util.h"
// Implements typed tests. // Implements typed tests.
#if GTEST_HAS_TYPED_TEST
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. // INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
// //
// Expands to the name of the typedef for the type parameters of the // Expands to the name of the typedef for the type parameters of the
// given test case. // given test suite.
# define GTEST_TYPE_PARAMS_(TestCaseName) gtest_type_params_##TestCaseName##_ #define GTEST_TYPE_PARAMS_(TestSuiteName) gtest_type_params_##TestSuiteName##_
// The 'Types' template argument below must have spaces around it // Expands to the name of the typedef for the NameGenerator, responsible for
// since some compilers may choke on '>>' when passing a template // creating the suffixes of the name.
// instance (e.g. Types<int>) #define GTEST_NAME_GENERATOR_(TestSuiteName) \
# define TYPED_TEST_CASE(CaseName, Types) \ gtest_type_params_##TestSuiteName##_NameGenerator
typedef ::testing::internal::TypeList< Types >::type \
GTEST_TYPE_PARAMS_(CaseName)
# define TYPED_TEST(CaseName, TestName) \ #define TYPED_TEST_SUITE(CaseName, Types, ...) \
template <typename gtest_TypeParam_> \ typedef ::testing::internal::GenerateTypeList<Types>::type \
class GTEST_TEST_CLASS_NAME_(CaseName, TestName) \ GTEST_TYPE_PARAMS_(CaseName); \
: public CaseName<gtest_TypeParam_> { \ typedef ::testing::internal::NameGeneratorSelector<__VA_ARGS__>::type \
private: \ GTEST_NAME_GENERATOR_(CaseName)
typedef CaseName<gtest_TypeParam_> TestFixture; \
typedef gtest_TypeParam_ TypeParam; \
virtual void TestBody(); \
}; \
bool gtest_##CaseName##_##TestName##_registered_ GTEST_ATTRIBUTE_UNUSED_ = \
::testing::internal::TypeParameterizedTest< \
CaseName, \
::testing::internal::TemplateSel< \
GTEST_TEST_CLASS_NAME_(CaseName, TestName)>, \
GTEST_TYPE_PARAMS_(CaseName)>::Register(\
"", ::testing::internal::CodeLocation(__FILE__, __LINE__), \
#CaseName, #TestName, 0); \
template <typename gtest_TypeParam_> \
void GTEST_TEST_CLASS_NAME_(CaseName, TestName)<gtest_TypeParam_>::TestBody()
#endif // GTEST_HAS_TYPED_TEST #define TYPED_TEST(CaseName, TestName) \
static_assert(sizeof(GTEST_STRINGIFY_(TestName)) > 1, \
"test-name must not be empty"); \
template <typename gtest_TypeParam_> \
class GTEST_TEST_CLASS_NAME_(CaseName, TestName) \
: public CaseName<gtest_TypeParam_> { \
private: \
typedef CaseName<gtest_TypeParam_> TestFixture; \
typedef gtest_TypeParam_ TypeParam; \
void TestBody() override; \
}; \
static bool gtest_##CaseName##_##TestName##_registered_ \
GTEST_ATTRIBUTE_UNUSED_ = ::testing::internal::TypeParameterizedTest< \
CaseName, \
::testing::internal::TemplateSel<GTEST_TEST_CLASS_NAME_(CaseName, \
TestName)>, \
GTEST_TYPE_PARAMS_( \
CaseName)>::Register("", \
::testing::internal::CodeLocation( \
__FILE__, __LINE__), \
GTEST_STRINGIFY_(CaseName), \
GTEST_STRINGIFY_(TestName), 0, \
::testing::internal::GenerateNames< \
GTEST_NAME_GENERATOR_(CaseName), \
GTEST_TYPE_PARAMS_(CaseName)>()); \
template <typename gtest_TypeParam_> \
void GTEST_TEST_CLASS_NAME_(CaseName, \
TestName)<gtest_TypeParam_>::TestBody()
// Legacy API is deprecated but still available
#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
#define TYPED_TEST_CASE \
static_assert(::testing::internal::TypedTestCaseIsDeprecated(), ""); \
TYPED_TEST_SUITE
#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
// Implements type-parameterized tests. // Implements type-parameterized tests.
#if GTEST_HAS_TYPED_TEST_P
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. // INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
// //
// Expands to the namespace name that the type-parameterized tests for // Expands to the namespace name that the type-parameterized tests for
// the given type-parameterized test case are defined in. The exact // the given type-parameterized test suite are defined in. The exact
// name of the namespace is subject to change without notice. // name of the namespace is subject to change without notice.
# define GTEST_CASE_NAMESPACE_(TestCaseName) \ #define GTEST_SUITE_NAMESPACE_(TestSuiteName) gtest_suite_##TestSuiteName##_
gtest_case_##TestCaseName##_
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. // INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
// //
// Expands to the name of the variable used to remember the names of // Expands to the name of the variable used to remember the names of
// the defined tests in the given test case. // the defined tests in the given test suite.
# define GTEST_TYPED_TEST_CASE_P_STATE_(TestCaseName) \ #define GTEST_TYPED_TEST_SUITE_P_STATE_(TestSuiteName) \
gtest_typed_test_case_p_state_##TestCaseName##_ gtest_typed_test_suite_p_state_##TestSuiteName##_
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE DIRECTLY. // INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE DIRECTLY.
// //
// Expands to the name of the variable used to remember the names of // Expands to the name of the variable used to remember the names of
// the registered tests in the given test case. // the registered tests in the given test suite.
# define GTEST_REGISTERED_TEST_NAMES_(TestCaseName) \ #define GTEST_REGISTERED_TEST_NAMES_(TestSuiteName) \
gtest_registered_test_names_##TestCaseName##_ gtest_registered_test_names_##TestSuiteName##_
// The variables defined in the type-parameterized test macros are // The variables defined in the type-parameterized test macros are
// static as typically these macros are used in a .h file that can be // static as typically these macros are used in a .h file that can be
// #included in multiple translation units linked together. // #included in multiple translation units linked together.
# define TYPED_TEST_CASE_P(CaseName) \ #define TYPED_TEST_SUITE_P(SuiteName) \
static ::testing::internal::TypedTestCasePState \ static ::testing::internal::TypedTestSuitePState \
GTEST_TYPED_TEST_CASE_P_STATE_(CaseName) GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName)
# define TYPED_TEST_P(CaseName, TestName) \ // Legacy API is deprecated but still available
namespace GTEST_CASE_NAMESPACE_(CaseName) { \ #ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
template <typename gtest_TypeParam_> \ #define TYPED_TEST_CASE_P \
class TestName : public CaseName<gtest_TypeParam_> { \ static_assert(::testing::internal::TypedTestCase_P_IsDeprecated(), ""); \
private: \ TYPED_TEST_SUITE_P
typedef CaseName<gtest_TypeParam_> TestFixture; \ #endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
typedef gtest_TypeParam_ TypeParam; \
virtual void TestBody(); \
}; \
static bool gtest_##TestName##_defined_ GTEST_ATTRIBUTE_UNUSED_ = \
GTEST_TYPED_TEST_CASE_P_STATE_(CaseName).AddTestName(\
__FILE__, __LINE__, #CaseName, #TestName); \
} \
template <typename gtest_TypeParam_> \
void GTEST_CASE_NAMESPACE_(CaseName)::TestName<gtest_TypeParam_>::TestBody()
# define REGISTER_TYPED_TEST_CASE_P(CaseName, ...) \ #define TYPED_TEST_P(SuiteName, TestName) \
namespace GTEST_CASE_NAMESPACE_(CaseName) { \ namespace GTEST_SUITE_NAMESPACE_(SuiteName) { \
typedef ::testing::internal::Templates<__VA_ARGS__>::type gtest_AllTests_; \ template <typename gtest_TypeParam_> \
} \ class TestName : public SuiteName<gtest_TypeParam_> { \
static const char* const GTEST_REGISTERED_TEST_NAMES_(CaseName) = \ private: \
GTEST_TYPED_TEST_CASE_P_STATE_(CaseName).VerifyRegisteredTestNames(\ typedef SuiteName<gtest_TypeParam_> TestFixture; \
__FILE__, __LINE__, #__VA_ARGS__) typedef gtest_TypeParam_ TypeParam; \
void TestBody() override; \
}; \
static bool gtest_##TestName##_defined_ GTEST_ATTRIBUTE_UNUSED_ = \
GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName).AddTestName( \
__FILE__, __LINE__, GTEST_STRINGIFY_(SuiteName), \
GTEST_STRINGIFY_(TestName)); \
} \
template <typename gtest_TypeParam_> \
void GTEST_SUITE_NAMESPACE_( \
SuiteName)::TestName<gtest_TypeParam_>::TestBody()
// The 'Types' template argument below must have spaces around it // Note: this won't work correctly if the trailing arguments are macros.
// since some compilers may choke on '>>' when passing a template #define REGISTER_TYPED_TEST_SUITE_P(SuiteName, ...) \
// instance (e.g. Types<int>) namespace GTEST_SUITE_NAMESPACE_(SuiteName) { \
# define INSTANTIATE_TYPED_TEST_CASE_P(Prefix, CaseName, Types) \ typedef ::testing::internal::Templates<__VA_ARGS__> gtest_AllTests_; \
bool gtest_##Prefix##_##CaseName GTEST_ATTRIBUTE_UNUSED_ = \ } \
::testing::internal::TypeParameterizedTestCase<CaseName, \ static const char* const GTEST_REGISTERED_TEST_NAMES_( \
GTEST_CASE_NAMESPACE_(CaseName)::gtest_AllTests_, \ SuiteName) GTEST_ATTRIBUTE_UNUSED_ = \
::testing::internal::TypeList< Types >::type>::Register(\ GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName).VerifyRegisteredTestNames( \
#Prefix, \ GTEST_STRINGIFY_(SuiteName), __FILE__, __LINE__, #__VA_ARGS__)
::testing::internal::CodeLocation(__FILE__, __LINE__), \
&GTEST_TYPED_TEST_CASE_P_STATE_(CaseName), \
#CaseName, GTEST_REGISTERED_TEST_NAMES_(CaseName))
#endif // GTEST_HAS_TYPED_TEST_P // Legacy API is deprecated but still available
#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
#define REGISTER_TYPED_TEST_CASE_P \
static_assert(::testing::internal::RegisterTypedTestCase_P_IsDeprecated(), \
""); \
REGISTER_TYPED_TEST_SUITE_P
#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
#endif // GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_ #define INSTANTIATE_TYPED_TEST_SUITE_P(Prefix, SuiteName, Types, ...) \
static_assert(sizeof(GTEST_STRINGIFY_(Prefix)) > 1, \
"test-suit-prefix must not be empty"); \
static bool gtest_##Prefix##_##SuiteName GTEST_ATTRIBUTE_UNUSED_ = \
::testing::internal::TypeParameterizedTestSuite< \
SuiteName, GTEST_SUITE_NAMESPACE_(SuiteName)::gtest_AllTests_, \
::testing::internal::GenerateTypeList<Types>::type>:: \
Register(GTEST_STRINGIFY_(Prefix), \
::testing::internal::CodeLocation(__FILE__, __LINE__), \
&GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName), \
GTEST_STRINGIFY_(SuiteName), \
GTEST_REGISTERED_TEST_NAMES_(SuiteName), \
::testing::internal::GenerateNames< \
::testing::internal::NameGeneratorSelector< \
__VA_ARGS__>::type, \
::testing::internal::GenerateTypeList<Types>::type>())
// Legacy API is deprecated but still available
#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
#define INSTANTIATE_TYPED_TEST_CASE_P \
static_assert( \
::testing::internal::InstantiateTypedTestCase_P_IsDeprecated(), ""); \
INSTANTIATE_TYPED_TEST_SUITE_P
#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_

File diff suppressed because it is too large Load Diff

View File

@ -27,18 +27,18 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// This file is AUTOMATICALLY GENERATED on 10/31/2011 by command // This file is AUTOMATICALLY GENERATED on 01/02/2019 by command
// 'gen_gtest_pred_impl.py 5'. DO NOT EDIT BY HAND! // 'gen_gtest_pred_impl.py 5'. DO NOT EDIT BY HAND!
// //
// Implements a family of generic predicate assertion macros. // Implements a family of generic predicate assertion macros.
// GOOGLETEST_CM0001 DO NOT DELETE
#ifndef GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ #ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_
#define GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ #define GOOGLETEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_
// Makes sure this header is not included before gtest.h. #include "gtest/gtest.h"
#ifndef GTEST_INCLUDE_GTEST_GTEST_H_
# error Do not include gtest_pred_impl.h directly. Include gtest.h instead. namespace testing {
#endif // GTEST_INCLUDE_GTEST_GTEST_H_
// This header implements a family of generic predicate assertion // This header implements a family of generic predicate assertion
// macros: // macros:
@ -90,9 +90,10 @@ AssertionResult AssertPred1Helper(const char* pred_text,
const T1& v1) { const T1& v1) {
if (pred(v1)) return AssertionSuccess(); if (pred(v1)) return AssertionSuccess();
return AssertionFailure() << pred_text << "(" return AssertionFailure()
<< e1 << ") evaluates to false, where" << pred_text << "(" << e1 << ") evaluates to false, where"
<< "\n" << e1 << " evaluates to " << v1; << "\n"
<< e1 << " evaluates to " << ::testing::PrintToString(v1);
} }
// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT1. // Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT1.
@ -134,11 +135,12 @@ AssertionResult AssertPred2Helper(const char* pred_text,
const T2& v2) { const T2& v2) {
if (pred(v1, v2)) return AssertionSuccess(); if (pred(v1, v2)) return AssertionSuccess();
return AssertionFailure() << pred_text << "(" return AssertionFailure()
<< e1 << ", " << pred_text << "(" << e1 << ", " << e2
<< e2 << ") evaluates to false, where" << ") evaluates to false, where"
<< "\n" << e1 << " evaluates to " << v1 << "\n"
<< "\n" << e2 << " evaluates to " << v2; << e1 << " evaluates to " << ::testing::PrintToString(v1) << "\n"
<< e2 << " evaluates to " << ::testing::PrintToString(v2);
} }
// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT2. // Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT2.
@ -185,13 +187,13 @@ AssertionResult AssertPred3Helper(const char* pred_text,
const T3& v3) { const T3& v3) {
if (pred(v1, v2, v3)) return AssertionSuccess(); if (pred(v1, v2, v3)) return AssertionSuccess();
return AssertionFailure() << pred_text << "(" return AssertionFailure()
<< e1 << ", " << pred_text << "(" << e1 << ", " << e2 << ", " << e3
<< e2 << ", " << ") evaluates to false, where"
<< e3 << ") evaluates to false, where" << "\n"
<< "\n" << e1 << " evaluates to " << v1 << e1 << " evaluates to " << ::testing::PrintToString(v1) << "\n"
<< "\n" << e2 << " evaluates to " << v2 << e2 << " evaluates to " << ::testing::PrintToString(v2) << "\n"
<< "\n" << e3 << " evaluates to " << v3; << e3 << " evaluates to " << ::testing::PrintToString(v3);
} }
// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT3. // Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT3.
@ -243,15 +245,14 @@ AssertionResult AssertPred4Helper(const char* pred_text,
const T4& v4) { const T4& v4) {
if (pred(v1, v2, v3, v4)) return AssertionSuccess(); if (pred(v1, v2, v3, v4)) return AssertionSuccess();
return AssertionFailure() << pred_text << "(" return AssertionFailure()
<< e1 << ", " << pred_text << "(" << e1 << ", " << e2 << ", " << e3 << ", " << e4
<< e2 << ", " << ") evaluates to false, where"
<< e3 << ", " << "\n"
<< e4 << ") evaluates to false, where" << e1 << " evaluates to " << ::testing::PrintToString(v1) << "\n"
<< "\n" << e1 << " evaluates to " << v1 << e2 << " evaluates to " << ::testing::PrintToString(v2) << "\n"
<< "\n" << e2 << " evaluates to " << v2 << e3 << " evaluates to " << ::testing::PrintToString(v3) << "\n"
<< "\n" << e3 << " evaluates to " << v3 << e4 << " evaluates to " << ::testing::PrintToString(v4);
<< "\n" << e4 << " evaluates to " << v4;
} }
// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT4. // Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT4.
@ -308,17 +309,15 @@ AssertionResult AssertPred5Helper(const char* pred_text,
const T5& v5) { const T5& v5) {
if (pred(v1, v2, v3, v4, v5)) return AssertionSuccess(); if (pred(v1, v2, v3, v4, v5)) return AssertionSuccess();
return AssertionFailure() << pred_text << "(" return AssertionFailure()
<< e1 << ", " << pred_text << "(" << e1 << ", " << e2 << ", " << e3 << ", " << e4
<< e2 << ", " << ", " << e5 << ") evaluates to false, where"
<< e3 << ", " << "\n"
<< e4 << ", " << e1 << " evaluates to " << ::testing::PrintToString(v1) << "\n"
<< e5 << ") evaluates to false, where" << e2 << " evaluates to " << ::testing::PrintToString(v2) << "\n"
<< "\n" << e1 << " evaluates to " << v1 << e3 << " evaluates to " << ::testing::PrintToString(v3) << "\n"
<< "\n" << e2 << " evaluates to " << v2 << e4 << " evaluates to " << ::testing::PrintToString(v4) << "\n"
<< "\n" << e3 << " evaluates to " << v3 << e5 << " evaluates to " << ::testing::PrintToString(v5);
<< "\n" << e4 << " evaluates to " << v4
<< "\n" << e5 << " evaluates to " << v5;
} }
// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT5. // Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT5.
@ -355,4 +354,6 @@ AssertionResult AssertPred5Helper(const char* pred_text,
#endif // GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ } // namespace testing
#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_

View File

@ -26,13 +26,13 @@
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Author: wan@google.com (Zhanyong Wan)
//
// Google C++ Testing Framework definitions useful in production code.
#ifndef GTEST_INCLUDE_GTEST_GTEST_PROD_H_ //
#define GTEST_INCLUDE_GTEST_GTEST_PROD_H_ // Google C++ Testing and Mocking Framework definitions useful in production code.
// GOOGLETEST_CM0003 DO NOT DELETE
#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_PROD_H_
#define GOOGLETEST_INCLUDE_GTEST_GTEST_PROD_H_
// When you need to test the private or protected members of a class, // When you need to test the private or protected members of a class,
// use the FRIEND_TEST macro to declare your tests as friends of the // use the FRIEND_TEST macro to declare your tests as friends of the
@ -40,19 +40,22 @@
// //
// class MyClass { // class MyClass {
// private: // private:
// void MyMethod(); // void PrivateMethod();
// FRIEND_TEST(MyClassTest, MyMethod); // FRIEND_TEST(MyClassTest, PrivateMethodWorks);
// }; // };
// //
// class MyClassTest : public testing::Test { // class MyClassTest : public testing::Test {
// // ... // // ...
// }; // };
// //
// TEST_F(MyClassTest, MyMethod) { // TEST_F(MyClassTest, PrivateMethodWorks) {
// // Can call MyClass::MyMethod() here. // // Can call MyClass::PrivateMethod() here.
// } // }
//
// Note: The test class must be in the same namespace as the class being tested.
// For example, putting MyClassTest in an anonymous namespace will not work.
#define FRIEND_TEST(test_case_name, test_name)\ #define FRIEND_TEST(test_case_name, test_name)\
friend class test_case_name##_##test_name##_Test friend class test_case_name##_##test_name##_Test
#endif // GTEST_INCLUDE_GTEST_GTEST_PROD_H_ #endif // GOOGLETEST_INCLUDE_GTEST_GTEST_PROD_H_

View File

@ -0,0 +1,56 @@
# Customization Points
The custom directory is an injection point for custom user configurations.
## Header `gtest.h`
### The following macros can be defined:
* `GTEST_OS_STACK_TRACE_GETTER_` - The name of an implementation of
`OsStackTraceGetterInterface`.
* `GTEST_CUSTOM_TEMPDIR_FUNCTION_` - An override for `testing::TempDir()`. See
`testing::TempDir` for semantics and signature.
## Header `gtest-port.h`
The following macros can be defined:
### Flag related macros:
* `GTEST_FLAG(flag_name)`
* `GTEST_USE_OWN_FLAGFILE_FLAG_` - Define to 0 when the system provides its
own flagfile flag parsing.
* `GTEST_DECLARE_bool_(name)`
* `GTEST_DECLARE_int32_(name)`
* `GTEST_DECLARE_string_(name)`
* `GTEST_DEFINE_bool_(name, default_val, doc)`
* `GTEST_DEFINE_int32_(name, default_val, doc)`
* `GTEST_DEFINE_string_(name, default_val, doc)`
### Logging:
* `GTEST_LOG_(severity)`
* `GTEST_CHECK_(condition)`
* Functions `LogToStderr()` and `FlushInfoLog()` have to be provided too.
### Threading:
* `GTEST_HAS_NOTIFICATION_` - Enabled if Notification is already provided.
* `GTEST_HAS_MUTEX_AND_THREAD_LOCAL_` - Enabled if `Mutex` and `ThreadLocal`
are already provided. Must also provide `GTEST_DECLARE_STATIC_MUTEX_(mutex)`
and `GTEST_DEFINE_STATIC_MUTEX_(mutex)`
* `GTEST_EXCLUSIVE_LOCK_REQUIRED_(locks)`
* `GTEST_LOCK_EXCLUDED_(locks)`
### Underlying library support features
* `GTEST_HAS_CXXABI_H_`
### Exporting API symbols:
* `GTEST_API_` - Specifier for exported symbols.
## Header `gtest-printers.h`
* See documentation at `gtest/gtest-printers.h` for details on how to define a
custom printer.

View File

@ -27,43 +27,11 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// //
// Injection point for custom user configurations. // Injection point for custom user configurations. See README for details
// The following macros can be defined:
//
// Flag related macros:
// GTEST_FLAG(flag_name)
// GTEST_USE_OWN_FLAGFILE_FLAG_ - Define to 0 when the system provides its
// own flagfile flag parsing.
// GTEST_DECLARE_bool_(name)
// GTEST_DECLARE_int32_(name)
// GTEST_DECLARE_string_(name)
// GTEST_DEFINE_bool_(name, default_val, doc)
// GTEST_DEFINE_int32_(name, default_val, doc)
// GTEST_DEFINE_string_(name, default_val, doc)
//
// Test filtering:
// GTEST_TEST_FILTER_ENV_VAR_ - The name of an environment variable that
// will be used if --GTEST_FLAG(test_filter)
// is not provided.
//
// Logging:
// GTEST_LOG_(severity)
// GTEST_CHECK_(condition)
// Functions LogToStderr() and FlushInfoLog() have to be provided too.
//
// Threading:
// GTEST_HAS_NOTIFICATION_ - Enabled if Notification is already provided.
// GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ - Enabled if Mutex and ThreadLocal are
// already provided.
// Must also provide GTEST_DECLARE_STATIC_MUTEX_(mutex) and
// GTEST_DEFINE_STATIC_MUTEX_(mutex)
//
// GTEST_EXCLUSIVE_LOCK_REQUIRED_(locks)
// GTEST_LOCK_EXCLUDED_(locks)
// //
// ** Custom implementation starts here ** // ** Custom implementation starts here **
#ifndef GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_ #ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_
#define GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_ #define GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_
#endif // GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_ #endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_

View File

@ -31,12 +31,12 @@
// installation of gTest. // installation of gTest.
// It will be included from gtest-printers.h and the overrides in this file // It will be included from gtest-printers.h and the overrides in this file
// will be visible to everyone. // will be visible to everyone.
// See documentation at gtest/gtest-printers.h for details on how to define a //
// custom printer. // Injection point for custom user configurations. See README for details
// //
// ** Custom implementation starts here ** // ** Custom implementation starts here **
#ifndef GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_ #ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_
#define GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_ #define GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_
#endif // GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_ #endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_

View File

@ -27,15 +27,11 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// //
// Injection point for custom user configurations. // Injection point for custom user configurations. See README for details
// The following macros can be defined:
//
// GTEST_OS_STACK_TRACE_GETTER_ - The name of an implementation of
// OsStackTraceGetterInterface.
// //
// ** Custom implementation starts here ** // ** Custom implementation starts here **
#ifndef GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_H_ #ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_H_
#define GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_H_ #define GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_H_
#endif // GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_H_ #endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_H_

View File

@ -27,19 +27,20 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// //
// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee) // The Google C++ Testing and Mocking Framework (Google Test)
//
// The Google C++ Testing Framework (Google Test)
// //
// This header file defines internal utilities needed for implementing // This header file defines internal utilities needed for implementing
// death tests. They are subject to change without notice. // death tests. They are subject to change without notice.
// GOOGLETEST_CM0001 DO NOT DELETE
#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ #ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_
#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ #define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_
#include "gtest/gtest-matchers.h"
#include "gtest/internal/gtest-internal.h" #include "gtest/internal/gtest-internal.h"
#include <stdio.h> #include <stdio.h>
#include <memory>
namespace testing { namespace testing {
namespace internal { namespace internal {
@ -53,6 +54,9 @@ const char kInternalRunDeathTestFlag[] = "internal_run_death_test";
#if GTEST_HAS_DEATH_TEST #if GTEST_HAS_DEATH_TEST
GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
/* class A needs to have dll-interface to be used by clients of class B */)
// DeathTest is a class that hides much of the complexity of the // DeathTest is a class that hides much of the complexity of the
// GTEST_DEATH_TEST_ macro. It is abstract; its static Create method // GTEST_DEATH_TEST_ macro. It is abstract; its static Create method
// returns a concrete class that depends on the prevailing death test // returns a concrete class that depends on the prevailing death test
@ -76,7 +80,7 @@ class GTEST_API_ DeathTest {
// argument is set. If the death test should be skipped, the pointer // argument is set. If the death test should be skipped, the pointer
// is set to NULL; otherwise, it is set to the address of a new concrete // is set to NULL; otherwise, it is set to the address of a new concrete
// DeathTest object that controls the execution of the current test. // DeathTest object that controls the execution of the current test.
static bool Create(const char* statement, const RE* regex, static bool Create(const char* statement, Matcher<const std::string&> matcher,
const char* file, int line, DeathTest** test); const char* file, int line, DeathTest** test);
DeathTest(); DeathTest();
virtual ~DeathTest() { } virtual ~DeathTest() { }
@ -136,25 +140,50 @@ class GTEST_API_ DeathTest {
GTEST_DISALLOW_COPY_AND_ASSIGN_(DeathTest); GTEST_DISALLOW_COPY_AND_ASSIGN_(DeathTest);
}; };
GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251
// Factory interface for death tests. May be mocked out for testing. // Factory interface for death tests. May be mocked out for testing.
class DeathTestFactory { class DeathTestFactory {
public: public:
virtual ~DeathTestFactory() { } virtual ~DeathTestFactory() { }
virtual bool Create(const char* statement, const RE* regex, virtual bool Create(const char* statement,
const char* file, int line, DeathTest** test) = 0; Matcher<const std::string&> matcher, const char* file,
int line, DeathTest** test) = 0;
}; };
// A concrete DeathTestFactory implementation for normal use. // A concrete DeathTestFactory implementation for normal use.
class DefaultDeathTestFactory : public DeathTestFactory { class DefaultDeathTestFactory : public DeathTestFactory {
public: public:
virtual bool Create(const char* statement, const RE* regex, bool Create(const char* statement, Matcher<const std::string&> matcher,
const char* file, int line, DeathTest** test); const char* file, int line, DeathTest** test) override;
}; };
// Returns true if exit_status describes a process that was terminated // Returns true if exit_status describes a process that was terminated
// by a signal, or exited normally with a nonzero exit code. // by a signal, or exited normally with a nonzero exit code.
GTEST_API_ bool ExitedUnsuccessfully(int exit_status); GTEST_API_ bool ExitedUnsuccessfully(int exit_status);
// A string passed to EXPECT_DEATH (etc.) is caught by one of these overloads
// and interpreted as a regex (rather than an Eq matcher) for legacy
// compatibility.
inline Matcher<const ::std::string&> MakeDeathTestMatcher(
::testing::internal::RE regex) {
return ContainsRegex(regex.pattern());
}
inline Matcher<const ::std::string&> MakeDeathTestMatcher(const char* regex) {
return ContainsRegex(regex);
}
inline Matcher<const ::std::string&> MakeDeathTestMatcher(
const ::std::string& regex) {
return ContainsRegex(regex);
}
// If a Matcher<const ::std::string&> is passed to EXPECT_DEATH (etc.), it's
// used directly.
inline Matcher<const ::std::string&> MakeDeathTestMatcher(
Matcher<const ::std::string&> matcher) {
return matcher;
}
// Traps C++ exceptions escaping statement and reports them as test // Traps C++ exceptions escaping statement and reports them as test
// failures. Note that trapping SEH exceptions is not implemented here. // failures. Note that trapping SEH exceptions is not implemented here.
# if GTEST_HAS_EXCEPTIONS # if GTEST_HAS_EXCEPTIONS
@ -182,50 +211,53 @@ GTEST_API_ bool ExitedUnsuccessfully(int exit_status);
// This macro is for implementing ASSERT_DEATH*, EXPECT_DEATH*, // This macro is for implementing ASSERT_DEATH*, EXPECT_DEATH*,
// ASSERT_EXIT*, and EXPECT_EXIT*. // ASSERT_EXIT*, and EXPECT_EXIT*.
# define GTEST_DEATH_TEST_(statement, predicate, regex, fail) \ #define GTEST_DEATH_TEST_(statement, predicate, regex_or_matcher, fail) \
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
if (::testing::internal::AlwaysTrue()) { \ if (::testing::internal::AlwaysTrue()) { \
const ::testing::internal::RE& gtest_regex = (regex); \ ::testing::internal::DeathTest* gtest_dt; \
::testing::internal::DeathTest* gtest_dt; \ if (!::testing::internal::DeathTest::Create( \
if (!::testing::internal::DeathTest::Create(#statement, &gtest_regex, \ #statement, \
__FILE__, __LINE__, &gtest_dt)) { \ ::testing::internal::MakeDeathTestMatcher(regex_or_matcher), \
goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \ __FILE__, __LINE__, &gtest_dt)) { \
} \ goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \
if (gtest_dt != NULL) { \ } \
::testing::internal::scoped_ptr< ::testing::internal::DeathTest> \ if (gtest_dt != nullptr) { \
gtest_dt_ptr(gtest_dt); \ std::unique_ptr< ::testing::internal::DeathTest> gtest_dt_ptr(gtest_dt); \
switch (gtest_dt->AssumeRole()) { \ switch (gtest_dt->AssumeRole()) { \
case ::testing::internal::DeathTest::OVERSEE_TEST: \ case ::testing::internal::DeathTest::OVERSEE_TEST: \
if (!gtest_dt->Passed(predicate(gtest_dt->Wait()))) { \ if (!gtest_dt->Passed(predicate(gtest_dt->Wait()))) { \
goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \ goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \
} \ } \
break; \ break; \
case ::testing::internal::DeathTest::EXECUTE_TEST: { \ case ::testing::internal::DeathTest::EXECUTE_TEST: { \
::testing::internal::DeathTest::ReturnSentinel \ ::testing::internal::DeathTest::ReturnSentinel gtest_sentinel( \
gtest_sentinel(gtest_dt); \ gtest_dt); \
GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, gtest_dt); \ GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, gtest_dt); \
gtest_dt->Abort(::testing::internal::DeathTest::TEST_DID_NOT_DIE); \ gtest_dt->Abort(::testing::internal::DeathTest::TEST_DID_NOT_DIE); \
break; \ break; \
} \ } \
default: \ default: \
break; \ break; \
} \ } \
} \ } \
} else \ } else \
GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__): \ GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__) \
fail(::testing::internal::DeathTest::LastMessage()) : fail(::testing::internal::DeathTest::LastMessage())
// The symbol "fail" here expands to something into which a message // The symbol "fail" here expands to something into which a message
// can be streamed. // can be streamed.
// This macro is for implementing ASSERT/EXPECT_DEBUG_DEATH when compiled in // This macro is for implementing ASSERT/EXPECT_DEBUG_DEATH when compiled in
// NDEBUG mode. In this case we need the statements to be executed, the regex is // NDEBUG mode. In this case we need the statements to be executed and the macro
// ignored, and the macro must accept a streamed message even though the message // must accept a streamed message even though the message is never printed.
// is never printed. // The regex object is not evaluated, but it is used to prevent "unused"
# define GTEST_EXECUTE_STATEMENT_(statement, regex) \ // warnings and to avoid an expression that doesn't compile in debug mode.
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ #define GTEST_EXECUTE_STATEMENT_(statement, regex_or_matcher) \
if (::testing::internal::AlwaysTrue()) { \ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ if (::testing::internal::AlwaysTrue()) { \
} else \ GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
} else if (!::testing::internal::AlwaysTrue()) { \
::testing::internal::MakeDeathTestMatcher(regex_or_matcher); \
} else \
::testing::Message() ::testing::Message()
// A class representing the parsed contents of the // A class representing the parsed contents of the
@ -264,56 +296,9 @@ class InternalRunDeathTestFlag {
// the flag is specified; otherwise returns NULL. // the flag is specified; otherwise returns NULL.
InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag(); InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag();
#else // GTEST_HAS_DEATH_TEST
// This macro is used for implementing macros such as
// EXPECT_DEATH_IF_SUPPORTED and ASSERT_DEATH_IF_SUPPORTED on systems where
// death tests are not supported. Those macros must compile on such systems
// iff EXPECT_DEATH and ASSERT_DEATH compile with the same parameters on
// systems that support death tests. This allows one to write such a macro
// on a system that does not support death tests and be sure that it will
// compile on a death-test supporting system.
//
// Parameters:
// statement - A statement that a macro such as EXPECT_DEATH would test
// for program termination. This macro has to make sure this
// statement is compiled but not executed, to ensure that
// EXPECT_DEATH_IF_SUPPORTED compiles with a certain
// parameter iff EXPECT_DEATH compiles with it.
// regex - A regex that a macro such as EXPECT_DEATH would use to test
// the output of statement. This parameter has to be
// compiled but not evaluated by this macro, to ensure that
// this macro only accepts expressions that a macro such as
// EXPECT_DEATH would accept.
// terminator - Must be an empty statement for EXPECT_DEATH_IF_SUPPORTED
// and a return statement for ASSERT_DEATH_IF_SUPPORTED.
// This ensures that ASSERT_DEATH_IF_SUPPORTED will not
// compile inside functions where ASSERT_DEATH doesn't
// compile.
//
// The branch that has an always false condition is used to ensure that
// statement and regex are compiled (and thus syntactically correct) but
// never executed. The unreachable code macro protects the terminator
// statement from generating an 'unreachable code' warning in case
// statement unconditionally returns or throws. The Message constructor at
// the end allows the syntax of streaming additional messages into the
// macro, for compilational compatibility with EXPECT_DEATH/ASSERT_DEATH.
# define GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, terminator) \
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
if (::testing::internal::AlwaysTrue()) { \
GTEST_LOG_(WARNING) \
<< "Death tests are not supported on this platform.\n" \
<< "Statement '" #statement "' cannot be verified."; \
} else if (::testing::internal::AlwaysFalse()) { \
::testing::internal::RE::PartialMatch(".*", (regex)); \
GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
terminator; \
} else \
::testing::Message()
#endif // GTEST_HAS_DEATH_TEST #endif // GTEST_HAS_DEATH_TEST
} // namespace internal } // namespace internal
} // namespace testing } // namespace testing
#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ #endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_

View File

@ -27,21 +27,24 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// //
// Author: keith.ray@gmail.com (Keith Ray)
//
// Google Test filepath utilities // Google Test filepath utilities
// //
// This header file declares classes and functions used internally by // This header file declares classes and functions used internally by
// Google Test. They are subject to change without notice. // Google Test. They are subject to change without notice.
// //
// This file is #included in <gtest/internal/gtest-internal.h>. // This file is #included in gtest/internal/gtest-internal.h.
// Do not include this header file separately! // Do not include this header file separately!
#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ // GOOGLETEST_CM0001 DO NOT DELETE
#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_
#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_
#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_
#include "gtest/internal/gtest-string.h" #include "gtest/internal/gtest-string.h"
GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
/* class A needs to have dll-interface to be used by clients of class B */)
namespace testing { namespace testing {
namespace internal { namespace internal {
@ -107,7 +110,7 @@ class GTEST_API_ FilePath {
const FilePath& base_name, const FilePath& base_name,
const char* extension); const char* extension);
// Returns true iff the path is "". // Returns true if and only if the path is "".
bool IsEmpty() const { return pathname_.empty(); } bool IsEmpty() const { return pathname_.empty(); }
// If input name has a trailing separator character, removes it and returns // If input name has a trailing separator character, removes it and returns
@ -192,7 +195,7 @@ class GTEST_API_ FilePath {
void Normalize(); void Normalize();
// Returns a pointer to the last occurence of a valid path separator in // Returns a pointer to the last occurrence of a valid path separator in
// the FilePath. On Windows, for example, both '/' and '\' are valid path // the FilePath. On Windows, for example, both '/' and '\' are valid path
// separators. Returns NULL if no path separator was found. // separators. Returns NULL if no path separator was found.
const char* FindLastPathSeparator() const; const char* FindLastPathSeparator() const;
@ -203,4 +206,6 @@ class GTEST_API_ FilePath {
} // namespace internal } // namespace internal
} // namespace testing } // namespace testing
#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251
#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_

File diff suppressed because it is too large Load Diff

View File

@ -26,33 +26,32 @@
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Author: vladl@google.com (Vlad Losev)
// Type and function utilities for implementing parameterized tests. // Type and function utilities for implementing parameterized tests.
#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_ // GOOGLETEST_CM0001 DO NOT DELETE
#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_
#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_
#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_
#include <ctype.h> #include <ctype.h>
#include <cassert>
#include <iterator> #include <iterator>
#include <memory>
#include <set> #include <set>
#include <tuple>
#include <type_traits>
#include <utility> #include <utility>
#include <vector> #include <vector>
// scripts/fuse_gtest.py depends on gtest's own header being #included
// *unconditionally*. Therefore these #includes cannot be moved
// inside #if GTEST_HAS_PARAM_TEST.
#include "gtest/internal/gtest-internal.h" #include "gtest/internal/gtest-internal.h"
#include "gtest/internal/gtest-linked_ptr.h"
#include "gtest/internal/gtest-port.h" #include "gtest/internal/gtest-port.h"
#include "gtest/gtest-printers.h" #include "gtest/gtest-printers.h"
#include "gtest/gtest-test-part.h"
#if GTEST_HAS_PARAM_TEST
namespace testing { namespace testing {
// Input to a parameterized test name generator, describing a test parameter. // Input to a parameterized test name generator, describing a test parameter.
// Consists of the parameter value and the integer parameter index. // Consists of the parameter value and the integer parameter index.
template <class ParamType> template <class ParamType>
@ -76,13 +75,14 @@ struct PrintToStringParamName {
namespace internal { namespace internal {
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. // INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
// // Utility Functions
// Outputs a message explaining invalid registration of different // Outputs a message explaining invalid registration of different
// fixture class for the same test case. This may happen when // fixture class for the same test suite. This may happen when
// TEST_P macro is used to define two tests with the same name // TEST_P macro is used to define two tests with the same name
// but in different namespaces. // but in different namespaces.
GTEST_API_ void ReportInvalidTestCaseType(const char* test_case_name, GTEST_API_ void ReportInvalidTestSuiteType(const char* test_suite_name,
CodeLocation code_location); CodeLocation code_location);
template <typename> class ParamGeneratorInterface; template <typename> class ParamGeneratorInterface;
template <typename> class ParamGenerator; template <typename> class ParamGenerator;
@ -157,7 +157,7 @@ class ParamIterator {
private: private:
friend class ParamGenerator<T>; friend class ParamGenerator<T>;
explicit ParamIterator(ParamIteratorInterface<T>* impl) : impl_(impl) {} explicit ParamIterator(ParamIteratorInterface<T>* impl) : impl_(impl) {}
scoped_ptr<ParamIteratorInterface<T> > impl_; std::unique_ptr<ParamIteratorInterface<T> > impl_;
}; };
// ParamGeneratorInterface<T> is the binary interface to access generators // ParamGeneratorInterface<T> is the binary interface to access generators
@ -196,7 +196,7 @@ class ParamGenerator {
iterator end() const { return iterator(impl_->End()); } iterator end() const { return iterator(impl_->End()); }
private: private:
linked_ptr<const ParamGeneratorInterface<T> > impl_; std::shared_ptr<const ParamGeneratorInterface<T> > impl_;
}; };
// Generates values from a range of two comparable values. Can be used to // Generates values from a range of two comparable values. Can be used to
@ -209,12 +209,12 @@ class RangeGenerator : public ParamGeneratorInterface<T> {
RangeGenerator(T begin, T end, IncrementT step) RangeGenerator(T begin, T end, IncrementT step)
: begin_(begin), end_(end), : begin_(begin), end_(end),
step_(step), end_index_(CalculateEndIndex(begin, end, step)) {} step_(step), end_index_(CalculateEndIndex(begin, end, step)) {}
virtual ~RangeGenerator() {} ~RangeGenerator() override {}
virtual ParamIteratorInterface<T>* Begin() const { ParamIteratorInterface<T>* Begin() const override {
return new Iterator(this, begin_, 0, step_); return new Iterator(this, begin_, 0, step_);
} }
virtual ParamIteratorInterface<T>* End() const { ParamIteratorInterface<T>* End() const override {
return new Iterator(this, end_, end_index_, step_); return new Iterator(this, end_, end_index_, step_);
} }
@ -224,20 +224,20 @@ class RangeGenerator : public ParamGeneratorInterface<T> {
Iterator(const ParamGeneratorInterface<T>* base, T value, int index, Iterator(const ParamGeneratorInterface<T>* base, T value, int index,
IncrementT step) IncrementT step)
: base_(base), value_(value), index_(index), step_(step) {} : base_(base), value_(value), index_(index), step_(step) {}
virtual ~Iterator() {} ~Iterator() override {}
virtual const ParamGeneratorInterface<T>* BaseGenerator() const { const ParamGeneratorInterface<T>* BaseGenerator() const override {
return base_; return base_;
} }
virtual void Advance() { void Advance() override {
value_ = static_cast<T>(value_ + step_); value_ = static_cast<T>(value_ + step_);
index_++; index_++;
} }
virtual ParamIteratorInterface<T>* Clone() const { ParamIteratorInterface<T>* Clone() const override {
return new Iterator(*this); return new Iterator(*this);
} }
virtual const T* Current() const { return &value_; } const T* Current() const override { return &value_; }
virtual bool Equals(const ParamIteratorInterface<T>& other) const { bool Equals(const ParamIteratorInterface<T>& other) const override {
// Having the same base generator guarantees that the other // Having the same base generator guarantees that the other
// iterator is of the same type and we can downcast. // iterator is of the same type and we can downcast.
GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
@ -294,12 +294,12 @@ class ValuesInIteratorRangeGenerator : public ParamGeneratorInterface<T> {
template <typename ForwardIterator> template <typename ForwardIterator>
ValuesInIteratorRangeGenerator(ForwardIterator begin, ForwardIterator end) ValuesInIteratorRangeGenerator(ForwardIterator begin, ForwardIterator end)
: container_(begin, end) {} : container_(begin, end) {}
virtual ~ValuesInIteratorRangeGenerator() {} ~ValuesInIteratorRangeGenerator() override {}
virtual ParamIteratorInterface<T>* Begin() const { ParamIteratorInterface<T>* Begin() const override {
return new Iterator(this, container_.begin()); return new Iterator(this, container_.begin());
} }
virtual ParamIteratorInterface<T>* End() const { ParamIteratorInterface<T>* End() const override {
return new Iterator(this, container_.end()); return new Iterator(this, container_.end());
} }
@ -311,16 +311,16 @@ class ValuesInIteratorRangeGenerator : public ParamGeneratorInterface<T> {
Iterator(const ParamGeneratorInterface<T>* base, Iterator(const ParamGeneratorInterface<T>* base,
typename ContainerType::const_iterator iterator) typename ContainerType::const_iterator iterator)
: base_(base), iterator_(iterator) {} : base_(base), iterator_(iterator) {}
virtual ~Iterator() {} ~Iterator() override {}
virtual const ParamGeneratorInterface<T>* BaseGenerator() const { const ParamGeneratorInterface<T>* BaseGenerator() const override {
return base_; return base_;
} }
virtual void Advance() { void Advance() override {
++iterator_; ++iterator_;
value_.reset(); value_.reset();
} }
virtual ParamIteratorInterface<T>* Clone() const { ParamIteratorInterface<T>* Clone() const override {
return new Iterator(*this); return new Iterator(*this);
} }
// We need to use cached value referenced by iterator_ because *iterator_ // We need to use cached value referenced by iterator_ because *iterator_
@ -330,12 +330,11 @@ class ValuesInIteratorRangeGenerator : public ParamGeneratorInterface<T> {
// can advance iterator_ beyond the end of the range, and we cannot // can advance iterator_ beyond the end of the range, and we cannot
// detect that fact. The client code, on the other hand, is // detect that fact. The client code, on the other hand, is
// responsible for not calling Current() on an out-of-range iterator. // responsible for not calling Current() on an out-of-range iterator.
virtual const T* Current() const { const T* Current() const override {
if (value_.get() == NULL) if (value_.get() == nullptr) value_.reset(new T(*iterator_));
value_.reset(new T(*iterator_));
return value_.get(); return value_.get();
} }
virtual bool Equals(const ParamIteratorInterface<T>& other) const { bool Equals(const ParamIteratorInterface<T>& other) const override {
// Having the same base generator guarantees that the other // Having the same base generator guarantees that the other
// iterator is of the same type and we can downcast. // iterator is of the same type and we can downcast.
GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
@ -358,9 +357,9 @@ class ValuesInIteratorRangeGenerator : public ParamGeneratorInterface<T> {
// A cached value of *iterator_. We keep it here to allow access by // A cached value of *iterator_. We keep it here to allow access by
// pointer in the wrapping iterator's operator->(). // pointer in the wrapping iterator's operator->().
// value_ needs to be mutable to be accessed in Current(). // value_ needs to be mutable to be accessed in Current().
// Use of scoped_ptr helps manage cached value's lifetime, // Use of std::unique_ptr helps manage cached value's lifetime,
// which is bound by the lifespan of the iterator itself. // which is bound by the lifespan of the iterator itself.
mutable scoped_ptr<const T> value_; mutable std::unique_ptr<const T> value_;
}; // class ValuesInIteratorRangeGenerator::Iterator }; // class ValuesInIteratorRangeGenerator::Iterator
// No implementation - assignment is unsupported. // No implementation - assignment is unsupported.
@ -380,25 +379,12 @@ std::string DefaultParamName(const TestParamInfo<ParamType>& info) {
return name_stream.GetString(); return name_stream.GetString();
} }
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. template <typename T = int>
// void TestNotEmpty() {
// Parameterized test name overload helpers, which help the static_assert(sizeof(T) == 0, "Empty arguments are not allowed.");
// INSTANTIATE_TEST_CASE_P macro choose between the default parameterized
// test name generator and user param name generator.
template <class ParamType, class ParamNameGenFunctor>
ParamNameGenFunctor GetParamNameGen(ParamNameGenFunctor func) {
return func;
}
template <class ParamType>
struct ParamNameGenFunc {
typedef std::string Type(const TestParamInfo<ParamType>&);
};
template <class ParamType>
typename ParamNameGenFunc<ParamType>::Type *GetParamNameGen() {
return DefaultParamName;
} }
template <typename T = int>
void TestNotEmpty(const T&) {}
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. // INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
// //
@ -410,7 +396,7 @@ class ParameterizedTestFactory : public TestFactoryBase {
typedef typename TestClass::ParamType ParamType; typedef typename TestClass::ParamType ParamType;
explicit ParameterizedTestFactory(ParamType parameter) : explicit ParameterizedTestFactory(ParamType parameter) :
parameter_(parameter) {} parameter_(parameter) {}
virtual Test* CreateTest() { Test* CreateTest() override {
TestClass::SetParam(&parameter_); TestClass::SetParam(&parameter_);
return new TestClass(); return new TestClass();
} }
@ -438,19 +424,19 @@ class TestMetaFactoryBase {
// TestMetaFactory creates test factories for passing into // TestMetaFactory creates test factories for passing into
// MakeAndRegisterTestInfo function. Since MakeAndRegisterTestInfo receives // MakeAndRegisterTestInfo function. Since MakeAndRegisterTestInfo receives
// ownership of test factory pointer, same factory object cannot be passed // ownership of test factory pointer, same factory object cannot be passed
// into that method twice. But ParameterizedTestCaseInfo is going to call // into that method twice. But ParameterizedTestSuiteInfo is going to call
// it for each Test/Parameter value combination. Thus it needs meta factory // it for each Test/Parameter value combination. Thus it needs meta factory
// creator class. // creator class.
template <class TestCase> template <class TestSuite>
class TestMetaFactory class TestMetaFactory
: public TestMetaFactoryBase<typename TestCase::ParamType> { : public TestMetaFactoryBase<typename TestSuite::ParamType> {
public: public:
typedef typename TestCase::ParamType ParamType; using ParamType = typename TestSuite::ParamType;
TestMetaFactory() {} TestMetaFactory() {}
virtual TestFactoryBase* CreateTestFactory(ParamType parameter) { TestFactoryBase* CreateTestFactory(ParamType parameter) override {
return new ParameterizedTestFactory<TestCase>(parameter); return new ParameterizedTestFactory<TestSuite>(parameter);
} }
private: private:
@ -459,113 +445,128 @@ class TestMetaFactory
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. // INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
// //
// ParameterizedTestCaseInfoBase is a generic interface // ParameterizedTestSuiteInfoBase is a generic interface
// to ParameterizedTestCaseInfo classes. ParameterizedTestCaseInfoBase // to ParameterizedTestSuiteInfo classes. ParameterizedTestSuiteInfoBase
// accumulates test information provided by TEST_P macro invocations // accumulates test information provided by TEST_P macro invocations
// and generators provided by INSTANTIATE_TEST_CASE_P macro invocations // and generators provided by INSTANTIATE_TEST_SUITE_P macro invocations
// and uses that information to register all resulting test instances // and uses that information to register all resulting test instances
// in RegisterTests method. The ParameterizeTestCaseRegistry class holds // in RegisterTests method. The ParameterizeTestSuiteRegistry class holds
// a collection of pointers to the ParameterizedTestCaseInfo objects // a collection of pointers to the ParameterizedTestSuiteInfo objects
// and calls RegisterTests() on each of them when asked. // and calls RegisterTests() on each of them when asked.
class ParameterizedTestCaseInfoBase { class ParameterizedTestSuiteInfoBase {
public: public:
virtual ~ParameterizedTestCaseInfoBase() {} virtual ~ParameterizedTestSuiteInfoBase() {}
// Base part of test case name for display purposes. // Base part of test suite name for display purposes.
virtual const string& GetTestCaseName() const = 0; virtual const std::string& GetTestSuiteName() const = 0;
// Test case id to verify identity. // Test suite id to verify identity.
virtual TypeId GetTestCaseTypeId() const = 0; virtual TypeId GetTestSuiteTypeId() const = 0;
// UnitTest class invokes this method to register tests in this // UnitTest class invokes this method to register tests in this
// test case right before running them in RUN_ALL_TESTS macro. // test suite right before running them in RUN_ALL_TESTS macro.
// This method should not be called more then once on any single // This method should not be called more than once on any single
// instance of a ParameterizedTestCaseInfoBase derived class. // instance of a ParameterizedTestSuiteInfoBase derived class.
virtual void RegisterTests() = 0; virtual void RegisterTests() = 0;
protected: protected:
ParameterizedTestCaseInfoBase() {} ParameterizedTestSuiteInfoBase() {}
private: private:
GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseInfoBase); GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestSuiteInfoBase);
}; };
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. // INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
// //
// ParameterizedTestCaseInfo accumulates tests obtained from TEST_P // Report a the name of a test_suit as safe to ignore
// macro invocations for a particular test case and generators // as the side effect of construction of this type.
// obtained from INSTANTIATE_TEST_CASE_P macro invocations for that struct GTEST_API_ MarkAsIgnored {
// test case. It registers tests with all values generated by all explicit MarkAsIgnored(const char* test_suite);
};
GTEST_API_ void InsertSyntheticTestCase(const std::string& name,
CodeLocation location, bool has_test_p);
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
//
// ParameterizedTestSuiteInfo accumulates tests obtained from TEST_P
// macro invocations for a particular test suite and generators
// obtained from INSTANTIATE_TEST_SUITE_P macro invocations for that
// test suite. It registers tests with all values generated by all
// generators when asked. // generators when asked.
template <class TestCase> template <class TestSuite>
class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase { class ParameterizedTestSuiteInfo : public ParameterizedTestSuiteInfoBase {
public: public:
// ParamType and GeneratorCreationFunc are private types but are required // ParamType and GeneratorCreationFunc are private types but are required
// for declarations of public methods AddTestPattern() and // for declarations of public methods AddTestPattern() and
// AddTestCaseInstantiation(). // AddTestSuiteInstantiation().
typedef typename TestCase::ParamType ParamType; using ParamType = typename TestSuite::ParamType;
// A function that returns an instance of appropriate generator type. // A function that returns an instance of appropriate generator type.
typedef ParamGenerator<ParamType>(GeneratorCreationFunc)(); typedef ParamGenerator<ParamType>(GeneratorCreationFunc)();
typedef typename ParamNameGenFunc<ParamType>::Type ParamNameGeneratorFunc; using ParamNameGeneratorFunc = std::string(const TestParamInfo<ParamType>&);
explicit ParameterizedTestCaseInfo( explicit ParameterizedTestSuiteInfo(const char* name,
const char* name, CodeLocation code_location) CodeLocation code_location)
: test_case_name_(name), code_location_(code_location) {} : test_suite_name_(name), code_location_(code_location) {}
// Test case base name for display purposes. // Test suite base name for display purposes.
virtual const string& GetTestCaseName() const { return test_case_name_; } const std::string& GetTestSuiteName() const override {
// Test case id to verify identity. return test_suite_name_;
virtual TypeId GetTestCaseTypeId() const { return GetTypeId<TestCase>(); } }
// Test suite id to verify identity.
TypeId GetTestSuiteTypeId() const override { return GetTypeId<TestSuite>(); }
// TEST_P macro uses AddTestPattern() to record information // TEST_P macro uses AddTestPattern() to record information
// about a single test in a LocalTestInfo structure. // about a single test in a LocalTestInfo structure.
// test_case_name is the base name of the test case (without invocation // test_suite_name is the base name of the test suite (without invocation
// prefix). test_base_name is the name of an individual test without // prefix). test_base_name is the name of an individual test without
// parameter index. For the test SequenceA/FooTest.DoBar/1 FooTest is // parameter index. For the test SequenceA/FooTest.DoBar/1 FooTest is
// test case base name and DoBar is test base name. // test suite base name and DoBar is test base name.
void AddTestPattern(const char* test_case_name, void AddTestPattern(const char* test_suite_name, const char* test_base_name,
const char* test_base_name, TestMetaFactoryBase<ParamType>* meta_factory,
TestMetaFactoryBase<ParamType>* meta_factory) { CodeLocation code_location) {
tests_.push_back(linked_ptr<TestInfo>(new TestInfo(test_case_name, tests_.push_back(std::shared_ptr<TestInfo>(new TestInfo(
test_base_name, test_suite_name, test_base_name, meta_factory, code_location)));
meta_factory)));
} }
// INSTANTIATE_TEST_CASE_P macro uses AddGenerator() to record information // INSTANTIATE_TEST_SUITE_P macro uses AddGenerator() to record information
// about a generator. // about a generator.
int AddTestCaseInstantiation(const string& instantiation_name, int AddTestSuiteInstantiation(const std::string& instantiation_name,
GeneratorCreationFunc* func, GeneratorCreationFunc* func,
ParamNameGeneratorFunc* name_func, ParamNameGeneratorFunc* name_func,
const char* file, const char* file, int line) {
int line) {
instantiations_.push_back( instantiations_.push_back(
InstantiationInfo(instantiation_name, func, name_func, file, line)); InstantiationInfo(instantiation_name, func, name_func, file, line));
return 0; // Return value used only to run this method in namespace scope. return 0; // Return value used only to run this method in namespace scope.
} }
// UnitTest class invokes this method to register tests in this test case // UnitTest class invokes this method to register tests in this test suite
// test cases right before running tests in RUN_ALL_TESTS macro. // right before running tests in RUN_ALL_TESTS macro.
// This method should not be called more then once on any single // This method should not be called more than once on any single
// instance of a ParameterizedTestCaseInfoBase derived class. // instance of a ParameterizedTestSuiteInfoBase derived class.
// UnitTest has a guard to prevent from calling this method more then once. // UnitTest has a guard to prevent from calling this method more than once.
virtual void RegisterTests() { void RegisterTests() override {
bool generated_instantiations = false;
for (typename TestInfoContainer::iterator test_it = tests_.begin(); for (typename TestInfoContainer::iterator test_it = tests_.begin();
test_it != tests_.end(); ++test_it) { test_it != tests_.end(); ++test_it) {
linked_ptr<TestInfo> test_info = *test_it; std::shared_ptr<TestInfo> test_info = *test_it;
for (typename InstantiationContainer::iterator gen_it = for (typename InstantiationContainer::iterator gen_it =
instantiations_.begin(); gen_it != instantiations_.end(); instantiations_.begin(); gen_it != instantiations_.end();
++gen_it) { ++gen_it) {
const string& instantiation_name = gen_it->name; const std::string& instantiation_name = gen_it->name;
ParamGenerator<ParamType> generator((*gen_it->generator)()); ParamGenerator<ParamType> generator((*gen_it->generator)());
ParamNameGeneratorFunc* name_func = gen_it->name_func; ParamNameGeneratorFunc* name_func = gen_it->name_func;
const char* file = gen_it->file; const char* file = gen_it->file;
int line = gen_it->line; int line = gen_it->line;
string test_case_name; std::string test_suite_name;
if ( !instantiation_name.empty() ) if ( !instantiation_name.empty() )
test_case_name = instantiation_name + "/"; test_suite_name = instantiation_name + "/";
test_case_name += test_info->test_case_base_name; test_suite_name += test_info->test_suite_base_name;
size_t i = 0; size_t i = 0;
std::set<std::string> test_param_names; std::set<std::string> test_param_names;
for (typename ParamGenerator<ParamType>::iterator param_it = for (typename ParamGenerator<ParamType>::iterator param_it =
generator.begin(); generator.begin();
param_it != generator.end(); ++param_it, ++i) { param_it != generator.end(); ++param_it, ++i) {
generated_instantiations = true;
Message test_name_stream; Message test_name_stream;
std::string param_name = name_func( std::string param_name = name_func(
@ -582,39 +583,48 @@ class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase {
test_param_names.insert(param_name); test_param_names.insert(param_name);
test_name_stream << test_info->test_base_name << "/" << param_name; if (!test_info->test_base_name.empty()) {
test_name_stream << test_info->test_base_name << "/";
}
test_name_stream << param_name;
MakeAndRegisterTestInfo( MakeAndRegisterTestInfo(
test_case_name.c_str(), test_suite_name.c_str(), test_name_stream.GetString().c_str(),
test_name_stream.GetString().c_str(), nullptr, // No type parameter.
NULL, // No type parameter. PrintToString(*param_it).c_str(), test_info->code_location,
PrintToString(*param_it).c_str(), GetTestSuiteTypeId(),
code_location_, SuiteApiResolver<TestSuite>::GetSetUpCaseOrSuite(file, line),
GetTestCaseTypeId(), SuiteApiResolver<TestSuite>::GetTearDownCaseOrSuite(file, line),
TestCase::SetUpTestCase,
TestCase::TearDownTestCase,
test_info->test_meta_factory->CreateTestFactory(*param_it)); test_info->test_meta_factory->CreateTestFactory(*param_it));
} // for param_it } // for param_it
} // for gen_it } // for gen_it
} // for test_it } // for test_it
} // RegisterTests
if (!generated_instantiations) {
// There are no generaotrs, or they all generate nothing ...
InsertSyntheticTestCase(GetTestSuiteName(), code_location_,
!tests_.empty());
}
} // RegisterTests
private: private:
// LocalTestInfo structure keeps information about a single test registered // LocalTestInfo structure keeps information about a single test registered
// with TEST_P macro. // with TEST_P macro.
struct TestInfo { struct TestInfo {
TestInfo(const char* a_test_case_base_name, TestInfo(const char* a_test_suite_base_name, const char* a_test_base_name,
const char* a_test_base_name, TestMetaFactoryBase<ParamType>* a_test_meta_factory,
TestMetaFactoryBase<ParamType>* a_test_meta_factory) : CodeLocation a_code_location)
test_case_base_name(a_test_case_base_name), : test_suite_base_name(a_test_suite_base_name),
test_base_name(a_test_base_name), test_base_name(a_test_base_name),
test_meta_factory(a_test_meta_factory) {} test_meta_factory(a_test_meta_factory),
code_location(a_code_location) {}
const string test_case_base_name; const std::string test_suite_base_name;
const string test_base_name; const std::string test_base_name;
const scoped_ptr<TestMetaFactoryBase<ParamType> > test_meta_factory; const std::unique_ptr<TestMetaFactoryBase<ParamType> > test_meta_factory;
const CodeLocation code_location;
}; };
typedef ::std::vector<linked_ptr<TestInfo> > TestInfoContainer; using TestInfoContainer = ::std::vector<std::shared_ptr<TestInfo> >;
// Records data received from INSTANTIATE_TEST_CASE_P macros: // Records data received from INSTANTIATE_TEST_SUITE_P macros:
// <Instantiation name, Sequence generator creation function, // <Instantiation name, Sequence generator creation function,
// Name generator function, Source file, Source line> // Name generator function, Source file, Source line>
struct InstantiationInfo { struct InstantiationInfo {
@ -651,81 +661,287 @@ class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase {
return true; return true;
} }
const string test_case_name_; const std::string test_suite_name_;
CodeLocation code_location_; CodeLocation code_location_;
TestInfoContainer tests_; TestInfoContainer tests_;
InstantiationContainer instantiations_; InstantiationContainer instantiations_;
GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseInfo); GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestSuiteInfo);
}; // class ParameterizedTestCaseInfo }; // class ParameterizedTestSuiteInfo
// Legacy API is deprecated but still available
#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
template <class TestCase>
using ParameterizedTestCaseInfo = ParameterizedTestSuiteInfo<TestCase>;
#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. // INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
// //
// ParameterizedTestCaseRegistry contains a map of ParameterizedTestCaseInfoBase // ParameterizedTestSuiteRegistry contains a map of
// classes accessed by test case names. TEST_P and INSTANTIATE_TEST_CASE_P // ParameterizedTestSuiteInfoBase classes accessed by test suite names. TEST_P
// macros use it to locate their corresponding ParameterizedTestCaseInfo // and INSTANTIATE_TEST_SUITE_P macros use it to locate their corresponding
// descriptors. // ParameterizedTestSuiteInfo descriptors.
class ParameterizedTestCaseRegistry { class ParameterizedTestSuiteRegistry {
public: public:
ParameterizedTestCaseRegistry() {} ParameterizedTestSuiteRegistry() {}
~ParameterizedTestCaseRegistry() { ~ParameterizedTestSuiteRegistry() {
for (TestCaseInfoContainer::iterator it = test_case_infos_.begin(); for (auto& test_suite_info : test_suite_infos_) {
it != test_case_infos_.end(); ++it) { delete test_suite_info;
delete *it;
} }
} }
// Looks up or creates and returns a structure containing information about // Looks up or creates and returns a structure containing information about
// tests and instantiations of a particular test case. // tests and instantiations of a particular test suite.
template <class TestCase> template <class TestSuite>
ParameterizedTestCaseInfo<TestCase>* GetTestCasePatternHolder( ParameterizedTestSuiteInfo<TestSuite>* GetTestSuitePatternHolder(
const char* test_case_name, const char* test_suite_name, CodeLocation code_location) {
CodeLocation code_location) { ParameterizedTestSuiteInfo<TestSuite>* typed_test_info = nullptr;
ParameterizedTestCaseInfo<TestCase>* typed_test_info = NULL; for (auto& test_suite_info : test_suite_infos_) {
for (TestCaseInfoContainer::iterator it = test_case_infos_.begin(); if (test_suite_info->GetTestSuiteName() == test_suite_name) {
it != test_case_infos_.end(); ++it) { if (test_suite_info->GetTestSuiteTypeId() != GetTypeId<TestSuite>()) {
if ((*it)->GetTestCaseName() == test_case_name) {
if ((*it)->GetTestCaseTypeId() != GetTypeId<TestCase>()) {
// Complain about incorrect usage of Google Test facilities // Complain about incorrect usage of Google Test facilities
// and terminate the program since we cannot guaranty correct // and terminate the program since we cannot guaranty correct
// test case setup and tear-down in this case. // test suite setup and tear-down in this case.
ReportInvalidTestCaseType(test_case_name, code_location); ReportInvalidTestSuiteType(test_suite_name, code_location);
posix::Abort(); posix::Abort();
} else { } else {
// At this point we are sure that the object we found is of the same // At this point we are sure that the object we found is of the same
// type we are looking for, so we downcast it to that type // type we are looking for, so we downcast it to that type
// without further checks. // without further checks.
typed_test_info = CheckedDowncastToActualType< typed_test_info = CheckedDowncastToActualType<
ParameterizedTestCaseInfo<TestCase> >(*it); ParameterizedTestSuiteInfo<TestSuite> >(test_suite_info);
} }
break; break;
} }
} }
if (typed_test_info == NULL) { if (typed_test_info == nullptr) {
typed_test_info = new ParameterizedTestCaseInfo<TestCase>( typed_test_info = new ParameterizedTestSuiteInfo<TestSuite>(
test_case_name, code_location); test_suite_name, code_location);
test_case_infos_.push_back(typed_test_info); test_suite_infos_.push_back(typed_test_info);
} }
return typed_test_info; return typed_test_info;
} }
void RegisterTests() { void RegisterTests() {
for (TestCaseInfoContainer::iterator it = test_case_infos_.begin(); for (auto& test_suite_info : test_suite_infos_) {
it != test_case_infos_.end(); ++it) { test_suite_info->RegisterTests();
(*it)->RegisterTests();
} }
} }
// Legacy API is deprecated but still available
#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
template <class TestCase>
ParameterizedTestCaseInfo<TestCase>* GetTestCasePatternHolder(
const char* test_case_name, CodeLocation code_location) {
return GetTestSuitePatternHolder<TestCase>(test_case_name, code_location);
}
#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
private:
using TestSuiteInfoContainer = ::std::vector<ParameterizedTestSuiteInfoBase*>;
TestSuiteInfoContainer test_suite_infos_;
GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestSuiteRegistry);
};
// Keep track of what type-parameterized test suite are defined and
// where as well as which are intatiated. This allows susequently
// identifying suits that are defined but never used.
class TypeParameterizedTestSuiteRegistry {
public:
// Add a suite definition
void RegisterTestSuite(const char* test_suite_name,
CodeLocation code_location);
// Add an instantiation of a suit.
void RegisterInstantiation(const char* test_suite_name);
// For each suit repored as defined but not reported as instantiation,
// emit a test that reports that fact (configurably, as an error).
void CheckForInstantiations();
private:
struct TypeParameterizedTestSuiteInfo {
explicit TypeParameterizedTestSuiteInfo(CodeLocation c)
: code_location(c), instantiated(false) {}
CodeLocation code_location;
bool instantiated;
};
std::map<std::string, TypeParameterizedTestSuiteInfo> suites_;
};
} // namespace internal
// Forward declarations of ValuesIn(), which is implemented in
// include/gtest/gtest-param-test.h.
template <class Container>
internal::ParamGenerator<typename Container::value_type> ValuesIn(
const Container& container);
namespace internal {
// Used in the Values() function to provide polymorphic capabilities.
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4100)
#endif
template <typename... Ts>
class ValueArray {
public:
explicit ValueArray(Ts... v) : v_(FlatTupleConstructTag{}, std::move(v)...) {}
template <typename T>
operator ParamGenerator<T>() const { // NOLINT
return ValuesIn(MakeVector<T>(MakeIndexSequence<sizeof...(Ts)>()));
}
private: private:
typedef ::std::vector<ParameterizedTestCaseInfoBase*> TestCaseInfoContainer; template <typename T, size_t... I>
std::vector<T> MakeVector(IndexSequence<I...>) const {
return std::vector<T>{static_cast<T>(v_.template Get<I>())...};
}
TestCaseInfoContainer test_case_infos_; FlatTuple<Ts...> v_;
};
GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseRegistry); #ifdef _MSC_VER
#pragma warning(pop)
#endif
template <typename... T>
class CartesianProductGenerator
: public ParamGeneratorInterface<::std::tuple<T...>> {
public:
typedef ::std::tuple<T...> ParamType;
CartesianProductGenerator(const std::tuple<ParamGenerator<T>...>& g)
: generators_(g) {}
~CartesianProductGenerator() override {}
ParamIteratorInterface<ParamType>* Begin() const override {
return new Iterator(this, generators_, false);
}
ParamIteratorInterface<ParamType>* End() const override {
return new Iterator(this, generators_, true);
}
private:
template <class I>
class IteratorImpl;
template <size_t... I>
class IteratorImpl<IndexSequence<I...>>
: public ParamIteratorInterface<ParamType> {
public:
IteratorImpl(const ParamGeneratorInterface<ParamType>* base,
const std::tuple<ParamGenerator<T>...>& generators, bool is_end)
: base_(base),
begin_(std::get<I>(generators).begin()...),
end_(std::get<I>(generators).end()...),
current_(is_end ? end_ : begin_) {
ComputeCurrentValue();
}
~IteratorImpl() override {}
const ParamGeneratorInterface<ParamType>* BaseGenerator() const override {
return base_;
}
// Advance should not be called on beyond-of-range iterators
// so no component iterators must be beyond end of range, either.
void Advance() override {
assert(!AtEnd());
// Advance the last iterator.
++std::get<sizeof...(T) - 1>(current_);
// if that reaches end, propagate that up.
AdvanceIfEnd<sizeof...(T) - 1>();
ComputeCurrentValue();
}
ParamIteratorInterface<ParamType>* Clone() const override {
return new IteratorImpl(*this);
}
const ParamType* Current() const override { return current_value_.get(); }
bool Equals(const ParamIteratorInterface<ParamType>& other) const override {
// Having the same base generator guarantees that the other
// iterator is of the same type and we can downcast.
GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
<< "The program attempted to compare iterators "
<< "from different generators." << std::endl;
const IteratorImpl* typed_other =
CheckedDowncastToActualType<const IteratorImpl>(&other);
// We must report iterators equal if they both point beyond their
// respective ranges. That can happen in a variety of fashions,
// so we have to consult AtEnd().
if (AtEnd() && typed_other->AtEnd()) return true;
bool same = true;
bool dummy[] = {
(same = same && std::get<I>(current_) ==
std::get<I>(typed_other->current_))...};
(void)dummy;
return same;
}
private:
template <size_t ThisI>
void AdvanceIfEnd() {
if (std::get<ThisI>(current_) != std::get<ThisI>(end_)) return;
bool last = ThisI == 0;
if (last) {
// We are done. Nothing else to propagate.
return;
}
constexpr size_t NextI = ThisI - (ThisI != 0);
std::get<ThisI>(current_) = std::get<ThisI>(begin_);
++std::get<NextI>(current_);
AdvanceIfEnd<NextI>();
}
void ComputeCurrentValue() {
if (!AtEnd())
current_value_ = std::make_shared<ParamType>(*std::get<I>(current_)...);
}
bool AtEnd() const {
bool at_end = false;
bool dummy[] = {
(at_end = at_end || std::get<I>(current_) == std::get<I>(end_))...};
(void)dummy;
return at_end;
}
const ParamGeneratorInterface<ParamType>* const base_;
std::tuple<typename ParamGenerator<T>::iterator...> begin_;
std::tuple<typename ParamGenerator<T>::iterator...> end_;
std::tuple<typename ParamGenerator<T>::iterator...> current_;
std::shared_ptr<ParamType> current_value_;
};
using Iterator = IteratorImpl<typename MakeIndexSequence<sizeof...(T)>::type>;
std::tuple<ParamGenerator<T>...> generators_;
};
template <class... Gen>
class CartesianProductHolder {
public:
CartesianProductHolder(const Gen&... g) : generators_(g...) {}
template <typename... T>
operator ParamGenerator<::std::tuple<T...>>() const {
return ParamGenerator<::std::tuple<T...>>(
new CartesianProductGenerator<T...>(generators_));
}
private:
std::tuple<Gen...> generators_;
}; };
} // namespace internal } // namespace internal
} // namespace testing } // namespace testing
#endif // GTEST_HAS_PARAM_TEST #endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_
#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_

View File

@ -27,25 +27,24 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// //
// The Google C++ Testing Framework (Google Test) // The Google C++ Testing and Mocking Framework (Google Test)
// //
// This header file defines the GTEST_OS_* macro. // This header file defines the GTEST_OS_* macro.
// It is separate from gtest-port.h so that custom/gtest-port.h can include it. // It is separate from gtest-port.h so that custom/gtest-port.h can include it.
#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_ #ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_
#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_ #define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_
// Determines the platform on which Google Test is compiled. // Determines the platform on which Google Test is compiled.
#ifdef __CYGWIN__ #ifdef __CYGWIN__
# define GTEST_OS_CYGWIN 1 # define GTEST_OS_CYGWIN 1
#elif defined __SYMBIAN32__ # elif defined(__MINGW__) || defined(__MINGW32__) || defined(__MINGW64__)
# define GTEST_OS_SYMBIAN 1 # define GTEST_OS_WINDOWS_MINGW 1
# define GTEST_OS_WINDOWS 1
#elif defined _WIN32 #elif defined _WIN32
# define GTEST_OS_WINDOWS 1 # define GTEST_OS_WINDOWS 1
# ifdef _WIN32_WCE # ifdef _WIN32_WCE
# define GTEST_OS_WINDOWS_MOBILE 1 # define GTEST_OS_WINDOWS_MOBILE 1
# elif defined(__MINGW__) || defined(__MINGW32__)
# define GTEST_OS_WINDOWS_MINGW 1
# elif defined(WINAPI_FAMILY) # elif defined(WINAPI_FAMILY)
# include <winapifamily.h> # include <winapifamily.h>
# if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) # if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
@ -54,6 +53,9 @@
# define GTEST_OS_WINDOWS_PHONE 1 # define GTEST_OS_WINDOWS_PHONE 1
# elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) # elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)
# define GTEST_OS_WINDOWS_RT 1 # define GTEST_OS_WINDOWS_RT 1
# elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_TV_TITLE)
# define GTEST_OS_WINDOWS_PHONE 1
# define GTEST_OS_WINDOWS_TV_TITLE 1
# else # else
// WINAPI_FAMILY defined but no known partition matched. // WINAPI_FAMILY defined but no known partition matched.
// Default to desktop. // Default to desktop.
@ -62,13 +64,22 @@
# else # else
# define GTEST_OS_WINDOWS_DESKTOP 1 # define GTEST_OS_WINDOWS_DESKTOP 1
# endif // _WIN32_WCE # endif // _WIN32_WCE
#elif defined __OS2__
# define GTEST_OS_OS2 1
#elif defined __APPLE__ #elif defined __APPLE__
# define GTEST_OS_MAC 1 # define GTEST_OS_MAC 1
# include <TargetConditionals.h>
# if TARGET_OS_IPHONE # if TARGET_OS_IPHONE
# define GTEST_OS_IOS 1 # define GTEST_OS_IOS 1
# endif # endif
#elif defined __DragonFly__
# define GTEST_OS_DRAGONFLY 1
#elif defined __FreeBSD__ #elif defined __FreeBSD__
# define GTEST_OS_FREEBSD 1 # define GTEST_OS_FREEBSD 1
#elif defined __Fuchsia__
# define GTEST_OS_FUCHSIA 1
#elif defined(__GLIBC__) && defined(__FreeBSD_kernel__)
# define GTEST_OS_GNU_KFREEBSD 1
#elif defined __linux__ #elif defined __linux__
# define GTEST_OS_LINUX 1 # define GTEST_OS_LINUX 1
# if defined __ANDROID__ # if defined __ANDROID__
@ -84,10 +95,20 @@
# define GTEST_OS_HPUX 1 # define GTEST_OS_HPUX 1
#elif defined __native_client__ #elif defined __native_client__
# define GTEST_OS_NACL 1 # define GTEST_OS_NACL 1
#elif defined __NetBSD__
# define GTEST_OS_NETBSD 1
#elif defined __OpenBSD__ #elif defined __OpenBSD__
# define GTEST_OS_OPENBSD 1 # define GTEST_OS_OPENBSD 1
#elif defined __QNX__ #elif defined __QNX__
# define GTEST_OS_QNX 1 # define GTEST_OS_QNX 1
#elif defined(__HAIKU__)
#define GTEST_OS_HAIKU 1
#elif defined ESP8266
#define GTEST_OS_ESP8266 1
#elif defined ESP32
#define GTEST_OS_ESP32 1
#elif defined(__XTENSA__)
#define GTEST_OS_XTENSA 1
#endif // __CYGWIN__ #endif // __CYGWIN__
#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_ #endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_

File diff suppressed because it is too large Load Diff

View File

@ -27,19 +27,19 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// //
// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee) // The Google C++ Testing and Mocking Framework (Google Test)
//
// The Google C++ Testing Framework (Google Test)
// //
// This header file declares the String class and functions used internally by // This header file declares the String class and functions used internally by
// Google Test. They are subject to change without notice. They should not used // Google Test. They are subject to change without notice. They should not used
// by code external to Google Test. // by code external to Google Test.
// //
// This header file is #included by <gtest/internal/gtest-internal.h>. // This header file is #included by gtest-internal.h.
// It should not be #included by other files. // It should not be #included by other files.
#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ // GOOGLETEST_CM0001 DO NOT DELETE
#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_
#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_
#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_
#ifdef __BORLANDC__ #ifdef __BORLANDC__
// string.h is not guaranteed to provide strcpy on C++ Builder. // string.h is not guaranteed to provide strcpy on C++ Builder.
@ -47,6 +47,7 @@
#endif #endif
#include <string.h> #include <string.h>
#include <cstdint>
#include <string> #include <string>
#include "gtest/internal/gtest-port.h" #include "gtest/internal/gtest-port.h"
@ -94,7 +95,8 @@ class GTEST_API_ String {
static const char* Utf16ToAnsi(LPCWSTR utf16_str); static const char* Utf16ToAnsi(LPCWSTR utf16_str);
#endif #endif
// Compares two C strings. Returns true iff they have the same content. // Compares two C strings. Returns true if and only if they have the same
// content.
// //
// Unlike strcmp(), this function can handle NULL argument(s). A // Unlike strcmp(), this function can handle NULL argument(s). A
// NULL C string is considered different to any non-NULL C string, // NULL C string is considered different to any non-NULL C string,
@ -107,16 +109,16 @@ class GTEST_API_ String {
// returned. // returned.
static std::string ShowWideCString(const wchar_t* wide_c_str); static std::string ShowWideCString(const wchar_t* wide_c_str);
// Compares two wide C strings. Returns true iff they have the same // Compares two wide C strings. Returns true if and only if they have the
// content. // same content.
// //
// Unlike wcscmp(), this function can handle NULL argument(s). A // Unlike wcscmp(), this function can handle NULL argument(s). A
// NULL C string is considered different to any non-NULL C string, // NULL C string is considered different to any non-NULL C string,
// including the empty string. // including the empty string.
static bool WideCStringEquals(const wchar_t* lhs, const wchar_t* rhs); static bool WideCStringEquals(const wchar_t* lhs, const wchar_t* rhs);
// Compares two C strings, ignoring case. Returns true iff they // Compares two C strings, ignoring case. Returns true if and only if
// have the same content. // they have the same content.
// //
// Unlike strcasecmp(), this function can handle NULL argument(s). // Unlike strcasecmp(), this function can handle NULL argument(s).
// A NULL C string is considered different to any non-NULL C string, // A NULL C string is considered different to any non-NULL C string,
@ -124,8 +126,8 @@ class GTEST_API_ String {
static bool CaseInsensitiveCStringEquals(const char* lhs, static bool CaseInsensitiveCStringEquals(const char* lhs,
const char* rhs); const char* rhs);
// Compares two wide C strings, ignoring case. Returns true iff they // Compares two wide C strings, ignoring case. Returns true if and only if
// have the same content. // they have the same content.
// //
// Unlike wcscasecmp(), this function can handle NULL argument(s). // Unlike wcscasecmp(), this function can handle NULL argument(s).
// A NULL C string is considered different to any non-NULL wide C string, // A NULL C string is considered different to any non-NULL wide C string,
@ -139,17 +141,23 @@ class GTEST_API_ String {
static bool CaseInsensitiveWideCStringEquals(const wchar_t* lhs, static bool CaseInsensitiveWideCStringEquals(const wchar_t* lhs,
const wchar_t* rhs); const wchar_t* rhs);
// Returns true iff the given string ends with the given suffix, ignoring // Returns true if and only if the given string ends with the given suffix,
// case. Any string is considered to end with an empty suffix. // ignoring case. Any string is considered to end with an empty suffix.
static bool EndsWithCaseInsensitive( static bool EndsWithCaseInsensitive(
const std::string& str, const std::string& suffix); const std::string& str, const std::string& suffix);
// Formats an int value as "%02d". // Formats an int value as "%02d".
static std::string FormatIntWidth2(int value); // "%02d" for width == 2 static std::string FormatIntWidth2(int value); // "%02d" for width == 2
// Formats an int value to given width with leading zeros.
static std::string FormatIntWidthN(int value, int width);
// Formats an int value as "%X". // Formats an int value as "%X".
static std::string FormatHexInt(int value); static std::string FormatHexInt(int value);
// Formats an int value as "%X".
static std::string FormatHexUInt32(uint32_t value);
// Formats a byte as "%02X". // Formats a byte as "%02X".
static std::string FormatByte(unsigned char value); static std::string FormatByte(unsigned char value);
@ -164,4 +172,4 @@ GTEST_API_ std::string StringStreamToString(::std::stringstream* stream);
} // namespace internal } // namespace internal
} // namespace testing } // namespace testing
#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ #endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_

File diff suppressed because it is too large Load Diff

View File

@ -26,16 +26,15 @@
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Author: wan@google.com (Zhanyong Wan)
// Author: vladl@google.com (Vlad Losev)
// This provides interface PrimeTable that determines whether a number is a // This provides interface PrimeTable that determines whether a number is a
// prime and determines a next prime number. This interface is used // prime and determines a next prime number. This interface is used
// in Google Test samples demonstrating use of parameterized tests. // in Google Test samples demonstrating use of parameterized tests.
#ifndef GTEST_SAMPLES_PRIME_TABLES_H_ #ifndef GOOGLETEST_SAMPLES_PRIME_TABLES_H_
#define GTEST_SAMPLES_PRIME_TABLES_H_ #define GOOGLETEST_SAMPLES_PRIME_TABLES_H_
#include <algorithm> #include <algorithm>
@ -44,7 +43,7 @@ class PrimeTable {
public: public:
virtual ~PrimeTable() {} virtual ~PrimeTable() {}
// Returns true iff n is a prime number. // Returns true if and only if n is a prime number.
virtual bool IsPrime(int n) const = 0; virtual bool IsPrime(int n) const = 0;
// Returns the smallest prime number greater than p; or returns -1 // Returns the smallest prime number greater than p; or returns -1
@ -55,7 +54,7 @@ class PrimeTable {
// Implementation #1 calculates the primes on-the-fly. // Implementation #1 calculates the primes on-the-fly.
class OnTheFlyPrimeTable : public PrimeTable { class OnTheFlyPrimeTable : public PrimeTable {
public: public:
virtual bool IsPrime(int n) const { bool IsPrime(int n) const override {
if (n <= 1) return false; if (n <= 1) return false;
for (int i = 2; i*i <= n; i++) { for (int i = 2; i*i <= n; i++) {
@ -66,12 +65,12 @@ class OnTheFlyPrimeTable : public PrimeTable {
return true; return true;
} }
virtual int GetNextPrime(int p) const { int GetNextPrime(int p) const override {
for (int n = p + 1; n > 0; n++) { if (p < 0) return -1;
for (int n = p + 1;; n++) {
if (IsPrime(n)) return n; if (IsPrime(n)) return n;
} }
return -1;
} }
}; };
@ -84,13 +83,13 @@ class PreCalculatedPrimeTable : public PrimeTable {
: is_prime_size_(max + 1), is_prime_(new bool[max + 1]) { : is_prime_size_(max + 1), is_prime_(new bool[max + 1]) {
CalculatePrimesUpTo(max); CalculatePrimesUpTo(max);
} }
virtual ~PreCalculatedPrimeTable() { delete[] is_prime_; } ~PreCalculatedPrimeTable() override { delete[] is_prime_; }
virtual bool IsPrime(int n) const { bool IsPrime(int n) const override {
return 0 <= n && n < is_prime_size_ && is_prime_[n]; return 0 <= n && n < is_prime_size_ && is_prime_[n];
} }
virtual int GetNextPrime(int p) const { int GetNextPrime(int p) const override {
for (int n = p + 1; n < is_prime_size_; n++) { for (int n = p + 1; n < is_prime_size_; n++) {
if (is_prime_[n]) return n; if (is_prime_[n]) return n;
} }
@ -103,11 +102,15 @@ class PreCalculatedPrimeTable : public PrimeTable {
::std::fill(is_prime_, is_prime_ + is_prime_size_, true); ::std::fill(is_prime_, is_prime_ + is_prime_size_, true);
is_prime_[0] = is_prime_[1] = false; is_prime_[0] = is_prime_[1] = false;
for (int i = 2; i <= max; i++) { // Checks every candidate for prime number (we know that 2 is the only even
// prime).
for (int i = 2; i*i <= max; i += i%2+1) {
if (!is_prime_[i]) continue; if (!is_prime_[i]) continue;
// Marks all multiples of i (except i itself) as non-prime. // Marks all multiples of i (except i itself) as non-prime.
for (int j = 2*i; j <= max; j += i) { // We are starting here from i-th multiplier, because all smaller
// complex numbers were already marked.
for (int j = i*i; j <= max; j += i) {
is_prime_[j] = false; is_prime_[j] = false;
} }
} }
@ -120,4 +123,4 @@ class PreCalculatedPrimeTable : public PrimeTable {
void operator=(const PreCalculatedPrimeTable& rhs); void operator=(const PreCalculatedPrimeTable& rhs);
}; };
#endif // GTEST_SAMPLES_PRIME_TABLES_H_ #endif // GOOGLETEST_SAMPLES_PRIME_TABLES_H_

View File

@ -28,8 +28,6 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// A sample program demonstrating using Google C++ testing framework. // A sample program demonstrating using Google C++ testing framework.
//
// Author: wan@google.com (Zhanyong Wan)
#include "sample1.h" #include "sample1.h"
@ -43,7 +41,7 @@ int Factorial(int n) {
return result; return result;
} }
// Returns true iff n is a prime number. // Returns true if and only if n is a prime number.
bool IsPrime(int n) { bool IsPrime(int n) {
// Trivial case 1: small numbers // Trivial case 1: small numbers
if (n <= 1) return false; if (n <= 1) return false;
@ -55,7 +53,7 @@ bool IsPrime(int n) {
// Try to divide n by every odd number i, starting from 3 // Try to divide n by every odd number i, starting from 3
for (int i = 3; ; i += 2) { for (int i = 3; ; i += 2) {
// We only have to try i up to the squre root of n // We only have to try i up to the square root of n
if (i > n/i) break; if (i > n/i) break;
// Now, we have i <= n/i < n. // Now, we have i <= n/i < n.

View File

@ -28,16 +28,14 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// A sample program demonstrating using Google C++ testing framework. // A sample program demonstrating using Google C++ testing framework.
//
// Author: wan@google.com (Zhanyong Wan)
#ifndef GTEST_SAMPLES_SAMPLE1_H_ #ifndef GOOGLETEST_SAMPLES_SAMPLE1_H_
#define GTEST_SAMPLES_SAMPLE1_H_ #define GOOGLETEST_SAMPLES_SAMPLE1_H_
// Returns n! (the factorial of n). For negative n, n! is defined to be 1. // Returns n! (the factorial of n). For negative n, n! is defined to be 1.
int Factorial(int n); int Factorial(int n);
// Returns true iff n is a prime number. // Returns true if and only if n is a prime number.
bool IsPrime(int n); bool IsPrime(int n);
#endif // GTEST_SAMPLES_SAMPLE1_H_ #endif // GOOGLETEST_SAMPLES_SAMPLE1_H_

View File

@ -25,8 +25,7 @@
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Author: vladl@google.com (Vlad Losev)
// This sample shows how to use Google Test listener API to implement // This sample shows how to use Google Test listener API to implement
// a primitive leak checker. // a primitive leak checker.
@ -35,18 +34,15 @@
#include <stdlib.h> #include <stdlib.h>
#include "gtest/gtest.h" #include "gtest/gtest.h"
using ::testing::EmptyTestEventListener; using ::testing::EmptyTestEventListener;
using ::testing::InitGoogleTest; using ::testing::InitGoogleTest;
using ::testing::Test; using ::testing::Test;
using ::testing::TestCase;
using ::testing::TestEventListeners; using ::testing::TestEventListeners;
using ::testing::TestInfo; using ::testing::TestInfo;
using ::testing::TestPartResult; using ::testing::TestPartResult;
using ::testing::UnitTest; using ::testing::UnitTest;
namespace { namespace {
// We will track memory used by this class. // We will track memory used by this class.
class Water { class Water {
public: public:
@ -78,12 +74,12 @@ int Water::allocated_ = 0;
class LeakChecker : public EmptyTestEventListener { class LeakChecker : public EmptyTestEventListener {
private: private:
// Called before a test starts. // Called before a test starts.
virtual void OnTestStart(const TestInfo& /* test_info */) { void OnTestStart(const TestInfo& /* test_info */) override {
initially_allocated_ = Water::allocated(); initially_allocated_ = Water::allocated();
} }
// Called after a test ends. // Called after a test ends.
virtual void OnTestEnd(const TestInfo& /* test_info */) { void OnTestEnd(const TestInfo& /* test_info */) override {
int difference = Water::allocated() - initially_allocated_; int difference = Water::allocated() - initially_allocated_;
// You can generate a failure in any event handler except // You can generate a failure in any event handler except
@ -104,9 +100,8 @@ TEST(ListenersTest, DoesNotLeak) {
// specified. // specified.
TEST(ListenersTest, LeaksWater) { TEST(ListenersTest, LeaksWater) {
Water* water = new Water; Water* water = new Water;
EXPECT_TRUE(water != NULL); EXPECT_TRUE(water != nullptr);
} }
} // namespace } // namespace
int main(int argc, char **argv) { int main(int argc, char **argv) {

View File

@ -28,9 +28,6 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// A sample program demonstrating using Google C++ testing framework. // A sample program demonstrating using Google C++ testing framework.
//
// Author: wan@google.com (Zhanyong Wan)
// This sample shows how to write a simple unit test for a function, // This sample shows how to write a simple unit test for a function,
// using Google C++ testing framework. // using Google C++ testing framework.
@ -46,7 +43,7 @@
#include <limits.h> #include <limits.h>
#include "sample1.h" #include "sample1.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
namespace {
// Step 2. Use the TEST macro to define your tests. // Step 2. Use the TEST macro to define your tests.
// //
@ -139,6 +136,7 @@ TEST(IsPrimeTest, Positive) {
EXPECT_FALSE(IsPrime(6)); EXPECT_FALSE(IsPrime(6));
EXPECT_TRUE(IsPrime(23)); EXPECT_TRUE(IsPrime(23));
} }
} // namespace
// Step 3. Call RUN_ALL_TESTS() in main(). // Step 3. Call RUN_ALL_TESTS() in main().
// //

View File

@ -28,8 +28,6 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// A sample program demonstrating using Google C++ testing framework. // A sample program demonstrating using Google C++ testing framework.
//
// Author: wan@google.com (Zhanyong Wan)
#include "sample2.h" #include "sample2.h"
@ -37,7 +35,7 @@
// Clones a 0-terminated C string, allocating memory using new. // Clones a 0-terminated C string, allocating memory using new.
const char* MyString::CloneCString(const char* a_c_string) { const char* MyString::CloneCString(const char* a_c_string) {
if (a_c_string == NULL) return NULL; if (a_c_string == nullptr) return nullptr;
const size_t len = strlen(a_c_string); const size_t len = strlen(a_c_string);
char* const clone = new char[ len + 1 ]; char* const clone = new char[ len + 1 ];

View File

@ -28,11 +28,9 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// A sample program demonstrating using Google C++ testing framework. // A sample program demonstrating using Google C++ testing framework.
//
// Author: wan@google.com (Zhanyong Wan)
#ifndef GTEST_SAMPLES_SAMPLE2_H_ #ifndef GOOGLETEST_SAMPLES_SAMPLE2_H_
#define GTEST_SAMPLES_SAMPLE2_H_ #define GOOGLETEST_SAMPLES_SAMPLE2_H_
#include <string.h> #include <string.h>
@ -52,15 +50,15 @@ class MyString {
// C'tors // C'tors
// The default c'tor constructs a NULL string. // The default c'tor constructs a NULL string.
MyString() : c_string_(NULL) {} MyString() : c_string_(nullptr) {}
// Constructs a MyString by cloning a 0-terminated C string. // Constructs a MyString by cloning a 0-terminated C string.
explicit MyString(const char* a_c_string) : c_string_(NULL) { explicit MyString(const char* a_c_string) : c_string_(nullptr) {
Set(a_c_string); Set(a_c_string);
} }
// Copy c'tor // Copy c'tor
MyString(const MyString& string) : c_string_(NULL) { MyString(const MyString& string) : c_string_(nullptr) {
Set(string.c_string_); Set(string.c_string_);
} }
@ -73,13 +71,10 @@ class MyString {
// Gets the 0-terminated C string this MyString object represents. // Gets the 0-terminated C string this MyString object represents.
const char* c_string() const { return c_string_; } const char* c_string() const { return c_string_; }
size_t Length() const { size_t Length() const { return c_string_ == nullptr ? 0 : strlen(c_string_); }
return c_string_ == NULL ? 0 : strlen(c_string_);
}
// Sets the 0-terminated C string this MyString object represents. // Sets the 0-terminated C string this MyString object represents.
void Set(const char* c_string); void Set(const char* c_string);
}; };
#endif // GOOGLETEST_SAMPLES_SAMPLE2_H_
#endif // GTEST_SAMPLES_SAMPLE2_H_

View File

@ -28,9 +28,6 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// A sample program demonstrating using Google C++ testing framework. // A sample program demonstrating using Google C++ testing framework.
//
// Author: wan@google.com (Zhanyong Wan)
// This sample shows how to write a more complex unit test for a class // This sample shows how to write a more complex unit test for a class
// that has multiple member functions. // that has multiple member functions.
@ -42,7 +39,7 @@
#include "sample2.h" #include "sample2.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
namespace {
// In this example, we test the MyString class (a simple string). // In this example, we test the MyString class (a simple string).
// Tests the default c'tor. // Tests the default c'tor.
@ -69,7 +66,7 @@ TEST(MyString, DefaultConstructor) {
// we have to live with this fact. // we have to live with this fact.
// //
// </TechnicalDetails> // </TechnicalDetails>
EXPECT_STREQ(NULL, s.c_string()); EXPECT_STREQ(nullptr, s.c_string());
EXPECT_EQ(0u, s.Length()); EXPECT_EQ(0u, s.Length());
} }
@ -104,6 +101,7 @@ TEST(MyString, Set) {
EXPECT_EQ(0, strcmp(s.c_string(), kHelloString)); EXPECT_EQ(0, strcmp(s.c_string(), kHelloString));
// Can we set the MyString to NULL? // Can we set the MyString to NULL?
s.Set(NULL); s.Set(nullptr);
EXPECT_STREQ(NULL, s.c_string()); EXPECT_STREQ(nullptr, s.c_string());
} }
} // namespace

View File

@ -28,11 +28,9 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// A sample program demonstrating using Google C++ testing framework. // A sample program demonstrating using Google C++ testing framework.
//
// Author: wan@google.com (Zhanyong Wan)
#ifndef GTEST_SAMPLES_SAMPLE3_INL_H_ #ifndef GOOGLETEST_SAMPLES_SAMPLE3_INL_H_
#define GTEST_SAMPLES_SAMPLE3_INL_H_ #define GOOGLETEST_SAMPLES_SAMPLE3_INL_H_
#include <stddef.h> #include <stddef.h>
@ -60,7 +58,8 @@ class QueueNode {
private: private:
// Creates a node with a given element value. The next pointer is // Creates a node with a given element value. The next pointer is
// set to NULL. // set to NULL.
explicit QueueNode(const E& an_element) : element_(an_element), next_(NULL) {} explicit QueueNode(const E& an_element)
: element_(an_element), next_(nullptr) {}
// We disable the default assignment operator and copy c'tor. // We disable the default assignment operator and copy c'tor.
const QueueNode& operator = (const QueueNode&); const QueueNode& operator = (const QueueNode&);
@ -74,7 +73,7 @@ template <typename E> // E is the element type.
class Queue { class Queue {
public: public:
// Creates an empty queue. // Creates an empty queue.
Queue() : head_(NULL), last_(NULL), size_(0) {} Queue() : head_(nullptr), last_(nullptr), size_(0) {}
// D'tor. Clears the queue. // D'tor. Clears the queue.
~Queue() { Clear(); } ~Queue() { Clear(); }
@ -88,12 +87,12 @@ class Queue {
for (; ;) { for (; ;) {
delete node; delete node;
node = next; node = next;
if (node == NULL) break; if (node == nullptr) break;
next = node->next(); next = node->next();
} }
// 2. Resets the member variables. // 2. Resets the member variables.
head_ = last_ = NULL; head_ = last_ = nullptr;
size_ = 0; size_ = 0;
} }
} }
@ -130,14 +129,14 @@ class Queue {
// the queue is empty. // the queue is empty.
E* Dequeue() { E* Dequeue() {
if (size_ == 0) { if (size_ == 0) {
return NULL; return nullptr;
} }
const QueueNode<E>* const old_head = head_; const QueueNode<E>* const old_head = head_;
head_ = head_->next_; head_ = head_->next_;
size_--; size_--;
if (size_ == 0) { if (size_ == 0) {
last_ = NULL; last_ = nullptr;
} }
E* element = new E(old_head->element()); E* element = new E(old_head->element());
@ -152,7 +151,8 @@ class Queue {
template <typename F> template <typename F>
Queue* Map(F function) const { Queue* Map(F function) const {
Queue* new_queue = new Queue(); Queue* new_queue = new Queue();
for (const QueueNode<E>* node = head_; node != NULL; node = node->next_) { for (const QueueNode<E>* node = head_; node != nullptr;
node = node->next_) {
new_queue->Enqueue(function(node->element())); new_queue->Enqueue(function(node->element()));
} }
@ -169,4 +169,4 @@ class Queue {
const Queue& operator = (const Queue&); const Queue& operator = (const Queue&);
}; };
#endif // GTEST_SAMPLES_SAMPLE3_INL_H_ #endif // GOOGLETEST_SAMPLES_SAMPLE3_INL_H_

View File

@ -28,9 +28,6 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// A sample program demonstrating using Google C++ testing framework. // A sample program demonstrating using Google C++ testing framework.
//
// Author: wan@google.com (Zhanyong Wan)
// In this example, we use a more advanced feature of Google Test called // In this example, we use a more advanced feature of Google Test called
// test fixture. // test fixture.
@ -65,16 +62,16 @@
#include "sample3-inl.h" #include "sample3-inl.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
namespace {
// To use a test fixture, derive a class from testing::Test. // To use a test fixture, derive a class from testing::Test.
class QueueTest : public testing::Test { class QueueTestSmpl3 : public testing::Test {
protected: // You should make the members protected s.t. they can be protected: // You should make the members protected s.t. they can be
// accessed from sub-classes. // accessed from sub-classes.
// virtual void SetUp() will be called before each test is run. You // virtual void SetUp() will be called before each test is run. You
// should define it if you need to initialize the varaibles. // should define it if you need to initialize the variables.
// Otherwise, this can be skipped. // Otherwise, this can be skipped.
virtual void SetUp() { void SetUp() override {
q1_.Enqueue(1); q1_.Enqueue(1);
q2_.Enqueue(2); q2_.Enqueue(2);
q2_.Enqueue(3); q2_.Enqueue(3);
@ -102,8 +99,8 @@ class QueueTest : public testing::Test {
ASSERT_EQ(q->Size(), new_q->Size()); ASSERT_EQ(q->Size(), new_q->Size());
// Verifies the relationship between the elements of the two queues. // Verifies the relationship between the elements of the two queues.
for ( const QueueNode<int> * n1 = q->Head(), * n2 = new_q->Head(); for (const QueueNode<int>*n1 = q->Head(), *n2 = new_q->Head();
n1 != NULL; n1 = n1->next(), n2 = n2->next() ) { n1 != nullptr; n1 = n1->next(), n2 = n2->next()) {
EXPECT_EQ(2 * n1->element(), n2->element()); EXPECT_EQ(2 * n1->element(), n2->element());
} }
@ -120,32 +117,33 @@ class QueueTest : public testing::Test {
// instead of TEST. // instead of TEST.
// Tests the default c'tor. // Tests the default c'tor.
TEST_F(QueueTest, DefaultConstructor) { TEST_F(QueueTestSmpl3, DefaultConstructor) {
// You can access data in the test fixture here. // You can access data in the test fixture here.
EXPECT_EQ(0u, q0_.Size()); EXPECT_EQ(0u, q0_.Size());
} }
// Tests Dequeue(). // Tests Dequeue().
TEST_F(QueueTest, Dequeue) { TEST_F(QueueTestSmpl3, Dequeue) {
int * n = q0_.Dequeue(); int * n = q0_.Dequeue();
EXPECT_TRUE(n == NULL); EXPECT_TRUE(n == nullptr);
n = q1_.Dequeue(); n = q1_.Dequeue();
ASSERT_TRUE(n != NULL); ASSERT_TRUE(n != nullptr);
EXPECT_EQ(1, *n); EXPECT_EQ(1, *n);
EXPECT_EQ(0u, q1_.Size()); EXPECT_EQ(0u, q1_.Size());
delete n; delete n;
n = q2_.Dequeue(); n = q2_.Dequeue();
ASSERT_TRUE(n != NULL); ASSERT_TRUE(n != nullptr);
EXPECT_EQ(2, *n); EXPECT_EQ(2, *n);
EXPECT_EQ(1u, q2_.Size()); EXPECT_EQ(1u, q2_.Size());
delete n; delete n;
} }
// Tests the Queue::Map() function. // Tests the Queue::Map() function.
TEST_F(QueueTest, Map) { TEST_F(QueueTestSmpl3, Map) {
MapTester(&q0_); MapTester(&q0_);
MapTester(&q1_); MapTester(&q1_);
MapTester(&q2_); MapTester(&q2_);
} }
} // namespace

View File

@ -28,8 +28,6 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// A sample program demonstrating using Google C++ testing framework. // A sample program demonstrating using Google C++ testing framework.
//
// Author: wan@google.com (Zhanyong Wan)
#include <stdio.h> #include <stdio.h>
@ -40,6 +38,16 @@ int Counter::Increment() {
return counter_++; return counter_++;
} }
// Returns the current counter value, and decrements it.
// counter can not be less than 0, return 0 in this case
int Counter::Decrement() {
if (counter_ == 0) {
return counter_;
} else {
return counter_--;
}
}
// Prints the current counter value to STDOUT. // Prints the current counter value to STDOUT.
void Counter::Print() const { void Counter::Print() const {
printf("%d", counter_); printf("%d", counter_);

View File

@ -28,11 +28,8 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// A sample program demonstrating using Google C++ testing framework. // A sample program demonstrating using Google C++ testing framework.
// #ifndef GOOGLETEST_SAMPLES_SAMPLE4_H_
// Author: wan@google.com (Zhanyong Wan) #define GOOGLETEST_SAMPLES_SAMPLE4_H_
#ifndef GTEST_SAMPLES_SAMPLE4_H_
#define GTEST_SAMPLES_SAMPLE4_H_
// A simple monotonic counter. // A simple monotonic counter.
class Counter { class Counter {
@ -46,8 +43,11 @@ class Counter {
// Returns the current counter value, and increments it. // Returns the current counter value, and increments it.
int Increment(); int Increment();
// Returns the current counter value, and decrements it.
int Decrement();
// Prints the current counter value to STDOUT. // Prints the current counter value to STDOUT.
void Print() const; void Print() const;
}; };
#endif // GTEST_SAMPLES_SAMPLE4_H_ #endif // GOOGLETEST_SAMPLES_SAMPLE4_H_

View File

@ -26,20 +26,28 @@
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Author: wan@google.com (Zhanyong Wan)
#include "gtest/gtest.h"
#include "sample4.h" #include "sample4.h"
#include "gtest/gtest.h"
namespace {
// Tests the Increment() method. // Tests the Increment() method.
TEST(Counter, Increment) { TEST(Counter, Increment) {
Counter c; Counter c;
// Test that counter 0 returns 0
EXPECT_EQ(0, c.Decrement());
// EXPECT_EQ() evaluates its arguments exactly once, so they // EXPECT_EQ() evaluates its arguments exactly once, so they
// can have side effects. // can have side effects.
EXPECT_EQ(0, c.Increment()); EXPECT_EQ(0, c.Increment());
EXPECT_EQ(1, c.Increment()); EXPECT_EQ(1, c.Increment());
EXPECT_EQ(2, c.Increment()); EXPECT_EQ(2, c.Increment());
EXPECT_EQ(3, c.Decrement());
} }
} // namespace

View File

@ -26,8 +26,7 @@
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Author: wan@google.com (Zhanyong Wan)
// This sample teaches how to reuse a test fixture in multiple test // This sample teaches how to reuse a test fixture in multiple test
// cases by deriving sub-fixtures from it. // cases by deriving sub-fixtures from it.
@ -46,10 +45,10 @@
#include <limits.h> #include <limits.h>
#include <time.h> #include <time.h>
#include "sample3-inl.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "sample1.h" #include "sample1.h"
#include "sample3-inl.h"
namespace {
// In this sample, we want to ensure that every test finishes within // In this sample, we want to ensure that every test finishes within
// ~5 seconds. If a test takes longer to run, we consider it a // ~5 seconds. If a test takes longer to run, we consider it a
// failure. // failure.
@ -64,15 +63,13 @@ class QuickTest : public testing::Test {
protected: protected:
// Remember that SetUp() is run immediately before a test starts. // Remember that SetUp() is run immediately before a test starts.
// This is a good place to record the start time. // This is a good place to record the start time.
virtual void SetUp() { void SetUp() override { start_time_ = time(nullptr); }
start_time_ = time(NULL);
}
// TearDown() is invoked immediately after a test finishes. Here we // TearDown() is invoked immediately after a test finishes. Here we
// check if the test was too slow. // check if the test was too slow.
virtual void TearDown() { void TearDown() override {
// Gets the time when the test finishes // Gets the time when the test finishes
const time_t end_time = time(NULL); const time_t end_time = time(nullptr);
// Asserts that the test took no more than ~5 seconds. Did you // Asserts that the test took no more than ~5 seconds. Did you
// know that you can use assertions in SetUp() and TearDown() as // know that you can use assertions in SetUp() and TearDown() as
@ -143,7 +140,7 @@ TEST_F(IntegerFunctionTest, IsPrime) {
// stuff inside the body of the test fixture, as usual. // stuff inside the body of the test fixture, as usual.
class QueueTest : public QuickTest { class QueueTest : public QuickTest {
protected: protected:
virtual void SetUp() { void SetUp() override {
// First, we need to set up the super fixture (QuickTest). // First, we need to set up the super fixture (QuickTest).
QuickTest::SetUp(); QuickTest::SetUp();
@ -177,21 +174,21 @@ TEST_F(QueueTest, DefaultConstructor) {
// Tests Dequeue(). // Tests Dequeue().
TEST_F(QueueTest, Dequeue) { TEST_F(QueueTest, Dequeue) {
int* n = q0_.Dequeue(); int* n = q0_.Dequeue();
EXPECT_TRUE(n == NULL); EXPECT_TRUE(n == nullptr);
n = q1_.Dequeue(); n = q1_.Dequeue();
EXPECT_TRUE(n != NULL); EXPECT_TRUE(n != nullptr);
EXPECT_EQ(1, *n); EXPECT_EQ(1, *n);
EXPECT_EQ(0u, q1_.Size()); EXPECT_EQ(0u, q1_.Size());
delete n; delete n;
n = q2_.Dequeue(); n = q2_.Dequeue();
EXPECT_TRUE(n != NULL); EXPECT_TRUE(n != nullptr);
EXPECT_EQ(2, *n); EXPECT_EQ(2, *n);
EXPECT_EQ(1u, q2_.Size()); EXPECT_EQ(1u, q2_.Size());
delete n; delete n;
} }
} // namespace
// If necessary, you can derive further test fixtures from a derived // If necessary, you can derive further test fixtures from a derived
// fixture itself. For example, you can derive another fixture from // fixture itself. For example, you can derive another fixture from
// QueueTest. Google Test imposes no limit on how deep the hierarchy // QueueTest. Google Test imposes no limit on how deep the hierarchy

View File

@ -26,8 +26,7 @@
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Author: wan@google.com (Zhanyong Wan)
// This sample shows how to test common properties of multiple // This sample shows how to test common properties of multiple
// implementations of the same interface (aka interface tests). // implementations of the same interface (aka interface tests).
@ -36,7 +35,7 @@
#include "prime_tables.h" #include "prime_tables.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
namespace {
// First, we define some factory functions for creating instances of // First, we define some factory functions for creating instances of
// the implementations. You may be able to skip this step if all your // the implementations. You may be able to skip this step if all your
// implementations can be constructed the same way. // implementations can be constructed the same way.
@ -62,7 +61,7 @@ class PrimeTableTest : public testing::Test {
// implemented by T. // implemented by T.
PrimeTableTest() : table_(CreatePrimeTable<T>()) {} PrimeTableTest() : table_(CreatePrimeTable<T>()) {}
virtual ~PrimeTableTest() { delete table_; } ~PrimeTableTest() override { delete table_; }
// Note that we test an implementation via the base interface // Note that we test an implementation via the base interface
// instead of the actual implementation class. This is important // instead of the actual implementation class. This is important
@ -74,8 +73,6 @@ class PrimeTableTest : public testing::Test {
PrimeTable* const table_; PrimeTable* const table_;
}; };
#if GTEST_HAS_TYPED_TEST
using testing::Types; using testing::Types;
// Google Test offers two ways for reusing tests for different types. // Google Test offers two ways for reusing tests for different types.
@ -85,7 +82,7 @@ using testing::Types;
// To write a typed test case, first use // To write a typed test case, first use
// //
// TYPED_TEST_CASE(TestCaseName, TypeList); // TYPED_TEST_SUITE(TestCaseName, TypeList);
// //
// to declare it and specify the type parameters. As with TEST_F, // to declare it and specify the type parameters. As with TEST_F,
// TestCaseName must match the test fixture name. // TestCaseName must match the test fixture name.
@ -93,7 +90,7 @@ using testing::Types;
// The list of types we want to test. // The list of types we want to test.
typedef Types<OnTheFlyPrimeTable, PreCalculatedPrimeTable> Implementations; typedef Types<OnTheFlyPrimeTable, PreCalculatedPrimeTable> Implementations;
TYPED_TEST_CASE(PrimeTableTest, Implementations); TYPED_TEST_SUITE(PrimeTableTest, Implementations);
// Then use TYPED_TEST(TestCaseName, TestName) to define a typed test, // Then use TYPED_TEST(TestCaseName, TestName) to define a typed test,
// similar to TEST_F. // similar to TEST_F.
@ -132,13 +129,9 @@ TYPED_TEST(PrimeTableTest, CanGetNextPrime) {
} }
// That's it! Google Test will repeat each TYPED_TEST for each type // That's it! Google Test will repeat each TYPED_TEST for each type
// in the type list specified in TYPED_TEST_CASE. Sit back and be // in the type list specified in TYPED_TEST_SUITE. Sit back and be
// happy that you don't have to define them multiple times. // happy that you don't have to define them multiple times.
#endif // GTEST_HAS_TYPED_TEST
#if GTEST_HAS_TYPED_TEST_P
using testing::Types; using testing::Types;
// Sometimes, however, you don't yet know all the types that you want // Sometimes, however, you don't yet know all the types that you want
@ -164,7 +157,7 @@ class PrimeTableTest2 : public PrimeTableTest<T> {
// Then, declare the test case. The argument is the name of the test // Then, declare the test case. The argument is the name of the test
// fixture, and also the name of the test case (as usual). The _P // fixture, and also the name of the test case (as usual). The _P
// suffix is for "parameterized" or "pattern". // suffix is for "parameterized" or "pattern".
TYPED_TEST_CASE_P(PrimeTableTest2); TYPED_TEST_SUITE_P(PrimeTableTest2);
// Next, use TYPED_TEST_P(TestCaseName, TestName) to define a test, // Next, use TYPED_TEST_P(TestCaseName, TestName) to define a test,
// similar to what you do with TEST_F. // similar to what you do with TEST_F.
@ -197,7 +190,7 @@ TYPED_TEST_P(PrimeTableTest2, CanGetNextPrime) {
// Type-parameterized tests involve one extra step: you have to // Type-parameterized tests involve one extra step: you have to
// enumerate the tests you defined: // enumerate the tests you defined:
REGISTER_TYPED_TEST_CASE_P( REGISTER_TYPED_TEST_SUITE_P(
PrimeTableTest2, // The first argument is the test case name. PrimeTableTest2, // The first argument is the test case name.
// The rest of the arguments are the test names. // The rest of the arguments are the test names.
ReturnsFalseForNonPrimes, ReturnsTrueForPrimes, CanGetNextPrime); ReturnsFalseForNonPrimes, ReturnsTrueForPrimes, CanGetNextPrime);
@ -217,8 +210,8 @@ REGISTER_TYPED_TEST_CASE_P(
// defined at the time we write the TYPED_TEST_P()s. // defined at the time we write the TYPED_TEST_P()s.
typedef Types<OnTheFlyPrimeTable, PreCalculatedPrimeTable> typedef Types<OnTheFlyPrimeTable, PreCalculatedPrimeTable>
PrimeTableImplementations; PrimeTableImplementations;
INSTANTIATE_TYPED_TEST_CASE_P(OnTheFlyAndPreCalculated, // Instance name INSTANTIATE_TYPED_TEST_SUITE_P(OnTheFlyAndPreCalculated, // Instance name
PrimeTableTest2, // Test case name PrimeTableTest2, // Test case name
PrimeTableImplementations); // Type list PrimeTableImplementations); // Type list
#endif // GTEST_HAS_TYPED_TEST_P } // namespace

Some files were not shown because too many files have changed in this diff Show More