Quellcode durchsuchen

Merge branch 'master' into kimkulling/create_skeleton_data_issue_4015

Kim Kulling vor 3 Jahren
Ursprung
Commit
39a4627519
100 geänderte Dateien mit 5523 neuen und 5662 gelöschten Zeilen
  1. 25 20
      Build.md
  2. 33 29
      CMakeLists.txt
  3. 0 1
      README
  4. 3 0
      Readme.md
  5. 1 1
      cmake-modules/FindDirectX.cmake
  6. 1 1
      code/AssetLib/3MF/XmlSerializer.cpp
  7. 6 3
      code/AssetLib/ASE/ASEParser.cpp
  8. 1 1
      code/AssetLib/Assxml/AssxmlFileWriter.cpp
  9. 1 1
      code/AssetLib/Blender/BlenderLoader.cpp
  10. 1 1
      code/AssetLib/Blender/BlenderLoader.h
  11. 18 25
      code/AssetLib/FBX/FBXConverter.cpp
  12. 8 13
      code/AssetLib/FBX/FBXConverter.h
  13. 23 54
      code/AssetLib/FBX/FBXDocument.cpp
  14. 20 28
      code/AssetLib/FBX/FBXDocument.h
  15. 22 1
      code/AssetLib/FBX/FBXMaterial.cpp
  16. 0 5
      code/AssetLib/FBX/FBXModel.cpp
  17. 4 2
      code/AssetLib/IFC/IFCCurve.cpp
  18. 5 5
      code/AssetLib/IFC/IFCOpenings.cpp
  19. 77 0
      code/AssetLib/LWO/LWOFileData.h
  20. 209 13
      code/AssetLib/LWO/LWOLoader.cpp
  21. 32 0
      code/AssetLib/LWO/LWOLoader.h
  22. 313 2
      code/AssetLib/LWO/LWOMaterial.cpp
  23. 1 3
      code/AssetLib/M3D/M3DWrapper.h
  24. 3 0
      code/AssetLib/MDL/MDLLoader.cpp
  25. 1 1
      code/AssetLib/MDL/MDLMaterialLoader.cpp
  26. 21 14
      code/AssetLib/MMD/MMDImporter.cpp
  27. 11 10
      code/AssetLib/Obj/ObjFileData.h
  28. 10 5
      code/AssetLib/Obj/ObjFileImporter.cpp
  29. 19 2
      code/AssetLib/Obj/ObjFileMtlImporter.cpp
  30. 3 0
      code/AssetLib/Obj/ObjFileMtlImporter.h
  31. 22 1
      code/AssetLib/Obj/ObjFileParser.cpp
  32. 1 1
      code/AssetLib/Obj/ObjFileParser.h
  33. 21 11
      code/AssetLib/Ogre/OgreXmlSerializer.cpp
  34. 1 1
      code/AssetLib/X3D/X3DImporter.cpp
  35. 1 1
      code/AssetLib/glTF/glTFAsset.inl
  36. 15 3
      code/AssetLib/glTF2/glTF2Asset.inl
  37. 202 198
      code/AssetLib/glTF2/glTF2Importer.cpp
  38. 31 15
      code/CMakeLists.txt
  39. 6 13
      code/Common/Assimp.cpp
  40. 2 2
      code/Common/DefaultIOStream.cpp
  41. 2 2
      code/Common/FileSystemFilter.h
  42. 29 0
      code/Common/IFF.h
  43. 29 0
      code/Common/Maybe.h
  44. 58 0
      code/Common/StbCommon.h
  45. 3 3
      code/Common/ZipArchiveIOSystem.cpp
  46. 1 1
      code/Common/material.cpp
  47. 2 2
      code/Pbrt/PbrtExporter.cpp
  48. 2 0
      code/PostProcessing/ArmaturePopulate.cpp
  49. 1 1
      code/PostProcessing/ComputeUVMappingProcess.cpp
  50. 75 93
      code/PostProcessing/JoinVerticesProcess.cpp
  51. 1 1
      code/PostProcessing/ValidateDataStructure.cpp
  52. 12 55
      code/res/assimp.rc
  53. 0 14
      code/res/resource.h
  54. 1 1
      contrib/draco/CMakeLists.txt
  55. 120 83
      contrib/gtest/CMakeLists.txt
  56. 164 229
      contrib/gtest/README.md
  57. 9 0
      contrib/gtest/cmake/Config.cmake.in
  58. 9 0
      contrib/gtest/cmake/gtest.pc.in
  59. 10 0
      contrib/gtest/cmake/gtest_main.pc.in
  60. 149 59
      contrib/gtest/cmake/internal_utils.cmake
  61. 21 0
      contrib/gtest/cmake/libgtest.la.in
  62. 4 0
      contrib/gtest/docs/README.md
  63. 84 32
      contrib/gtest/include/gtest/gtest-death-test.h
  64. 930 0
      contrib/gtest/include/gtest/gtest-matchers.h
  65. 16 47
      contrib/gtest/include/gtest/gtest-message.h
  66. 43 907
      contrib/gtest/include/gtest/gtest-param-test.h
  67. 462 426
      contrib/gtest/include/gtest/gtest-printers.h
  68. 16 10
      contrib/gtest/include/gtest/gtest-spi.h
  69. 27 22
      contrib/gtest/include/gtest/gtest-test-part.h
  70. 174 108
      contrib/gtest/include/gtest/gtest-typed-test.h
  71. 317 190
      contrib/gtest/include/gtest/gtest.h
  72. 44 43
      contrib/gtest/include/gtest/gtest_pred_impl.h
  73. 13 10
      contrib/gtest/include/gtest/gtest_prod.h
  74. 56 0
      contrib/gtest/include/gtest/internal/custom/README.md
  75. 4 36
      contrib/gtest/include/gtest/internal/custom/gtest-port.h
  76. 5 5
      contrib/gtest/include/gtest/internal/custom/gtest-printers.h
  77. 4 8
      contrib/gtest/include/gtest/internal/custom/gtest.h
  78. 83 98
      contrib/gtest/include/gtest/internal/gtest-death-test-internal.h
  79. 13 8
      contrib/gtest/include/gtest/internal/gtest-filepath.h
  80. 515 279
      contrib/gtest/include/gtest/internal/gtest-internal.h
  81. 398 182
      contrib/gtest/include/gtest/internal/gtest-param-util.h
  82. 29 8
      contrib/gtest/include/gtest/internal/gtest-port-arch.h
  83. 226 501
      contrib/gtest/include/gtest/internal/gtest-port.h
  84. 24 16
      contrib/gtest/include/gtest/internal/gtest-string.h
  85. 44 1562
      contrib/gtest/include/gtest/internal/gtest-type-util.h
  86. 20 17
      contrib/gtest/samples/prime_tables.h
  87. 2 4
      contrib/gtest/samples/sample1.cc
  88. 4 6
      contrib/gtest/samples/sample1.h
  89. 4 9
      contrib/gtest/samples/sample10_unittest.cc
  90. 2 4
      contrib/gtest/samples/sample1_unittest.cc
  91. 1 3
      contrib/gtest/samples/sample2.cc
  92. 7 12
      contrib/gtest/samples/sample2.h
  93. 5 7
      contrib/gtest/samples/sample2_unittest.cc
  94. 12 12
      contrib/gtest/samples/sample3-inl.h
  95. 13 15
      contrib/gtest/samples/sample3_unittest.cc
  96. 10 2
      contrib/gtest/samples/sample4.cc
  97. 6 6
      contrib/gtest/samples/sample4.h
  98. 11 3
      contrib/gtest/samples/sample4_unittest.cc
  99. 11 14
      contrib/gtest/samples/sample5_unittest.cc
  100. 12 19
      contrib/gtest/samples/sample6_unittest.cc

+ 25 - 20
Build.md

@@ -84,23 +84,28 @@ Besides the toolchain, compilation should be the same as for Linux / Unix.
 
 ### CMake build options
 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.
-- **BUILD_FRAMEWORK ( default OFF, MacOnly)**: Build package as Mac OS X Framework bundle
-- **ASSIMP_DOUBLE_PRECISION( default OFF )**: All data will be stored as double values.
-- **ASSIMP_OPT_BUILD_PACKAGES ( default OFF)**: Set to ON to generate CPack configuration files and packaging targets
-- **ASSIMP_ANDROID_JNIIOSYSTEM ( default OFF )**: Android JNI IOSystem support is active
-- **ASSIMP_NO_EXPORT ( default OFF )**: Disable Assimp's export functionality
-- **ASSIMP_BUILD_ZLIB ( default OFF )**: Build your own zlib
-- **ASSIMP_BUILD_ASSIMP_TOOLS ( default ON )**: If the supplementary tools for Assimp are built in addition to the library.
-- **ASSIMP_BUILD_SAMPLES ( default OFF )**: If the official samples are built as well (needs Glut).
-- **ASSIMP_BUILD_TESTS ( default ON )**: If the test suite for Assimp is built in addition to the library.
-- **ASSIMP_COVERALLS ( default OFF )**: Enable this to measure test coverage.
-- **ASSIMP_ERROR_MAX( default OFF)**: Enable all warnings.
-- **ASSIMP_WERROR( default OFF )**: Treat warnings as errors.
-- **ASSIMP_ASAN ( default OFF )**: Enable AddressSanitizer.
-- **ASSIMP_UBSAN ( default OFF )**: Enable Undefined Behavior sanitizer.
-- **SYSTEM_IRRXML ( default OFF )**: Use system installed Irrlicht/IrrXML library.
-- **BUILD_DOCS ( default OFF )**: Build documentation using Doxygen.
-- **INJECT_DEBUG_POSTFIX( default ON )**: Inject debug postfix in .a/.so lib names
-- **IGNORE_GIT_HASH ( default OFF )**: Don't call git to get the hash.
-- **ASSIMP_INSTALL_PDB ( default ON )**: Install MSVC debug files.
+- **ASSIMP_HUNTER_ENABLED (default OFF)**: Enable Hunter package manager support.
+- **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_BUILD_FRAMEWORK (default OFF, MacOnly)**: Build package as Mac OS X Framework bundle.
+- **ASSIMP_DOUBLE_PRECISION (default OFF)**: All data will be stored as double values.
+- **ASSIMP_OPT_BUILD_PACKAGES (default OFF)**: Set to ON to generate CPack configuration files and packaging targets.
+- **ASSIMP_ANDROID_JNIIOSYSTEM (default OFF)**: Android JNI IOSystem support is active.
+- **ASSIMP_NO_EXPORT (default OFF)**: Disable Assimp's export functionality.
+- **ASSIMP_BUILD_ZLIB (default OFF)**: Build our own zlib.
+- **ASSIMP_BUILD_ALL_EXPORTERS_BY_DEFAULT (default ON)**: Build Assimp with all exporter senabled.
+- **ASSIMP_BUILD_ALL_IMPORTERS_BY_DEFAULT (default ON)**: Build Assimp with all importer senabled.
+- **ASSIMP_BUILD_ASSIMP_TOOLS (default ON)**: If the supplementary tools for Assimp are built in addition to the library.
+- **ASSIMP_BUILD_SAMPLES (default OFF)**: If the official samples are built as well (needs Glut).
+- **ASSIMP_BUILD_TESTS (default ON)**: If the test suite for Assimp is built in addition to the library.
+- **ASSIMP_COVERALLS (default OFF)**: Enable this to measure test coverage.
+- **ASSIMP_INSTALL (default ON)**: Install Assimp library. Disable this if you want to use Assimp as a submodule.
+- **ASSIMP_WARNINGS_AS_ERRORS (default ON)**: Treat all warnings as errors.
+- **ASSIMP_ASAN (default OFF)**: Enable AddressSanitizer.
+- **ASSIMP_UBSAN (default OFF)**: Enable Undefined Behavior sanitizer.
+- **ASSIMP_BUILD_DOCS (default OFF)**: Build documentation using Doxygen. OBSOLETE, see https://github.com/assimp/assimp-docs
+- **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).

+ 33 - 29
CMakeLists.txt

@@ -108,9 +108,9 @@ OPTION( ASSIMP_INSTALL
   "Disable this if you want to use assimp as a submodule."
   ON
 )
-OPTION ( ASSIMP_ERROR_MAX
-  "Enable all warnings."
-  OFF
+OPTION ( ASSIMP_WARNINGS_AS_ERRORS
+  "Treat all warnings as errors."
+  ON
 )
 OPTION ( ASSIMP_ASAN
   "Enable AddressSanitizer."
@@ -139,10 +139,6 @@ IF (WIN32)
   ADD_DEFINITIONS( -DWIN32_LEAN_AND_MEAN )
 
   IF(MSVC)
-    OPTION (ASSIMP_BUILD_ASSIMP_VIEW
-      "If the Assimp view tool is built. (requires DirectX)"
-      OFF )
-
     OPTION( ASSIMP_INSTALL_PDB
       "Install MSVC debug files."
       ON )
@@ -150,6 +146,21 @@ IF (WIN32)
       # Multibyte character set is deprecated since at least MSVC2015 (possibly earlier)
       ADD_DEFINITIONS( -DUNICODE -D_UNICODE )
     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()
 
@@ -169,6 +180,7 @@ ENDIF()
 IF(NOT BUILD_SHARED_LIBS)
   MESSAGE(STATUS "Shared libraries disabled")
   SET(LINK_SEARCH_START_STATIC TRUE)
+  SET(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_STATIC_LIBRARY_SUFFIX} ${CMAKE_FIND_LIBRARY_SUFFIXES})
 ELSE()
   MESSAGE(STATUS "Shared libraries enabled")
 ENDIF()
@@ -216,16 +228,6 @@ IF(ASSIMP_DOUBLE_PRECISION)
   ADD_DEFINITIONS(-DASSIMP_DOUBLE_PRECISION)
 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
   ./
   code/
@@ -322,16 +324,6 @@ IF (ASSIMP_COVERALLS)
   SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage")
 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)
   MESSAGE(STATUS "AddressSanitizer enabled")
   SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address")
@@ -692,10 +684,12 @@ ENDIF()
 
 # Main assimp code
 ADD_SUBDIRECTORY( code/ )
+
 IF ( ASSIMP_BUILD_ASSIMP_TOOLS )
   # The viewer for windows only
   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 )
       ADD_SUBDIRECTORY( tools/assimp_view/ )
     ENDIF ()
@@ -720,12 +714,22 @@ IF ( ASSIMP_BUILD_TESTS )
   ADD_SUBDIRECTORY( test/ )
 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 )
 IF ( ASSIMP_INSTALL )
   INSTALL( FILES "${PROJECT_BINARY_DIR}/assimp.pc" DESTINATION ${ASSIMP_LIB_INSTALL_DIR}/pkgconfig/ COMPONENT ${LIBASSIMP-DEV_COMPONENT})
 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(CMAKE_CPACK_COMMAND AND UNIX AND ASSIMP_OPT_BUILD_PACKAGES)
     # Packing information

+ 0 - 1
README

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

+ 3 - 0
Readme.md

@@ -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"/>
 </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)
+
 [![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)
 [![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/)
 <br>
 
@@ -58,6 +60,7 @@ Open Asset Import Library is implemented in C++. The directory structure looks l
 	/code		Source code
 	/contrib	Third-party libraries
 	/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
 	/scripts 	Scripts used to generate the loading code for some formats
 	/port		Ports to other languages and scripts to maintain those.

+ 1 - 1
cmake-modules/FindDirectX.cmake

@@ -55,7 +55,7 @@ if(WIN32) # The only platform it makes sense to check for DirectX SDK
   endif(CMAKE_CL_64)
   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_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})
 
 

+ 1 - 1
code/AssetLib/3MF/XmlSerializer.cpp

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

+ 6 - 3
code/AssetLib/ASE/ASEParser.cpp

@@ -646,10 +646,13 @@ void Parser::ParseLV2MaterialBlock(ASE::Material &mat) {
                 }
 
                 // 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;
             }
         }

+ 1 - 1
code/AssetLib/Assxml/AssxmlFileWriter.cpp

@@ -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\"",
                         prop->mKey.data, sz,
-                        ::TextureTypeToString((aiTextureType)prop->mSemantic), prop->mIndex);
+                        ::aiTextureTypeToString((aiTextureType)prop->mSemantic), prop->mIndex);
 
                 if (prop->mType == aiPTI_Float) {
                     ioprintf(io, " size=\"%i\">\n\t\t\t\t",

+ 1 - 1
code/AssetLib/Blender/BlenderLoader.cpp

@@ -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;
     // Count number of objects

+ 1 - 1
code/AssetLib/Blender/BlenderLoader.h

@@ -117,7 +117,7 @@ protected:
     void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) override;
     void ParseBlendFile(Blender::FileDatabase &out, std::shared_ptr<IOStream> stream);
     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);
 
 private:

+ 18 - 25
code/AssetLib/FBX/FBXConverter.cpp

@@ -228,7 +228,6 @@ void FBXConverter::ConvertNodes(uint64_t id, aiNode *parent, aiNode *root_node)
             nodes_chain.clear();
             post_nodes_chain.clear();
 
-            aiMatrix4x4 new_abs_transform = parent->mTransformation;
             std::string node_name = FixNodeName(model->Name());
             // even though there is only a single input node, the design of
             // 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;
                 last_parent = child.mNode;
-
-                new_abs_transform *= child->mTransformation;
             }
 
             // 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
             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;
                     last_parent = postnode.mNode;
-
-                    new_abs_transform *= postnode->mTransformation;
                 }
             } else {
                 // 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,
-        const aiMatrix4x4 &absolute_transform) {
+void FBXConverter::ConvertModel(const Model &model, aiNode *parent, aiNode *root_node) {
     const std::vector<const Geometry *> &geos = model.GetGeometry();
 
     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 LineGeometry *const line = dynamic_cast<const LineGeometry *>(geo);
         if (mesh) {
-            const std::vector<unsigned int> &indices = ConvertMesh(*mesh, model, parent, root_node,
-                    absolute_transform);
+            const std::vector<unsigned int> &indices = ConvertMesh(*mesh, model, parent, root_node);
             std::copy(indices.begin(), indices.end(), std::back_inserter(meshes));
         } else if (line) {
             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>
-FBXConverter::ConvertMesh(const MeshGeometry &mesh, const Model &model, aiNode *parent, aiNode *root_node,
-        const aiMatrix4x4 &absolute_transform) {
+FBXConverter::ConvertMesh(const MeshGeometry &mesh, const Model &model, aiNode *parent, aiNode *root_node) {
     std::vector<unsigned int> temp;
 
     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];
         for (MatIndexArray::value_type index : mindices) {
             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
-    temp.push_back(ConvertMeshSingleMaterial(mesh, model, absolute_transform, parent, root_node));
+    temp.push_back(ConvertMeshSingleMaterial(mesh, model, parent, root_node));
     return temp;
 }
 
@@ -1055,8 +1047,7 @@ static aiSkeleton *createAiSkeleton(SkeletonBoneContainer &sbc) {
 }
 
 unsigned int FBXConverter::ConvertMeshSingleMaterial(const MeshGeometry &mesh, const Model &model,
-        const aiMatrix4x4 &absolute_transform, aiNode *parent,
-        aiNode *) {
+        aiNode *parent, aiNode *) {
     const MatIndexArray &mindices = mesh.GetMaterialIndices();
     aiMesh *const out_mesh = SetupEmptyMesh(mesh, parent);
 
@@ -1230,8 +1221,7 @@ unsigned int FBXConverter::ConvertMeshSingleMaterial(const MeshGeometry &mesh, c
 
 std::vector<unsigned int>
 FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model, aiNode *parent,
-        aiNode *root_node,
-        const aiMatrix4x4 &absolute_transform) {
+        aiNode *root_node) {
     const MatIndexArray &mindices = mesh.GetMaterialIndices();
     ai_assert(mindices.size());
 
@@ -1241,7 +1231,7 @@ FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &mo
     for (MatIndexArray::value_type index : mindices) {
         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);
         }
     }
@@ -1251,8 +1241,7 @@ FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &mo
 
 unsigned int FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model,
         MatIndexArray::value_type index,
-        aiNode *parent, aiNode *,
-        const aiMatrix4x4 &absolute_transform) {
+        aiNode *parent, aiNode *) {
     aiMesh *const out_mesh = SetupEmptyMesh(mesh, parent);
 
     const MatIndexArray &mindices = mesh.GetMaterialIndices();
@@ -1415,7 +1404,7 @@ unsigned int FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, co
     ConvertMaterialForMesh(out_mesh, model, mesh, index);
 
     if (process_weights) {
-        ConvertWeights(out_mesh, mesh, absolute_transform, parent, index, &reverseMapping);
+        ConvertWeights(out_mesh, mesh, parent, index, &reverseMapping);
     }
 
     std::vector<aiAnimMesh *> animMeshes;
@@ -1497,7 +1486,6 @@ void FBXConverter::ConvertWeightsToSkeleton(aiMesh *out, const MeshGeometry &geo
 }
 
 void FBXConverter::ConvertWeights(aiMesh *out, const MeshGeometry &geo,
-        const aiMatrix4x4 &absolute_transform,
         aiNode *parent, unsigned int materialIndex,
         std::vector<unsigned int> *outputVertStartIndices) {
     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
             // data in a single step.
             ConvertCluster(bones, cluster, out_indices, index_out_indices,
-                    count_out_indices, absolute_transform, parent);
+                    count_out_indices, parent);
         }
 
         bone_map.clear();
@@ -1605,14 +1593,16 @@ void FBXConverter::ConvertCluster(std::vector<aiBone*> &local_mesh_bones, const
         bone = new aiBone();
         bone->mName = bone_name;
 
+        bone->mOffsetMatrix = cl->Transform();
         // store local transform link for post processing
+        /*
         bone->mOffsetMatrix = cluster->TransformLink();
         bone->mOffsetMatrix.Inverse();
 
         aiMatrix4x4 matrix = (aiMatrix4x4)absolute_transform;
 
         bone->mOffsetMatrix = bone->mOffsetMatrix * matrix; // * mesh_offset
-
+        */
         //
         // Now calculate the aiVertexWeights
         //
@@ -2213,6 +2203,9 @@ void FBXConverter::SetShadingPropertiesCommon(aiMaterial *out_mat, const Propert
     const float ShininessExponent = PropertyGet<float>(props, "ShininessExponent", ok);
     if (ok) {
         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:

+ 8 - 13
code/AssetLib/FBX/FBXConverter.h

@@ -191,14 +191,12 @@ private:
     void SetupNodeMetadata(const Model& model, aiNode& nd);
 
     // ------------------------------------------------------------------------------------------------
-    void ConvertModel(const Model &model, aiNode *parent, aiNode *root_node,
-                      const aiMatrix4x4 &absolute_transform);
+    void ConvertModel(const Model &model, aiNode *parent, aiNode *root_node);
 
     // ------------------------------------------------------------------------------------------------
     // MeshGeometry -> aiMesh, return mesh index + 1 or 0 if the conversion failed
     std::vector<unsigned int>
-    ConvertMesh(const MeshGeometry &mesh, const Model &model, aiNode *parent, aiNode *root_node,
-                const aiMatrix4x4 &absolute_transform);
+    ConvertMesh(const MeshGeometry &mesh, const Model &model, aiNode *parent, 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,
-                                           const aiMatrix4x4 &absolute_transform, aiNode *parent,
-                                           aiNode *root_node);
+                                           aiNode *parent, aiNode *root_node);
 
     // ------------------------------------------------------------------------------------------------
     std::vector<unsigned int>
-    ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model, aiNode *parent, aiNode *root_node,
-                             const aiMatrix4x4 &absolute_transform);
+    ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model, aiNode *parent, aiNode *root_node);
 
     // ------------------------------------------------------------------------------------------------
     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() */
@@ -231,8 +227,8 @@ private:
     *  - outputVertStartIndices is only used when a material index is specified, it gives for
     *    each output vertex the DOM index it maps to.
     */
-    void ConvertWeights(aiMesh *out, const MeshGeometry &geo, const aiMatrix4x4 &absolute_transform,
-            aiNode *parent = nullptr, unsigned int materialIndex = NO_MATERIAL_SEPARATION,
+    void ConvertWeights(aiMesh *out, const MeshGeometry &geo, aiNode *parent = nullptr,
+            unsigned int materialIndex = NO_MATERIAL_SEPARATION,
             std::vector<unsigned int> *outputVertStartIndices = nullptr);
 
     // ------------------------------------------------------------------------------------------------
@@ -243,8 +239,7 @@ private:
     // ------------------------------------------------------------------------------------------------
     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> &count_out_indices, const aiMatrix4x4 &absolute_transform,
-                        aiNode *parent );
+                        std::vector<size_t> &count_out_indices, aiNode *parent );
 
     // ------------------------------------------------------------------------------------------------
     void ConvertMaterialForMesh(aiMesh* out, const Model& model, const MeshGeometry& geo,

+ 23 - 54
code/AssetLib/FBX/FBXDocument.cpp

@@ -235,10 +235,8 @@ FileGlobalSettings::FileGlobalSettings(const Document &doc, std::shared_ptr<cons
 }
 
 // ------------------------------------------------------------------------------------------------
-Document::Document(const Parser& parser, const ImportSettings& settings)
-: settings(settings)
-, parser(parser)
-{
+Document::Document(const Parser& parser, const ImportSettings& settings) :
+     settings(settings), parser(parser) {
 	ASSIMP_LOG_DEBUG("Creating FBX Document");
 
     // 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) {
         delete v.second;
     }
@@ -322,8 +319,7 @@ void Document::ReadHeader() {
 }
 
 // ------------------------------------------------------------------------------------------------
-void Document::ReadGlobalSettings()
-{
+void Document::ReadGlobalSettings() {
     const Scope& sc = parser.GetRootScope();
     const Element* const ehead = sc["GlobalSettings"];
     if ( nullptr == ehead || !ehead->Compound() ) {
@@ -344,8 +340,7 @@ void Document::ReadGlobalSettings()
 }
 
 // ------------------------------------------------------------------------------------------------
-void Document::ReadObjects()
-{
+void Document::ReadObjects() {
     // read ID objects from "Objects" section
     const Scope& sc = parser.GetRootScope();
     const Element* const eobjects = sc["Objects"];
@@ -392,8 +387,7 @@ void Document::ReadObjects()
 }
 
 // ------------------------------------------------------------------------------------------------
-void Document::ReadPropertyTemplates()
-{
+void Document::ReadPropertyTemplates() {
     const Scope& sc = parser.GetRootScope();
     // read property templates from "Definitions" section
     const Element* const edefs = sc["Definitions"];
@@ -450,8 +444,7 @@ void Document::ReadPropertyTemplates()
 }
 
 // ------------------------------------------------------------------------------------------------
-void Document::ReadConnections()
-{
+void Document::ReadConnections() {
     const Scope& sc = parser.GetRootScope();
     // read property templates from "Definitions" section
     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()) {
         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);
     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;
 
     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());
 }
 
 // ------------------------------------------------------------------------------------------------
-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};
     return GetConnectionsBySourceSequenced(src, arr,1);
 }
 
 // ------------------------------------------------------------------------------------------------
 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);
 }
 
 // ------------------------------------------------------------------------------------------------
 std::vector<const Connection*> Document::GetConnectionsByDestinationSequenced(uint64_t dest,
-        const char* classname) const
-{
+        const char* classname) const {
     const char* arr[] = {classname};
     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());
 }
 
@@ -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,
-        const Document& doc)
-
-: insertionOrder(insertionOrder)
-, prop(prop)
-, src(src)
-, dest(dest)
-, doc(doc)
-{
+            const Document& doc) :
+            insertionOrder(insertionOrder), prop(prop), src(src), dest(dest), doc(doc) {
     ai_assert(doc.Objects().find(src) != doc.Objects().end());
     // dest may be 0 (root node)
     ai_assert(!dest || doc.Objects().find(dest) != doc.Objects().end());
 }
 
 // ------------------------------------------------------------------------------------------------
-Connection::~Connection()
-{
-    // empty
-}
-
-// ------------------------------------------------------------------------------------------------
-LazyObject& Connection::LazySourceObject() const
-{
+LazyObject& Connection::LazySourceObject() const {
     LazyObject* const lazy = doc.GetObject(src);
     ai_assert(lazy);
     return *lazy;
 }
 
 // ------------------------------------------------------------------------------------------------
-LazyObject& Connection::LazyDestinationObject() const
-{
+LazyObject& Connection::LazyDestinationObject() const {
     LazyObject* const lazy = doc.GetObject(dest);
     ai_assert(lazy);
     return *lazy;
 }
 
 // ------------------------------------------------------------------------------------------------
-const Object* Connection::SourceObject() const
-{
+const Object* Connection::SourceObject() const {
     LazyObject* const lazy = doc.GetObject(src);
     ai_assert(lazy);
     return lazy->Get();
 }
 
 // ------------------------------------------------------------------------------------------------
-const Object* Connection::DestinationObject() const
-{
+const Object* Connection::DestinationObject() const {
     LazyObject* const lazy = doc.GetObject(dest);
     ai_assert(lazy);
     return lazy->Get();
@@ -689,4 +658,4 @@ const Object* Connection::DestinationObject() const
 } // !FBX
 } // !Assimp
 
-#endif
+#endif // ASSIMP_BUILD_NO_FBX_IMPORTER

+ 20 - 28
code/AssetLib/FBX/FBXDocument.h

@@ -4,7 +4,6 @@ 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,
@@ -267,8 +266,7 @@ public:
     Light(uint64_t id, const Element& element, const Document& doc, const std::string& name);
     virtual ~Light() = default;
 
-    enum Type
-    {
+    enum Type {
         Type_Point,
         Type_Directional,
         Type_Spot,
@@ -278,8 +276,7 @@ public:
         Type_MAX // end-of-enum sentinel
     };
 
-    enum Decay
-    {
+    enum Decay {
         Decay_None,
         Decay_Linear,
         Decay_Quadratic,
@@ -347,7 +344,7 @@ public:
 
     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)
 
@@ -578,31 +575,27 @@ public:
         BlendMode_BlendModeCount
     };
 
-    const Texture* getTexture(int index=0) const
-    {
+    const Texture* getTexture(int index=0) const {
 		return textures[index];
-
     }
 	int textureCount() const {
 		return static_cast<int>(textures.size());
 	}
-    BlendMode GetBlendMode() const
-    {
+    BlendMode GetBlendMode() const {
         return blendMode;
     }
-    float Alpha()
-    {
+    float Alpha() {
         return alpha;
     }
+
 private:
 	std::vector<const Texture*> textures;
     BlendMode blendMode;
     float alpha;
 };
 
-typedef std::fbx_unordered_map<std::string, const Texture*> TextureMap;
-typedef std::fbx_unordered_map<std::string, const LayeredTexture*> LayeredTextureMap;
-
+using TextureMap = std::fbx_unordered_map<std::string, const Texture*>;
+using LayeredTextureMap = std::fbx_unordered_map<std::string, const LayeredTexture*>;
 
 /** DOM class for generic FBX videos */
 class Video : public Object {
@@ -690,8 +683,8 @@ private:
     LayeredTextureMap layeredTextures;
 };
 
-typedef std::vector<int64_t> KeyTimeList;
-typedef std::vector<float> KeyValueList;
+using KeyTimeList = std::vector<int64_t>;
+using KeyValueList = std::vector<float>;
 
 /** Represents a FBX animation curve (i.e. a 1-dimensional set of keyframes and values therefore) */
 class AnimationCurve : public Object {
@@ -727,7 +720,7 @@ private:
 };
 
 // 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) */
 class AnimationCurveNode : public Object {
@@ -777,7 +770,7 @@ private:
     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) */
 class AnimationLayer : public Object {
@@ -800,7 +793,7 @@ private:
     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) */
 class AnimationStack : public Object {
@@ -843,8 +836,8 @@ private:
     std::shared_ptr<const PropertyTable> props;
 };
 
-typedef std::vector<float> WeightArray;
-typedef std::vector<unsigned int> WeightIndexArray;
+using WeightArray = std::vector<float>;
+using WeightIndexArray = std::vector<unsigned int>;
 
 
 /** DOM class for BlendShapeChannel deformers */
@@ -956,7 +949,7 @@ class Connection {
 public:
     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
     // 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
 // up to many thousands of objects (most of which we never use),
 // so the memory overhead for them should be kept at a minimum.
-typedef std::fbx_unordered_map<uint64_t, LazyObject*> ObjectMap;
-typedef std::fbx_unordered_map<std::string, std::shared_ptr<const PropertyTable> > PropertyTemplateMap;
-
-typedef std::fbx_unordered_multimap<uint64_t, const Connection*> ConnectionMap;
+using ObjectMap = std::fbx_unordered_map<uint64_t, LazyObject*> ;
+using PropertyTemplateMap = std::fbx_unordered_map<std::string, std::shared_ptr<const PropertyTable> > ;
+using ConnectionMap = std::fbx_unordered_multimap<uint64_t, const Connection*>;
 
 /** DOM class for global document settings, a single instance per document can
  *  be accessed via Document.Globals(). */

+ 22 - 1
code/AssetLib/FBX/FBXMaterial.cpp

@@ -140,11 +140,32 @@ Material::~Material() {
     // 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) :
         Object(id,element,name),
+        uvTrans(0.0f, 0.0f),
         uvScaling(1.0f,1.0f),
-        media(0) {
+        uvRotation(0.0f),
+        type(),
+        relativeFileName(),
+        fileName(),
+        alphaSource(),
+        props(),
+        media(nullptr) {
     const Scope& sc = GetRequiredScope(element);
 
     const Element* const Type = sc["Type"];

+ 0 - 5
code/AssetLib/FBX/FBXModel.cpp

@@ -4,7 +4,6 @@ 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,
@@ -76,10 +75,6 @@ Model::Model(uint64_t id, const Element &element, const Document &doc, const std
     ResolveLinks(element, doc);
 }
 
-// ------------------------------------------------------------------------------------------------
-Model::~Model() {
-}
-
 // ------------------------------------------------------------------------------------------------
 void Model::ResolveLinks(const Element&, const Document &doc) {
     const char *const arr[] = { "Geometry", "Material", "NodeAttribute" };

+ 4 - 2
code/AssetLib/IFC/IFCCurve.cpp

@@ -4,7 +4,6 @@ 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,
@@ -51,7 +50,6 @@ namespace Assimp {
 namespace IFC {
 namespace {
 
-
 // --------------------------------------------------------------------------------
 // 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[ 1 ] != inf );
+#endif // __INTEL_LLVM_COMPILER
     if ( std::fabs(a-min_point[0]) < threshold || recurse >= max_recurse) {
         return min_point[0];
     }
@@ -606,8 +606,10 @@ bool BoundedCurve::IsClosed() const {
 // ------------------------------------------------------------------------------------------------
 void BoundedCurve::SampleDiscrete(TempMesh& out) const {
     const ParamRange& range = GetParametricRange();
+#ifndef __INTEL_LLVM_COMPILER
     ai_assert( range.first != 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);
 }

+ 5 - 5
code/AssetLib/IFC/IFCOpenings.cpp

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

+ 77 - 0
code/AssetLib/LWO/LWOFileData.h

@@ -64,6 +64,7 @@ namespace LWO {
 
 #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_LWO3 AI_IFF_FOURCC('L', 'W', 'O', '3')
 #define AI_LWO_FOURCC_LXOB AI_IFF_FOURCC('L', 'X', 'O', 'B')
 
 // 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_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
 #define AI_LWO_MODO_NORM AI_IFF_FOURCC('N', 'O', 'R', 'M')
 
@@ -555,6 +607,31 @@ struct Surface {
     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)              \
     if (length < size) {                                              \

+ 209 - 13
code/AssetLib/LWO/LWOLoader.cpp

@@ -83,6 +83,7 @@ static const aiImporterDesc desc = {
 LWOImporter::LWOImporter() :
         mIsLWO2(),
         mIsLXOB(),
+        mIsLWO3(),
         mLayers(),
         mCurLayer(),
         mTags(),
@@ -182,16 +183,19 @@ void LWOImporter::InternReadFile(const std::string &pFile,
     mCurLayer->mIndex = (uint16_t) -1;
 
     // old lightwave file format (prior to v6)
+    mIsLWO2 = false;
+    mIsLWO3 = false;
+    mIsLXOB = false;
+
     if (AI_LWO_FOURCC_LWOB == fileType) {
         ASSIMP_LOG_INFO("LWO file format: LWOB (<= LightWave 5.5)");
 
-        mIsLWO2 = false;
-        mIsLXOB = false;
         LoadLWOBFile();
     } else if (AI_LWO_FOURCC_LWO2 == fileType) {
         // New lightwave format
-        mIsLXOB = false;
         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) {
         // MODO file format
         mIsLXOB = true;
@@ -207,8 +211,13 @@ void LWOImporter::InternReadFile(const std::string &pFile,
         throw DeadlyImportError("Unknown LWO sub format: ", szBuff);
     }
 
-    if (AI_LWO_FOURCC_LWOB != fileType) {
-        mIsLWO2 = true;
+    if (AI_LWO_FOURCC_LWOB != fileType) {   //
+        if( AI_LWO_FOURCC_LWO3 == fileType ) {
+            mIsLWO3 = true;
+        } else {
+            mIsLWO2 = true;
+        }
+        
         LoadLWO2File();
 
         // 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
     pScene->mMaterials = new aiMaterial *[pScene->mNumMaterials = (unsigned int)mSurfaces->size()];
+
     for (unsigned int mat = 0; mat < pScene->mNumMaterials; ++mat) {
         aiMaterial *pcMat = new aiMaterial();
         pScene->mMaterials[mat] = pcMat;
@@ -687,7 +697,7 @@ void LWOImporter::ResolveClips() {
 // ------------------------------------------------------------------------------------------------
 void LWOImporter::AdjustTexturePath(std::string &out) {
     // --- 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
         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)");
     }
     unsigned int regularSize = (unsigned int)mCurLayer->mTempPoints.size() + length / 12;
-    if (mIsLWO2) {
+    if (mIsLWO2 || mIsLWO3) {
         mCurLayer->mTempPoints.reserve(regularSize + (regularSize >> 2u));
         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
 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
 void LWOImporter::LoadLWO2File() {
@@ -1272,16 +1450,25 @@ void LWOImporter::LoadLWO2File() {
 
     LE_NCONST uint8_t *const end = mFileBuffer + fileSize;
     unsigned int iUnnamed = 0;
+
     while (true) {
         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) {
             throw DeadlyImportError("LWO2: Chunk length points behind the file");
             break;
         }
         uint8_t *const next = mFileBuffer + head.length;
-
+        mFileBuffer += bufOffset;
         if (!head.length) {
             mFileBuffer = next;
             continue;
@@ -1337,7 +1524,6 @@ void LWOImporter::LoadLWO2File() {
 
                 break;
             }
-
                 // vertex list
             case AI_LWO_PNTS: {
                 if (skip)
@@ -1399,19 +1585,29 @@ void LWOImporter::LoadLWO2File() {
 
                 // surface chunk
             case AI_LWO_SURF: {
-                LoadLWO2Surface(head.length);
+                if( mIsLWO3 )
+                    LoadLWO3Surface(head.length);
+                else
+                    LoadLWO2Surface(head.length);
+
                 break;
             }
 
                 // clip chunk
             case AI_LWO_CLIP: {
-                LoadLWO2Clip(head.length);
+                if( mIsLWO3 )
+                    LoadLWO3Clip(head.length);
+                else
+                    LoadLWO2Clip(head.length);
                 break;
             }
 
                 // envelope chunk
             case AI_LWO_ENVL: {
-                LoadLWO2Envelope(head.length);
+                if( mIsLWO3 )
+                    LoadLWO3Envelope(head.length);
+                else
+                    LoadLWO2Envelope(head.length);
                 break;
             }
         }

+ 32 - 0
code/AssetLib/LWO/LWOLoader.h

@@ -116,6 +116,8 @@ private:
     */
     inline void GetS0(std::string &out, unsigned int max);
     inline float GetF4();
+    inline float GetF8();
+    inline uint64_t GetU8();
     inline uint32_t GetU4();
     inline uint16_t GetU2();
     inline uint8_t GetU1();
@@ -131,6 +133,7 @@ private:
      *  @param size Maximum size to be read, in bytes.
      */
     void LoadLWO2Surface(unsigned int size);
+    void LoadLWO3Surface(unsigned int size);
 
     // -------------------------------------------------------------------
     /** Loads a texture block from a LWO2 file.
@@ -197,12 +200,23 @@ private:
      *  @param length Size of the chunk
     */
     void LoadLWO2Clip(unsigned int length);
+    void LoadLWO3Clip(unsigned int length);
 
     // -------------------------------------------------------------------
     /** Load an envelope from an EVL chunk
      *  @param length Size of the chunk
     */
     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
@@ -347,6 +361,8 @@ protected:
     /** true if the file is a LXOB file*/
     bool mIsLXOB;
 
+    bool mIsLWO3;
+
     /** Temporary list of layers from the file */
     LayerList *mLayers;
 
@@ -400,6 +416,22 @@ inline float LWOImporter::GetF4() {
     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() {
     uint32_t f;

+ 313 - 2
code/AssetLib/LWO/LWOMaterial.cpp

@@ -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 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
             // share the same index)
             ClipList::iterator end = mClips.end(), candidate = end;
@@ -270,7 +270,7 @@ void LWOImporter::ConvertMaterial(const LWO::Surface &surf, aiMaterial *pcMat) {
     aiShadingMode m;
     if (surf.mSpecularValue && surf.mGlossiness) {
         float fGloss;
-        if (mIsLWO2) {
+        if (mIsLWO2 || mIsLWO3) {
             fGloss = std::pow(surf.mGlossiness * ai_real(10.0) + ai_real(2.0), ai_real(2.0));
         } else {
             if (16.0 >= surf.mGlossiness)
@@ -688,6 +688,252 @@ void LWOImporter::LoadLWO2ShaderBlock(LE_NCONST IFF::SubChunkHeader * /*head*/,
     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) {
     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

+ 1 - 3
code/AssetLib/M3D/M3DWrapper.h

@@ -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
 //#define ASSIMP_USE_M3D_READFILECB
 
-// Share stb_image's PNG loader with other importers/exporters instead of bringing our own copy.
-#define STBI_ONLY_PNG
-#include <stb/stb_image.h>
+#include "Common/StbCommon.h"
 
 #include "m3d.h"
 

+ 3 - 0
code/AssetLib/MDL/MDLLoader.cpp

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

+ 1 - 1
code/AssetLib/MDL/MDLMaterialLoader.cpp

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

+ 21 - 14
code/AssetLib/MMD/MMDImporter.cpp

@@ -52,9 +52,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/scene.h>
 #include <assimp/Importer.hpp>
 
-#include <fstream>
+
 #include <iomanip>
 #include <memory>
+#include <sstream>
 
 static const aiImporterDesc desc = { "MMD Importer",
     "",
@@ -102,26 +103,32 @@ const aiImporterDesc *MMDImporter::GetInfo() const {
 // ------------------------------------------------------------------------------------------------
 //  MMD import implementation
 void MMDImporter::InternReadFile(const std::string &file, aiScene *pScene,
-        IOSystem * /*pIOHandler*/) {
-    // Read file by istream
-    std::filebuf fb;
-    if (!fb.open(file, std::ios::in | std::ios::binary)) {
-        throw DeadlyImportError("Failed to open file ", file, ".");
-    }
+        IOSystem* pIOHandler) {
+
+    auto streamCloser = [&](IOStream *pStream) {
+        pIOHandler->Close(pStream);
+    };
 
-    std::istream fileStream(&fb);
+    static const std::string mode = "rb";
+    const std::unique_ptr<IOStream, decltype(streamCloser)> fileStream(pIOHandler->Open(file, mode), streamCloser);
 
-    // 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 (fileStream == nullptr) {
+        throw DeadlyImportError("Failed to open file ", file, ".");
+    }
 
-    if (fileSize < sizeof(pmx::PmxModel)) {
+    const size_t fileSize = fileStream->FileSize();
+    if (fileSize < sizeof(pmx::PmxModel))
+    {
         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;
-    model.Read(&fileStream);
+    model.Read(&iss);
 
     CreateDataFromImport(&model, pScene);
 }

+ 11 - 10
code/AssetLib/Obj/ObjFileData.h

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

+ 10 - 5
code/AssetLib/Obj/ObjFileImporter.cpp

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

+ 19 - 2
code/AssetLib/Obj/ObjFileMtlImporter.cpp

@@ -205,7 +205,7 @@ void ObjFileMtlImporter::load() {
                         break;
                     case 's':
                         ++m_DataIt;
-                        getColorRGBA(&m_pModel->m_pCurrentMaterial->sheen);
+                        getColorRGBA(m_pModel->m_pCurrentMaterial->sheen);
                         break;
                     case 'c':
                         ++m_DataIt;
@@ -238,6 +238,7 @@ void ObjFileMtlImporter::load() {
 
             case 'a': // Anisotropy
             {
+                ++m_DataIt;
                 getFloatValue(m_pModel->m_pCurrentMaterial->anisotropy);
                 m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
             } break;
@@ -267,6 +268,12 @@ void ObjFileMtlImporter::getColorRGBA(aiColor3D *pColor) {
     pColor->b = b;
 }
 
+void ObjFileMtlImporter::getColorRGBA(Maybe<aiColor3D> &value) {
+    aiColor3D v;
+    getColorRGBA(&v);
+    value = Maybe<aiColor3D>(v);
+}
+
 // -------------------------------------------------------------------
 //  Loads the kind of illumination model.
 void ObjFileMtlImporter::getIlluminationModel(int &illum_model) {
@@ -274,6 +281,7 @@ void ObjFileMtlImporter::getIlluminationModel(int &illum_model) {
     illum_model = atoi(&m_buffer[0]);
 }
 
+
 // -------------------------------------------------------------------
 //  Loads a single float value.
 void ObjFileMtlImporter::getFloatValue(ai_real &value) {
@@ -283,10 +291,19 @@ void ObjFileMtlImporter::getFloatValue(ai_real &value) {
         value = 0.0f;
         return;
     }
-    
+
     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.
 void ObjFileMtlImporter::createMaterial() {

+ 3 - 0
code/AssetLib/Obj/ObjFileMtlImporter.h

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

+ 22 - 1
code/AssetLib/Obj/ObjFileParser.cpp

@@ -55,7 +55,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 namespace Assimp {
 
-constexpr char ObjFileParser::DEFAULT_MATERIAL[];
+constexpr const char ObjFileParser::DEFAULT_MATERIAL[];
 
 ObjFileParser::ObjFileParser() :
         m_DataIt(),
@@ -117,6 +117,7 @@ void ObjFileParser::parseFile(IOStreamBuffer<char> &streamBuffer) {
     unsigned int processed = 0;
     size_t lastFilePos(0);
 
+    bool insideCstype = false;
     std::vector<char> buffer;
     while (streamBuffer.getNextDataLine(buffer, '\\')) {
         m_DataIt = buffer.begin();
@@ -131,6 +132,18 @@ void ObjFileParser::parseFile(IOStreamBuffer<char> &streamBuffer) {
             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
         switch (*m_DataIt) {
         case 'v': // Parse a vertex texture coordinate
@@ -219,6 +232,14 @@ void ObjFileParser::parseFile(IOStreamBuffer<char> &streamBuffer) {
             getObjectName();
         } 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: {
         pf_skip_line:
             m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);

+ 1 - 1
code/AssetLib/Obj/ObjFileParser.h

@@ -141,7 +141,7 @@ private:
     // because the class contains pointer to allocated memory
 
     /// 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
     DataArrayIt m_DataIt;
     //! Iterator to end position of buffer

+ 21 - 11
code/AssetLib/Ogre/OgreXmlSerializer.cpp

@@ -248,6 +248,7 @@ void OgreXmlSerializer::ReadMesh(MeshXml *mesh) {
         } else if (currentName == nnBoneAssignments) {
             ReadBoneAssignments(currentNode, mesh->sharedVertexData);
         } 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();
     OgreXmlSerializer serializer(xmlParser.get());
     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);
     mesh->skeleton = skeleton;
     return true;
@@ -537,7 +547,7 @@ XmlParserPtr OgreXmlSerializer::OpenXmlParser(Assimp::IOSystem *pIOHandler, cons
 }
 
 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>");
     }
 
@@ -574,14 +584,14 @@ void OgreXmlSerializer::ReadAnimations(XmlNode &node, Skeleton *skeleton) {
             anim->name = ReadAttribute<std::string>(currentNode, "name");
             anim->length = ReadAttribute<float>(currentNode, "length");
             for (XmlNode &currentChildNode : currentNode.children()) {
-                const std::string currentChildName = currentNode.name();
+                const std::string currentChildName = currentChildNode.name();
                 if (currentChildName == nnTracks) {
                     ReadAnimationTracks(currentChildNode, anim);
-                    skeleton->animations.push_back(anim);
                 } else {
                     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.boneName = ReadAttribute<std::string>(currentNode, "bone");
             for (XmlNode &currentChildNode : currentNode.children()) {
-                const std::string currentChildName = currentNode.name();
+                const std::string currentChildName = currentChildNode.name();
                 if (currentChildName == nnKeyFrames) {
                     ReadAnimationKeyFrames(currentChildNode, dest, &track);
-                    dest->tracks.push_back(track);
                 } else {
                     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) {
             keyframe.timePos = ReadAttribute<float>(currentNode, "time");
             for (XmlNode &currentChildNode : currentNode.children()) {
-                const std::string currentChildName = currentNode.name();
+                const std::string currentChildName = currentChildNode.name();
                 if (currentChildName == nnTranslate) {
                     keyframe.position.x = ReadAttribute<float>(currentChildNode, anX);
                     keyframe.position.y = ReadAttribute<float>(currentChildNode, anY);
                     keyframe.position.z = ReadAttribute<float>(currentChildNode, anZ);
                 } else if (currentChildName == nnRotate) {
                     float angle = ReadAttribute<float>(currentChildNode, "angle");
-                    for (XmlNode &currentChildChildNode : currentNode.children()) {
-                        const std::string currentChildChildName = currentNode.name();
+                    for (XmlNode &currentChildChildNode : currentChildNode.children()) {
+                        const std::string currentChildChildName = currentChildChildNode.name();
                         if (currentChildChildName == nnAxis) {
                             aiVector3D axis;
                             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->name = ReadAttribute<std::string>(currentNode, "name");
             for (XmlNode &currentChildNode : currentNode.children()) {
-                const std::string currentChildName = currentNode.name();
-                if (currentChildName == nnRotation) {
+                const std::string currentChildName = currentChildNode.name();
+                if (currentChildName == nnPosition) {
                     bone->position.x = ReadAttribute<float>(currentChildNode, anX);
                     bone->position.y = ReadAttribute<float>(currentChildNode, anY);
                     bone->position.z = ReadAttribute<float>(currentChildNode, anZ);
-                } else if (currentChildName == nnScale) {
+                } else if (currentChildName == nnRotation) {
                     float angle = ReadAttribute<float>(currentChildNode, "angle");
                     for (XmlNode currentChildChildNode : currentChildNode.children()) {
                         const std::string &currentChildChildName = currentChildChildNode.name();

+ 1 - 1
code/AssetLib/X3D/X3DImporter.cpp

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

+ 1 - 1
code/AssetLib/glTF/glTFAsset.inl

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

+ 15 - 3
code/AssetLib/glTF2/glTF2Asset.inl

@@ -82,9 +82,21 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 // clang-format on
 
 using namespace Assimp;
-using namespace glTFCommon;
 
 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 {
 
@@ -179,11 +191,11 @@ inline bool GetAttribVector(Mesh::Primitive &p, const char *attr, Mesh::Accessor
         v = &(p.attributes.texcoord);
     } else if ((pos = Compare(attr, "COLOR"))) {
         v = &(p.attributes.color);
-    } else if ((pos = Compare(attr, "JOINT"))) {
+    } else if ((pos = Compare(attr, "JOINTS"))) {
         v = &(p.attributes.joint);
     } else if ((pos = Compare(attr, "JOINTMATRIX"))) {
         v = &(p.attributes.jointmatrix);
-    } else if ((pos = Compare(attr, "WEIGHT"))) {
+    } else if ((pos = Compare(attr, "WEIGHTS"))) {
         v = &(p.attributes.weight);
     } else
         return false;

+ 202 - 198
code/AssetLib/glTF2/glTF2Importer.cpp

@@ -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)
 
 #include "AssetLib/glTF2/glTF2Importer.h"
-#include "PostProcessing/MakeVerboseFormat.h"
 #include "AssetLib/glTF2/glTF2Asset.h"
+#include "PostProcessing/MakeVerboseFormat.h"
 
 #if !defined(ASSIMP_BUILD_NO_EXPORT)
 #include "AssetLib/glTF2/glTF2AssetWriter.h"
 #endif
 
 #include <assimp/CreateAnimMesh.h>
+#include <assimp/DefaultIOSystem.h>
 #include <assimp/StringComparison.h>
 #include <assimp/StringUtils.h>
 #include <assimp/ai_assert.h>
+#include <assimp/commonMetaData.h>
 #include <assimp/importerdesc.h>
 #include <assimp/scene.h>
 #include <assimp/DefaultLogger.hpp>
 #include <assimp/Importer.hpp>
-#include <assimp/commonMetaData.h>
-#include <assimp/DefaultIOSystem.h>
 
 #include <memory>
 #include <unordered_map>
@@ -111,7 +111,7 @@ const aiImporterDesc *glTF2Importer::GetInfo() const {
     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);
     if (!checkSig && (extension != "gltf") && (extension != "glb")) {
         return false;
@@ -127,16 +127,16 @@ bool glTF2Importer::CanRead(const std::string &filename, IOSystem *pIOHandler, b
 
 static inline aiTextureMapMode ConvertWrappingMode(SamplerWrap gltfWrapMode) {
     switch (gltfWrapMode) {
-        case SamplerWrap::Mirrored_Repeat:
-            return aiTextureMapMode_Mirror;
+    case SamplerWrap::Mirrored_Repeat:
+        return aiTextureMapMode_Mirror;
 
-        case SamplerWrap::Clamp_To_Edge:
-            return aiTextureMapMode_Clamp;
+    case SamplerWrap::Clamp_To_Edge:
+        return aiTextureMapMode_Clamp;
 
-        case SamplerWrap::UNSET:
-        case SamplerWrap::Repeat:
-        default:
-            return aiTextureMapMode_Wrap;
+    case SamplerWrap::UNSET:
+    case SamplerWrap::Repeat:
+    default:
+        return aiTextureMapMode_Wrap;
     }
 }
 
@@ -185,8 +185,9 @@ static void SetMaterialTextureProperty(std::vector<int> &embeddedTexIdxs, Asset
             // coordinate of the actual meshes during import.
             const ai_real rcos(cos(-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.y = ((static_cast<ai_real>( 0.5 ) * transform.mScaling.y) * (rsin + rcos - 1)) + 1 - transform.mScaling.y - prop.TextureTransformExt_t.offset[1];;
+            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];
+            ;
 
             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_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, 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.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(&mat.alphaCutoff, 1, AI_MATKEY_GLTF_ALPHACUTOFF);
 
-        //pbrSpecularGlossiness
+        // pbrSpecularGlossiness
         if (mat.pbrSpecularGlossiness.isPresent) {
             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);
 
-
         // KHR_materials_sheen
         if (mat.materialSheen.isPresent) {
             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) {
         return;
     }
@@ -388,7 +391,7 @@ static inline void SetFaceAndAdvance1(aiFace*& face, unsigned int numVertices, u
     ++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) {
     if ((a >= numVertices) || (b >= numVertices)) {
         return;
@@ -400,7 +403,7 @@ static inline void SetFaceAndAdvance2(aiFace*& face, unsigned int numVertices,
     ++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) {
     if ((a >= numVertices) || (b >= numVertices) || (c >= numVertices)) {
         return;
@@ -427,17 +430,16 @@ static inline bool CheckValidFacesIndices(aiFace *faces, unsigned nFaces, unsign
 }
 #endif // ASSIMP_BUILD_DEBUG
 
-template<typename T>
-aiColor4D* GetVertexColorsForType(Ref<Accessor> input) {
+template <typename T>
+aiColor4D *GetVertexColorsForType(Ref<Accessor> input) {
     constexpr float max = std::numeric_limits<T>::max();
-    aiColor4t<T>* colors;
+    aiColor4t<T> *colors;
     input->ExtractData(colors);
     auto output = new aiColor4D[input->count];
     for (size_t i = 0; i < input->count; i++) {
         output[i] = aiColor4D(
-            colors[i].r / max, colors[i].g / max,
-            colors[i].b / max, colors[i].a / max
-        );
+                colors[i].r / max, colors[i].g / max,
+                colors[i].b / max, colors[i].a / max);
     }
     delete[] colors;
     return output;
@@ -471,21 +473,21 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
             }
 
             switch (prim.mode) {
-                case PrimitiveMode_POINTS:
-                    aim->mPrimitiveTypes |= aiPrimitiveType_POINT;
-                    break;
+            case PrimitiveMode_POINTS:
+                aim->mPrimitiveTypes |= aiPrimitiveType_POINT;
+                break;
 
-                case PrimitiveMode_LINES:
-                case PrimitiveMode_LINE_LOOP:
-                case PrimitiveMode_LINE_STRIP:
-                    aim->mPrimitiveTypes |= aiPrimitiveType_LINE;
-                    break;
+            case PrimitiveMode_LINES:
+            case PrimitiveMode_LINE_LOOP:
+            case PrimitiveMode_LINE_STRIP:
+                aim->mPrimitiveTypes |= aiPrimitiveType_LINE;
+                break;
 
-                case PrimitiveMode_TRIANGLES:
-                case PrimitiveMode_TRIANGLE_STRIP:
-                case PrimitiveMode_TRIANGLE_FAN:
-                    aim->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
-                    break;
+            case PrimitiveMode_TRIANGLES:
+            case PrimitiveMode_TRIANGLE_STRIP:
+            case PrimitiveMode_TRIANGLE_FAN:
+                aim->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
+                break;
             }
 
             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) {
                 if (attr.color[c]->count != aim->mNumVertices) {
                     DefaultLogger::get()->warn("Color stream size in mesh \"", mesh.name,
-                                               "\" does not match the vertex count");
+                            "\" does not match the vertex count");
                     continue;
                 }
 
@@ -551,7 +553,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
 
                 if (attr.texcoord[tc]->count != aim->mNumVertices) {
                     DefaultLogger::get()->warn("Texcoord stream size in mesh \"", mesh.name,
-                                               "\" does not match the vertex count");
+                            "\" does not match the vertex count");
                     continue;
                 }
 
@@ -604,7 +606,10 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
                         }
                     }
                     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");
                         } else {
                             Tangent *tangent = nullptr;
@@ -644,77 +649,77 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
                 }
 
                 switch (prim.mode) {
-                    case PrimitiveMode_POINTS: {
-                        nFaces = count;
-                        facePtr = faces = new aiFace[nFaces];
-                        for (unsigned int i = 0; i < count; ++i) {
-                            SetFaceAndAdvance1(facePtr, aim->mNumVertices, data.GetUInt(i));
-                        }
-                        break;
+                case PrimitiveMode_POINTS: {
+                    nFaces = count;
+                    facePtr = faces = new aiFace[nFaces];
+                    for (unsigned int i = 0; i < count; ++i) {
+                        SetFaceAndAdvance1(facePtr, aim->mNumVertices, data.GetUInt(i));
                     }
+                    break;
+                }
 
-                    case PrimitiveMode_LINES: {
-                        nFaces = count / 2;
-                        if (nFaces * 2 != count) {
-                            ASSIMP_LOG_WARN("The number of vertices was not compatible with the LINES mode. Some vertices were dropped.");
-                            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;
+                case PrimitiveMode_LINES: {
+                    nFaces = count / 2;
+                    if (nFaces * 2 != count) {
+                        ASSIMP_LOG_WARN("The number of vertices was not compatible with the LINES mode. Some vertices were dropped.");
+                        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;
+                }
 
-                    case PrimitiveMode_LINE_LOOP:
-                    case PrimitiveMode_LINE_STRIP: {
-                        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;
+                case PrimitiveMode_LINE_LOOP:
+                case PrimitiveMode_LINE_STRIP: {
+                    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;
+                }
 
-                    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) {
+                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));
                         }
-                        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));
-                            }
-                        }
-                        break;
+                    break;
+                }
+                case PrimitiveMode_TRIANGLE_FAN:
+                    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));
                     }
-                    case PrimitiveMode_TRIANGLE_FAN:
-                        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;
+                    break;
                 }
             } else { // no indices provided so directly generate from counts
 
@@ -722,77 +727,77 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
                 unsigned int count = aim->mNumVertices;
 
                 switch (prim.mode) {
-                    case PrimitiveMode_POINTS: {
-                        nFaces = count;
-                        facePtr = faces = new aiFace[nFaces];
-                        for (unsigned int i = 0; i < count; ++i) {
-                            SetFaceAndAdvance1(facePtr, aim->mNumVertices, i);
-                        }
-                        break;
+                case PrimitiveMode_POINTS: {
+                    nFaces = count;
+                    facePtr = faces = new aiFace[nFaces];
+                    for (unsigned int i = 0; i < count; ++i) {
+                        SetFaceAndAdvance1(facePtr, aim->mNumVertices, i);
                     }
+                    break;
+                }
 
-                    case PrimitiveMode_LINES: {
-                        nFaces = count / 2;
-                        if (nFaces * 2 != count) {
-                            ASSIMP_LOG_WARN("The number of vertices was not compatible with the LINES mode. Some vertices were dropped.");
-                            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;
+                case PrimitiveMode_LINES: {
+                    nFaces = count / 2;
+                    if (nFaces * 2 != count) {
+                        ASSIMP_LOG_WARN("The number of vertices was not compatible with the LINES mode. Some vertices were dropped.");
+                        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;
+                }
 
-                    case PrimitiveMode_LINE_LOOP:
-                    case PrimitiveMode_LINE_STRIP: {
-                        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;
+                case PrimitiveMode_LINE_LOOP:
+                case PrimitiveMode_LINE_STRIP: {
+                    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;
+                }
 
-                    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) {
+                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);
                         }
-                        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);
-                            }
-                        }
-                        break;
+                    break;
+                }
+                case PrimitiveMode_TRIANGLE_FAN:
+                    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);
                     }
-                    case PrimitiveMode_TRIANGLE_FAN:
-                        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;
+                    break;
                 }
             }
 
@@ -876,15 +881,15 @@ void glTF2Importer::ImportLights(glTF2::Asset &r) {
         aiLight *ail = mScene->mLights[i] = new aiLight();
 
         switch (light.type) {
-            case Light::Directional:
-                ail->mType = aiLightSource_DIRECTIONAL;
-                break;
-            case Light::Point:
-                ail->mType = aiLightSource_POINT;
-                break;
-            case Light::Spot:
-                ail->mType = aiLightSource_SPOT;
-                break;
+        case Light::Directional:
+            ail->mType = aiLightSource_DIRECTIONAL;
+            break;
+        case Light::Point:
+            ail->mType = aiLightSource_POINT;
+            break;
+        case Light::Spot:
+            ail->mType = aiLightSource_SPOT;
+            break;
         }
 
         if (ail->mType != aiLightSource_POINT) {
@@ -902,14 +907,14 @@ void glTF2Importer::ImportLights(glTF2::Asset &r) {
             ail->mAttenuationLinear = 0.0;
             ail->mAttenuationQuadratic = 0.0;
         } else {
-            //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
-            //this is correct equation for the case when range (see
-            //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.
-            //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
-            //and its up to implementation to read it when it wants to
+            // 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
+            // this is correct equation for the case when range (see
+            // 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.
+            // 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
+            // and its up to implementation to read it when it wants to
             ail->mAttenuationConstant = 0.0;
             ail->mAttenuationLinear = 0.0;
             ail->mAttenuationQuadratic = 1.0;
@@ -926,7 +931,7 @@ static void GetNodeTransform(aiMatrix4x4 &matrix, const glTF2::Node &node) {
     if (node.matrix.isPresent) {
         CopyValue(node.matrix.value, matrix);
         return;
-    } 
+    }
 
     if (node.translation.isPresent) {
         aiVector3D trans;
@@ -1021,7 +1026,7 @@ void ParseExtensions(aiMetadata *metadata, const CustomExtension &extension) {
         metadata->Add(extension.name, extension.mBoolValue.value);
     } else if (extension.mValues.isPresent) {
         aiMetadata val;
-        for (auto const & subExtension : extension.mValues.value) {
+        for (auto const &subExtension : extension.mValues.value) {
             ParseExtensions(&val, subExtension);
         }
         metadata->Add(extension.name, val);
@@ -1030,7 +1035,7 @@ void ParseExtensions(aiMetadata *metadata, const CustomExtension &extension) {
 
 void ParseExtras(aiMetadata *metadata, const CustomExtension &extension) {
     if (extension.mValues.isPresent) {
-        for (auto const & subExtension : extension.mValues.value) {
+        for (auto const &subExtension : extension.mValues.value) {
             ParseExtensions(metadata, subExtension);
         }
     }
@@ -1068,11 +1073,10 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector<unsigned int> &
 
         if (!node.meshes.empty()) {
             // 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(),
-                    " meshes in ", getContextForErrorMessages(node.id, node.name),
-                    ", but only 1 mesh per node allowed.");
+                        " meshes in ", getContextForErrorMessages(node.id, node.name),
+                        ", but only 1 mesh per node allowed.");
             }
             int mesh_idx = node.meshes[0].GetIndex();
             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) {
                 for (int primitiveNo = 0; primitiveNo < count; ++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);
                     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) {
             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
-            //it is added to meta data of parent node, because there is no other place to put it
+            // 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
             if (node.light->range.isPresent) {
                 if (!ainode->mMetaData) {
                     ainode->mMetaData = aiMetadata::Alloc(1);
@@ -1222,7 +1226,7 @@ struct AnimationSamplers {
     Animation::Sampler *weight;
 };
 
-aiNodeAnim *CreateNodeAnim(glTF2::Asset&, Node &node, AnimationSamplers &samplers) {
+aiNodeAnim *CreateNodeAnim(glTF2::Asset &, Node &node, AnimationSamplers &samplers) {
     aiNodeAnim *anim = new aiNodeAnim();
 
     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();
 
     try {
@@ -1366,7 +1370,7 @@ std::unordered_map<unsigned int, AnimationSamplers> GatherSamplers(Animation &an
             continue;
         }
 
-        auto& animsampler = anim.samplers[channel.sampler];
+        auto &animsampler = anim.samplers[channel.sampler];
 
         if (!animsampler.input) {
             ASSIMP_LOG_WARN("Animation ", anim.name, ": Missing sampler input. Skipping.");
@@ -1555,9 +1559,9 @@ void glTF2Importer::ImportEmbeddedTextures(glTF2::Asset &r) {
             if (ext) {
                 if (strcmp(ext, "jpeg") == 0) {
                     ext = "jpg";
-                } else if(strcmp(ext, "ktx2") == 0) { //basisu: ktx remains
+                } else if (strcmp(ext, "ktx2") == 0) { // basisu: ktx remains
                     ext = "kx2";
-                } else if(strcmp(ext, "basis") == 0) { //basisu
+                } else if (strcmp(ext, "basis") == 0) { // basisu
                     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");
     ai_assert(mScene->mMetaData == nullptr);
     const bool hasVersion = !a.asset.version.empty();
@@ -1604,7 +1608,7 @@ void glTF2Importer::InternReadFile(const std::string &pFile, aiScene *pScene, IO
     this->mScene = pScene;
 
     // 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");
     if (asset.scene) {
         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) {
-    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

+ 31 - 15
code/CMakeLists.txt

@@ -78,6 +78,7 @@ SET( PUBLIC_HEADERS
   ${HEADER_PATH}/matrix4x4.h
   ${HEADER_PATH}/matrix4x4.inl
   ${HEADER_PATH}/mesh.h
+  ${HEADER_PATH}/ObjMaterial.h
   ${HEADER_PATH}/pbrmaterial.h
   ${HEADER_PATH}/GltfMaterial.h
   ${HEADER_PATH}/postprocess.h
@@ -149,7 +150,7 @@ SET( Core_SRCS
   Common/Assimp.cpp
 )
 
-IF(MSVC)
+IF(MSVC OR (MINGW AND BUILD_SHARED_LIBS))
   list(APPEND Core_SRCS "res/assimp.rc")
 ENDIF()
 
@@ -166,6 +167,7 @@ SET( Logging_SRCS
 SOURCE_GROUP(Logging FILES ${Logging_SRCS})
 
 SET( Common_SRCS
+  Common/StbCommon.h
   Common/Compression.cpp
   Common/Compression.h
   Common/BaseImporter.cpp
@@ -181,6 +183,7 @@ SET( Common_SRCS
   Common/DefaultIOSystem.cpp
   Common/ZipArchiveIOSystem.cpp
   Common/PolyTools.h
+  Common/Maybe.h
   Common/Importer.cpp
   Common/IFF.h
   Common/SGSpatialSort.cpp
@@ -1052,7 +1055,9 @@ ENDIF()
 # Check dependencies for glTF importer with Open3DGC-compression.
 # 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).
-FIND_PACKAGE(RT QUIET)
+IF (NOT WIN32)
+  FIND_PACKAGE(RT QUIET)
+ENDIF ()
 IF (NOT ASSIMP_HUNTER_ENABLED AND (RT_FOUND OR WIN32))
   SET( ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC 1 )
   ADD_DEFINITIONS( -DASSIMP_IMPORTER_GLTF_USE_OPEN3DGC=1 )
@@ -1095,8 +1100,6 @@ if(MSVC10)
   endif()
 endif()
 
-ADD_DEFINITIONS( -DASSIMP_BUILD_DLL_EXPORT )
-
 IF( MSVC OR "${CMAKE_CXX_SIMULATE_ID}" MATCHES "MSVC") # clang with MSVC ABI
   ADD_DEFINITIONS( -D_SCL_SECURE_NO_WARNINGS )
   ADD_DEFINITIONS( -D_CRT_SECURE_NO_WARNINGS )
@@ -1173,13 +1176,22 @@ ENDIF()
 ADD_LIBRARY( assimp ${assimp_src} )
 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)
 
-# enable warnings as errors ########################################
-IF (MSVC)
-  TARGET_COMPILE_OPTIONS(assimp PRIVATE /WX)
-ELSE()
-  TARGET_COMPILE_OPTIONS(assimp PRIVATE -Werror)
+IF (ASSIMP_WARNINGS_AS_ERRORS)
+  MESSAGE(STATUS "Treating all warnings as errors (for assimp library only)")
+  IF (MSVC)
+    TARGET_COMPILE_OPTIONS(assimp PRIVATE /W4 /WX)
+  ELSE()
+    TARGET_COMPILE_OPTIONS(assimp PRIVATE -Wall -Werror)
+  ENDIF()
 ENDIF()
 
 # 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")
 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")
     target_compile_definitions(assimp PUBLIC WindowsStore)
     TARGET_LINK_LIBRARIES(assimp advapi32)
@@ -1273,12 +1295,6 @@ SET_TARGET_PROPERTIES( assimp PROPERTIES
   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 (ASSIMP_BUILD_FRAMEWORK)
     SET_TARGET_PROPERTIES( assimp PROPERTIES

+ 6 - 13
code/Common/Assimp.cpp

@@ -5,8 +5,6 @@ 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,
@@ -1282,18 +1280,13 @@ ASSIMP_API void aiQuaternionInterpolate(
 #   define STBI_ONLY_PNG
 #endif
 
+// Ensure all symbols are linked correctly
 #if ASSIMP_NEEDS_STB_IMAGE
-
-#   if _MSC_VER // "unreferenced function has been removed" (SSE2 detection routine in x64 builds)
-#       pragma warning(push)
-#       pragma warning(disable: 4505)
+    // Share stb_image's PNG loader with other importers/exporters instead of bringing our own copy.
+#   define STBI_ONLY_PNG
+#   ifdef ASSIMP_USE_STB_IMAGE_STATIC
+#       define STB_IMAGE_STATIC
 #   endif
-
 #   define STB_IMAGE_IMPLEMENTATION
-#   include "stb/stb_image.h"
-
-#   if _MSC_VER
-#       pragma warning(pop)
-#   endif
-
+#   include "Common/StbCommon.h"
 #endif

+ 2 - 2
code/Common/DefaultIOStream.cpp

@@ -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 <>
 inline size_t select_ftell<8>(FILE *file) {
     return (size_t)::_ftelli64(file);
@@ -149,7 +149,7 @@ size_t DefaultIOStream::FileSize() const {
         //
         // 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
-#if defined _WIN32 && (!defined __GNUC__ || __MSVCRT_VERSION__ >= 0x0601)
+#if defined _WIN64 && (!defined __GNUC__ || __MSVCRT_VERSION__ >= 0x0601)
         struct __stat64 fileStat;
         //using fileno + fstat avoids having to handle the filename
         int err = _fstat64(_fileno(mFile), &fileStat);

+ 2 - 2
code/Common/FileSystemFilter.h

@@ -300,10 +300,10 @@ private:
 
         const char separator = getOsSeparator();
         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.
             // 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;
                 continue;
             }

+ 29 - 0
code/Common/IFF.h

@@ -35,6 +35,17 @@ struct SubChunkHeader
     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) | \
     ((uint8_t)b << 16u) | ((uint8_t)c << 8u) | ((uint8_t)d)))
@@ -77,6 +88,24 @@ inline SubChunkHeader LoadSubChunk(uint8_t*& outFile)
     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
 //! @param outFile Pointer to the file data. The buffer must at

+ 29 - 0
code/Common/Maybe.h

@@ -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;
+};

+ 58 - 0
code/Common/StbCommon.h

@@ -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
+

+ 3 - 3
code/Common/ZipArchiveIOSystem.cpp

@@ -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) {
     ZipFile *io_stream = (ZipFile *)stream;
     voidpf ret = NULL;
-    size_t i;
+    int i;
 
     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);
-    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] != '.')
             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;
     }
 

+ 1 - 1
code/Common/material.cpp

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

+ 2 - 2
code/Pbrt/PbrtExporter.cpp

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

+ 2 - 0
code/PostProcessing/ArmaturePopulate.cpp

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

+ 1 - 1
code/PostProcessing/ComputeUVMappingProcess.cpp

@@ -415,7 +415,7 @@ void ComputeUVMappingProcess::Execute( aiScene* pScene)
                     if (!DefaultLogger::isNullLogger())
                     {
                         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));
 
                         ASSIMP_LOG_INFO(buffer);

+ 75 - 93
code/PostProcessing/JoinVerticesProcess.cpp

@@ -5,8 +5,6 @@ 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,
@@ -45,41 +43,38 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  * for all imported meshes
  */
 
-
 #ifndef ASSIMP_BUILD_NO_JOINVERTICES_PROCESS
 
 #include "JoinVerticesProcess.h"
 #include "ProcessHelper.h"
 #include <assimp/Vertex.h>
 #include <assimp/TinyFormatter.h>
+
 #include <stdio.h>
 #include <unordered_set>
+#include <unordered_map>
 
 using namespace Assimp;
 // ------------------------------------------------------------------------------------------------
 // Constructor to be privately used by Importer
-JoinVerticesProcess::JoinVerticesProcess()
-{
+JoinVerticesProcess::JoinVerticesProcess() {
     // nothing to do here
 }
 
 // ------------------------------------------------------------------------------------------------
 // Destructor, private as well
-JoinVerticesProcess::~JoinVerticesProcess()
-{
+JoinVerticesProcess::~JoinVerticesProcess() {
     // nothing to do here
 }
 
 // ------------------------------------------------------------------------------------------------
 // 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;
 }
 // ------------------------------------------------------------------------------------------------
 // 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");
 
     // get the total number of vertices BEFORE the step is executed
@@ -92,27 +87,29 @@ void JoinVerticesProcess::Execute( aiScene* pScene)
 
     // execute the step
     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);
+    }
+
+    pScene->mFlags |= AI_SCENE_FLAGS_NON_VERBOSE_FORMAT;
 
     // if logging is active, print detailed statistics
     if (!DefaultLogger::isNullLogger()) {
         if (iNumOldVertices == iNumVertices) {
             ASSIMP_LOG_DEBUG("JoinVerticesProcess finished ");
-        } else {
-            ASSIMP_LOG_INFO("JoinVerticesProcess finished | Verts in: ", iNumOldVertices,
-                " out: ", iNumVertices, " | ~",
-                ((iNumOldVertices - iNumVertices) / (float)iNumOldVertices) * 100.f );
+            return;
         }
+        
+        // Show statistics
+        ASSIMP_LOG_INFO("JoinVerticesProcess finished | Verts in: ", iNumOldVertices,
+            " out: ", iNumVertices, " | ~",
+            ((iNumOldVertices - iNumVertices) / (float)iNumOldVertices) * 100.f );
     }
-
-    pScene->mFlags |= AI_SCENE_FLAGS_NON_VERBOSE_FORMAT;
 }
 
 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.
     // Try to reuse the lookup table from the last step.
     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)
-    if (pMesh->mVertices)
-    {
+    if (pMesh->mVertices) {
         delete [] pMesh->mVertices;
         pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
         for (unsigned int a = 0; a < pMesh->mNumVertices; a++) {
@@ -181,8 +177,7 @@ void updateXMeshVertices(XMesh *pMesh, std::vector<Vertex> &uniqueVertices) {
     }
 
     // Normals, if present
-    if (pMesh->mNormals)
-    {
+    if (pMesh->mNormals) {
         delete [] pMesh->mNormals;
         pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
         for( unsigned int a = 0; a < pMesh->mNumVertices; a++) {
@@ -190,8 +185,7 @@ void updateXMeshVertices(XMesh *pMesh, std::vector<Vertex> &uniqueVertices) {
         }
     }
     // Tangents, if present
-    if (pMesh->mTangents)
-    {
+    if (pMesh->mTangents) {
         delete [] pMesh->mTangents;
         pMesh->mTangents = new aiVector3D[pMesh->mNumVertices];
         for (unsigned int a = 0; a < pMesh->mNumVertices; a++) {
@@ -199,8 +193,7 @@ void updateXMeshVertices(XMesh *pMesh, std::vector<Vertex> &uniqueVertices) {
         }
     }
     // Bitangents as well
-    if (pMesh->mBitangents)
-    {
+    if (pMesh->mBitangents) {
         delete [] pMesh->mBitangents;
         pMesh->mBitangents = new aiVector3D[pMesh->mNumVertices];
         for (unsigned int a = 0; a < pMesh->mNumVertices; a++) {
@@ -208,8 +201,7 @@ void updateXMeshVertices(XMesh *pMesh, std::vector<Vertex> &uniqueVertices) {
         }
     }
     // Vertex colors
-    for (unsigned int a = 0; pMesh->HasVertexColors(a); a++)
-    {
+    for (unsigned int a = 0; pMesh->HasVertexColors(a); a++) {
         delete [] pMesh->mColors[a];
         pMesh->mColors[a] = new aiColor4D[pMesh->mNumVertices];
         for( unsigned int b = 0; b < pMesh->mNumVertices; b++) {
@@ -217,8 +209,7 @@ void updateXMeshVertices(XMesh *pMesh, std::vector<Vertex> &uniqueVertices) {
         }
     }
     // Texture coords
-    for (unsigned int a = 0; pMesh->HasTextureCoords(a); a++)
-    {
+    for (unsigned int a = 0; pMesh->HasTextureCoords(a); a++) {
         delete [] pMesh->mTextureCoords[a];
         pMesh->mTextureCoords[a] = new aiVector3D[pMesh->mNumVertices];
         for (unsigned int b = 0; b < pMesh->mNumVertices; b++) {
@@ -226,12 +217,40 @@ void updateXMeshVertices(XMesh *pMesh, std::vector<Vertex> &uniqueVertices) {
         }
     }
 }
+
 } // namespace
 
 // ------------------------------------------------------------------------------------------------
 // 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_TEXTURECOORDS == 8, "AI_MAX_NUMBER_OF_TEXTURECOORDS == 8");
 
@@ -245,8 +264,7 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
     // multiple meshes)
     std::unordered_set<unsigned int> usedVertexIndices;
     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];
         for( unsigned int b = 0; b < face.mNumIndices; 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.
     // 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;
 
     // We'll never have more vertices afterwards.
@@ -303,72 +320,38 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
             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
+    int newIndex = 0;
     for( unsigned int a = 0; a < pMesh->mNumVertices; a++)  {
+        // if the vertex is unused Do nothing
         if (usedVertexIndices.find(a) == usedVertexIndices.end()) {
             continue;
         }
-
         // collect the vertex data
         Vertex v(pMesh,a);
-
-        // collect all vertices that are close enough to the given position
-        vertexFinder->FindIdenticalPositions( v.position, verticesFound);
-        unsigned int matchIndex = 0xffffffff;
-
-        // check all unique vertices close to the position if this vertex is already present among them
-        for( unsigned int b = 0; b < verticesFound.size(); b++) {
-            const unsigned int vidx = verticesFound[b];
-            const unsigned int uidx = replaceIndex[ vidx];
-            if( uidx & 0x80000000)
-                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);
+        // is the vertex already in the map?
+        auto it = vertex2Index.find(v);
+        // if the vertex is not in the map then it is a new vertex add it.
+        if (it == vertex2Index.end()) {
+            // this is a new vertex give it a new index
+            vertex2Index[v] = newIndex;
+            //keep track of its index and increment 1
+            replaceIndex[a] = newIndex++;
+            // add the vertex to the unique vertices
+            uniqueVertices.push_back(v);
             if (hasAnimMeshes) {
                 for (unsigned int animMeshIndex = 0; animMeshIndex < pMesh->mNumAnimMeshes; animMeshIndex++) {
                     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
-    for( unsigned int a = 0; a < pMesh->mNumFaces; a++)
-    {
+    for( unsigned int a = 0; a < pMesh->mNumFaces; a++) {
         aiFace& face = pMesh->mFaces[a];
         for( unsigned int b = 0; b < face.mNumIndices; b++) {
             face.mIndices[b] = replaceIndex[face.mIndices[b]] & ~0x80000000;

+ 1 - 1
code/PostProcessing/ValidateDataStructure.cpp

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

+ 12 - 55
code/res/assimp.rc

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

+ 0 - 14
code/res/resource.h

@@ -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

+ 1 - 1
contrib/draco/CMakeLists.txt

@@ -10,7 +10,7 @@ endif()
 
 set(draco_root "${CMAKE_CURRENT_SOURCE_DIR}")
 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}")
   message(

+ 120 - 83
contrib/gtest/CMakeLists.txt

@@ -1,14 +1,13 @@
 ########################################################################
+# Note: CMake support is community-based. The maintainers do not use CMake
+# internally.
+#
 # CMake build script for Google Test.
 #
 # 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'.
 # 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,
 # Google Test also has to use one.
 option(
@@ -44,13 +43,45 @@ endif()
 # as ${gtest_SOURCE_DIR} and to the root binary directory as
 # ${gtest_BINARY_DIR}.
 # 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)
   set_up_hermetic_build()
 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)
   set(CMAKE_CXX_VISIBILITY_PRESET hidden)
   set(CMAKE_VISIBILITY_INLINES_HIDDEN 1)
@@ -61,24 +92,34 @@ include(cmake/internal_utils.cmake)
 
 config_compiler_and_linker()  # Defined in internal_utils.cmake.
 
-# Where Google Test's .h files can be found.
-include_directories(
-  ${gtest_SOURCE_DIR}/include
-  ${gtest_SOURCE_DIR})
-
-# Where Google Test's libraries can be found.
-link_directories(${gtest_BINARY_DIR}/src)
-
-# Summary of tuple support for Microsoft Visual Studio:
-# Compiler    version(MS)  version(cmake)  Support
-# ----------  -----------  --------------  -----------------------------
-# <= VS 2010  <= 10        <= 1600         Use Google Tests's own tuple.
-# VS 2012     11           1700            std::tr1::tuple + _VARIADIC_MAX=10
-# VS 2013     12           1800            std::tr1::tuple
-if (MSVC AND MSVC_VERSION EQUAL 1700)
-  add_definitions(/D _VARIADIC_MAX=10)
+# Needed to set the namespace for both the export targets and the
+# alias libraries
+set(cmake_package_name GTest CACHE INTERNAL "")
+
+# Create the CMake package file descriptors.
+if (INSTALL_GTEST)
+  include(CMakePackageConfigHelpers)
+  set(targets_export_name ${cmake_package_name}Targets CACHE INTERNAL "")
+  set(generated_dir "${CMAKE_CURRENT_BINARY_DIR}/generated" CACHE INTERNAL "")
+  set(cmake_files_install_dir "${CMAKE_INSTALL_LIBDIR}/cmake/${cmake_package_name}")
+  set(version_file "${generated_dir}/${cmake_package_name}ConfigVersion.cmake")
+  write_basic_package_version_file(${version_file} VERSION ${GOOGLETEST_VERSION} COMPATIBILITY AnyNewerVersion)
+  install(EXPORT ${targets_export_name}
+    NAMESPACE ${cmake_package_name}::
+    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()
 
+# 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
@@ -88,24 +129,26 @@ endif()
 # are used for other targets, to ensure that gtest can be compiled by a user
 # aggressive about warnings.
 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)
-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
 # to the targets for when we are part of a parent build (ie being pulled
 # in via add_subdirectory() rather than being a standalone build).
 if (DEFINED CMAKE_VERSION AND NOT "${CMAKE_VERSION}" VERSION_LESS "2.8.11")
-  target_include_directories(gtest      INTERFACE "${gtest_SOURCE_DIR}/include")
-  target_include_directories(gtest_main INTERFACE "${gtest_SOURCE_DIR}/include")
+  target_include_directories(gtest SYSTEM INTERFACE
+    "$<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()
+target_link_libraries(gtest_main PUBLIC gtest)
 
 ########################################################################
 #
 # Install rules
-install(TARGETS gtest gtest_main
-  DESTINATION lib)
-install(DIRECTORY ${gtest_SOURCE_DIR}/include/gtest
-  DESTINATION include)
+install_project(gtest gtest_main)
 
 ########################################################################
 #
@@ -147,33 +190,34 @@ if (gtest_build_tests)
   ############################################################
   # 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-filepath_test gtest_main)
-  cxx_test(gtest-linked_ptr_test gtest_main)
-  cxx_test(gtest-listener_test gtest_main)
+  cxx_test(googletest-filepath-test gtest_main)
+  cxx_test(googletest-listener-test 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-options_test gtest_main)
-  cxx_test(gtest-param-test_test gtest
-    test/gtest-param-test2_test.cc)
-  cxx_test(gtest-port_test gtest_main)
+  cxx_test(googletest-options-test gtest_main)
+  cxx_test(googletest-param-test-test gtest
+    test/googletest-param-test2-test.cc)
+  cxx_test(googletest-port-test gtest_main)
   cxx_test(gtest_pred_impl_unittest gtest_main)
   cxx_test(gtest_premature_exit_test gtest
     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
     test/production.cc)
   cxx_test(gtest_repeat_test gtest)
   cxx_test(gtest_sole_header_test gtest_main)
   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-typed-test_test gtest_main
     test/gtest-typed-test2_test.cc)
   cxx_test(gtest_unittest gtest_main)
   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.
@@ -190,10 +234,10 @@ if (gtest_build_tests)
 
   cxx_test_with_flags(gtest-death-test_ex_nocatch_test
     "${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_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}"
     gtest_main_no_rtti test/gtest_unittest.cc)
@@ -207,80 +251,73 @@ if (gtest_build_tests)
                         PROPERTIES
                         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.
 
-  cxx_executable(gtest_break_on_failure_unittest_ test gtest)
-  py_test(gtest_break_on_failure_unittest)
+  cxx_executable(googletest-break-on-failure-unittest_ test gtest)
+  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.
   if (NOT MSVC OR MSVC_VERSION GREATER 1310)  # 1310 is Visual Studio .NET 2003
     cxx_executable_with_flags(
-      gtest_catch_exceptions_no_ex_test_
+      googletest-catch-exceptions-no-ex-test_
       "${cxx_no_exception}"
       gtest_main_no_exception
-      test/gtest_catch_exceptions_test_.cc)
+      test/googletest-catch-exceptions-test_.cc)
   endif()
 
   cxx_executable_with_flags(
-    gtest_catch_exceptions_ex_test_
+    googletest-catch-exceptions-ex-test_
     "${cxx_exception}"
     gtest_main
-    test/gtest_catch_exceptions_test_.cc)
-  py_test(gtest_catch_exceptions_test)
+    test/googletest-catch-exceptions-test_.cc)
+  py_test(googletest-catch-exceptions-test)
 
-  cxx_executable(gtest_color_test_ test gtest)
-  py_test(gtest_color_test)
+  cxx_executable(googletest-color-test_ test gtest)
+  py_test(googletest-color-test)
 
-  cxx_executable(gtest_env_var_test_ test gtest)
-  py_test(gtest_env_var_test)
+  cxx_executable(googletest-env-var-test_ test gtest)
+  py_test(googletest-env-var-test)
 
-  cxx_executable(gtest_filter_unittest_ test gtest)
-  py_test(gtest_filter_unittest)
+  cxx_executable(googletest-filter-unittest_ test gtest)
+  py_test(googletest-filter-unittest)
 
   cxx_executable(gtest_help_test_ test gtest_main)
   py_test(gtest_help_test)
 
-  cxx_executable(gtest_list_tests_unittest_ test gtest)
-  py_test(gtest_list_tests_unittest)
+  cxx_executable(googletest-list-tests-unittest_ test gtest)
+  py_test(googletest-list-tests-unittest)
 
-  cxx_executable(gtest_output_test_ test gtest)
-  py_test(gtest_output_test)
+  cxx_executable(googletest-output-test_ test gtest)
+  py_test(googletest-output-test --no_stacktrace_support)
 
-  cxx_executable(gtest_shuffle_test_ test gtest)
-  py_test(gtest_shuffle_test)
+  cxx_executable(googletest-shuffle-test_ test gtest)
+  py_test(googletest-shuffle-test)
 
   # MSVC 7.1 does not support STL with exceptions disabled.
   if (NOT MSVC OR MSVC_VERSION GREATER 1310)
-    cxx_executable(gtest_throw_on_failure_test_ test gtest_no_exception)
-    set_target_properties(gtest_throw_on_failure_test_
+    cxx_executable(googletest-throw-on-failure-test_ test gtest_no_exception)
+    set_target_properties(googletest-throw-on-failure-test_
       PROPERTIES
       COMPILE_FLAGS "${cxx_no_exception}")
-    py_test(gtest_throw_on_failure_test)
+    py_test(googletest-throw-on-failure-test)
   endif()
 
-  cxx_executable(gtest_uninitialized_test_ test gtest)
-  py_test(gtest_uninitialized_test)
+  cxx_executable(googletest-uninitialized-test_ test gtest)
+  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_outfile2_test_ test gtest_main)
   py_test(gtest_xml_outfiles_test)
+  py_test(googletest-json-outfiles-test)
 
   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()

+ 164 - 229
contrib/gtest/README.md

@@ -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 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 with CMake
 
-#### 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,
-create a library build target (or a project as called by Visual Studio
-and Xcode) to compile
+CMake works by generating native makefiles or build projects that can be used in
+the compiler environment of your choice. You can either build GoogleTest as a
+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}`
-in the normal header search path.  Assuming a Linux-like system and gcc,
-something like the following will do:
+When building GoogleTest as a standalone project, the typical workflow starts
+with
 
-    g++ -isystem ${GTEST_DIR}/include -I${GTEST_DIR} \
-        -pthread -c ${GTEST_DIR}/src/gtest-all.cc
-    ar -rv libgtest.a gtest-all.o
+```
+git clone https://github.com/google/googletest.git -b release-1.10.0
+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
-with gtest and any other necessary libraries:
+```
+cmake .. -DBUILD_GMOCK=OFF
+```
 
-    g++ -isystem ${GTEST_DIR}/include -pthread path/to/your_test.cc libgtest.a \
-        -o your_test
+If you are on a \*nix system, you should now see a Makefile in the current
+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
-(e.g. Linux, Mac OS X, and Cygwin).  It doesn't try to build Google
-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.
+```
+make
+sudo make install    # Install in /usr/local/ by default
+```
 
-If the default settings are correct for your environment, the
-following commands should succeed:
-
-    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.
+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.
 
-### Legacy Build Scripts ###
-
-Before settling on CMake, we have been providing hand-maintained build
-projects/scripts for Visual Studio, Xcode, and Autotools.  While we
-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:
-
-The msvc\ folder contains two solutions with Visual C++ projects.
-Open the `gtest.sln` or `gtest-md.sln` file using Visual Studio, and you
-are ready to build Google Test the same way you build any Visual
-Studio project.  Files that have names ending with -md use DLL
-versions of Microsoft runtime libraries (the /MD or the /MDd compiler
-option).  Files without that suffix use static versions of the runtime
-libraries (the /MT or the /MTd option).  Please note that one must use
-the same option to compile both gtest and the test code.  If you use
-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
-Xcode.  Build the "gtest" target.  The universal binary framework will
-end up in your selected build directory (selected in the Xcode
-"Preferences..." -> "Building" pane and defaults to xcode/build).
-Alternatively, at the command line, enter:
-
-    xcodebuild
-
-This will build the "Release" configuration of gtest.framework in your
-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
-above, you need to either:
-
- * update the SDK configuration options in xcode/Config/General.xconfig.
-   Comment options `SDKROOT`, `MACOS_DEPLOYMENT_TARGET`, and `GCC_VERSION`. If
-   you choose this route you lose the ability to target earlier versions
-   of MacOS X.
- * 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 ###
-
-Google Test can be used in diverse environments.  The default
-configuration may not work (or may not work well) out of the box in
-some environments.  However, you can easily tweak Google 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,
-see file [include/gtest/internal/gtest-port.h](include/gtest/internal/gtest-port.h).
-
-### Choosing a TR1 Tuple Library ###
-
-Some Google Test features require the C++ Technical Report 1 (TR1)
-tuple library, which is not yet available with all compilers.  The
-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
-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
-
-to the compiler flags while compiling Google Test and your tests.  If
-you want to force Google Test to use its own tuple library, just add
-
-    -DGTEST_USE_OWN_TR1_TUPLE=1
-
-to the compiler flags instead.
-
-If you don't want Google Test to use tuple at all, add
-
-    -DGTEST_HAS_TR1_TUPLE=0
-
-and all features using tuple will be disabled.
-
-### 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
+#### Incorporating Into An Existing CMake Project
+
+If you want to use GoogleTest in a project which already uses CMake, the easiest
+way is to get installed libraries and headers.
+
+*   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`.
+
+And a more robust and flexible approach is to build GoogleTest as part of that
+project directly. This is done by making the GoogleTest source code available to
+the main build and adding it using CMake's `add_subdirectory()` command. This
+has the significant advantage that the same compiler and linker settings are
+used between GoogleTest and the rest of your project, so issues associated with
+using incompatible libraries (eg debug/release), etc. are avoided. This is
+particularly useful on Windows. Making GoogleTest's source code available to the
+main build can be done a few different ways:
+
+*   Download the GoogleTest source code manually and place it at a known
+    location. This is the least flexible approach and can make it more difficult
+    to use with continuous integration systems, etc.
+*   Embed the GoogleTest source code as a direct copy in the main project's
+    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.
+
+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.
+
+Just add to your `CMakeLists.txt`:
+
+```cmake
+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)
+
+# Now simply link against gtest or gtest_main as needed. Eg
+add_executable(example example.cpp)
+target_link_libraries(example gtest_main)
+add_test(NAME example_test COMMAND example)
+```
+
+Note that this approach requires CMake 3.14 or later due to its use of the
+`FetchContent_MakeAvailable()` command.
+
+##### Visual Studio Dynamic vs Static Runtimes
+
+By default, new Visual Studio projects link the C runtimes dynamically but
+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
+
+GoogleTest already has a CMake option for this: `gtest_force_shared_crt`
+
+Enabling this option will make gtest link the runtimes dynamically too, and
+match the project in which it is included.
+
+#### C++ Standard Version
+
+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.
+
+### Tweaking GoogleTest
+
+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.
+
+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).
+
+### Multi-threaded Tests
+
+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.).
+
+If GoogleTest doesn't correctly detect whether pthread is available in your
+environment, you can force it with
 
     -DGTEST_HAS_PTHREAD=1
 
@@ -183,26 +158,24 @@ or
 
     -DGTEST_HAS_PTHREAD=0
 
-When Google Test uses pthread, you may need to add flags to your
-compiler and/or linker to select the pthread library, or you'll get
-link errors.  If you use the CMake script or the deprecated Autotools
-script, this is taken care of for you.  If you use your own build
-script, you'll need to read your compiler and linker's manual to
-figure out what flags to add.
+When GoogleTest uses pthread, you may need to add flags to your compiler and/or
+linker to select the pthread library, or you'll get link errors. If you use the
+CMake script, this is taken care of for you. If you use your own build script,
+you'll need to read your compiler and linker's manual to 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
-static library for the simplicity.  You can choose to use Google Test
-as a shared library (known as a DLL on Windows) if you prefer.
+GoogleTest is compact, so most users can build and link it as a static library
+for the simplicity. You can choose to use GoogleTest as a shared library (known
+as a DLL on Windows) if you prefer.
 
 To compile *gtest* as a shared library, add
 
     -DGTEST_CREATE_SHARED_LIBRARY=1
 
-to the compiler flags.  You'll also need to tell the linker to produce
-a shared library instead - consult your linker's manual for how to do
-it.
+to the compiler flags. You'll also need to tell the linker to produce a shared
+library instead - consult your linker's manual for how to do it.
 
 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.
 
-Note: while the above steps aren't technically necessary today when
-using some compilers (e.g. GCC), they may become necessary in the
-future, if we decide to improve the speed of loading the library (see
-<http://gcc.gnu.org/wiki/Visibility> for details).  Therefore you are
-recommended to always add the above flags when using Google Test as a
-shared library.  Otherwise a future release of Google Test may break
-your build script.
+Note: while the above steps aren't technically necessary today when using some
+compilers (e.g. GCC), they may become necessary in the future, if we decide to
+improve the speed of loading the library (see
+<http://gcc.gnu.org/wiki/Visibility> for details). Therefore you are recommended
+to always add the above flags when using GoogleTest as a shared library.
+Otherwise a future release of GoogleTest may break your build script.
 
-### Avoiding Macro Name Clashes ###
+### Avoiding Macro Name Clashes
 
-In C++, macros don't obey namespaces.  Therefore two libraries that
-both define a macro of the same name will clash if you `#include` both
-definitions.  In case a Google Test macro clashes with another
-library, you can force Google Test to rename its macro to avoid the
-conflict.
+In C++, macros don't obey namespaces. Therefore two libraries that both define a
+macro of the same name will clash if you `#include` both definitions. In case a
+GoogleTest macro clashes with another library, you can force GoogleTest to
+rename its macro to avoid the conflict.
 
-Specifically, if both Google Test and some other code define macro
-FOO, you can add
+Specifically, if both GoogleTest and some other code define macro FOO, you can
+add
 
     -DGTEST_DONT_DEFINE_FOO=1
 
-to the compiler flags to tell Google Test to change the macro's name
-from `FOO` to `GTEST_FOO`.  Currently `FOO` can be `FAIL`, `SUCCEED`,
-or `TEST`.  For example, with `-DGTEST_DONT_DEFINE_TEST=1`, you'll
-need to write
+to the compiler flags to tell GoogleTest to change the macro's name from `FOO`
+to `GTEST_FOO`. Currently `FOO` can be `FAIL`, `SUCCEED`, or `TEST`. For
+example, with `-DGTEST_DONT_DEFINE_TEST=1`, you'll need to write
 
     GTEST_TEST(SomeTest, DoesThis) { ... }
 
@@ -243,38 +213,3 @@ instead of
     TEST(SomeTest, DoesThis) { ... }
 
 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.

+ 9 - 0
contrib/gtest/cmake/Config.cmake.in

@@ -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}/@[email protected]")
+check_required_components("@project_name@")

+ 9 - 0
contrib/gtest/cmake/gtest.pc.in

@@ -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@

+ 10 - 0
contrib/gtest/cmake/gtest_main.pc.in

@@ -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@

+ 149 - 59
contrib/gtest/cmake/internal_utils.cmake

@@ -12,6 +12,10 @@
 #   Test and Google Mock's option() definitions, and thus must be
 #   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.
 #
 # This must be a macro(), as inside a function string() can only
@@ -20,8 +24,10 @@ macro(fix_default_compiler_settings_)
   if (MSVC)
     # For MSVC, CMake sets certain flags to defaults we want to override.
     # 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
+             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_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
       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.
       # Replaces /W3 with /W4 in defaults.
       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()
   endif()
 endmacro()
@@ -46,52 +57,43 @@ endmacro()
 # Google Mock.  You can tweak these definitions to suit your need.  A
 # variable's value is empty before it's explicitly assigned to.
 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.
     find_package(Threads)
+    if (CMAKE_USE_PTHREADS_INIT)
+      set(GTEST_HAS_PTHREAD ON)
+    endif()
   endif()
 
   fix_default_compiler_settings_()
   if (MSVC)
     # Newlines inside flags variables break CMake's NMake generator.
     # TODO([email protected]): Add -RTCs and -RTCu to debug builds.
-    set(cxx_base_flags "-GS -W4 -WX -wd4251 -wd4275 -nologo -J -Zi")
-    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 "-GS -W4 -WX -wd4251 -wd4275 -nologo -J")
     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_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-")
+    # 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)
-    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_no_exception_flags "-fno-exceptions")
     # 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 "")
   endif()
 
-  if (CMAKE_USE_PTHREADS_INIT)  # The pthreads library is available and allowed.
-    set(cxx_base_flags "${cxx_base_flags} -DGTEST_HAS_PTHREAD=1")
+  # The pthreads library is available and allowed?
+  if (DEFINED GTEST_HAS_PTHREAD)
+    set(GTEST_HAS_PTHREAD_MACRO "-DGTEST_HAS_PTHREAD=1")
   else()
-    set(cxx_base_flags "${cxx_base_flags} -DGTEST_HAS_PTHREAD=0")
+    set(GTEST_HAS_PTHREAD_MACRO "-DGTEST_HAS_PTHREAD=0")
   endif()
+  set(cxx_base_flags "${cxx_base_flags} ${GTEST_HAS_PTHREAD_MACRO}")
 
   # 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
     "${CMAKE_CXX_FLAGS} ${cxx_base_flags} ${cxx_no_exception_flags}")
   set(cxx_default "${cxx_exception}")
   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.
   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.
   # ARGN refers to additional arguments after 'cxx_flags'.
   add_library(${name} ${type} ${ARGN})
+  add_library(${cmake_package_name}::${name} ALIAS ${name})
   set_target_properties(${name}
     PROPERTIES
     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")
     set_target_properties(${name}
       PROPERTIES
       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()
-  if (CMAKE_USE_PTHREADS_INIT)
-    target_link_libraries(${name} ${CMAKE_THREAD_LIBS_INIT})
+  if (DEFINED GTEST_HAS_PTHREAD)
+    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()
 endfunction()
 
@@ -178,6 +215,10 @@ endfunction()
 # is built from the given source files with the given compiler flags.
 function(cxx_executable_with_flags name cxx_flags libs)
   add_executable(${name} ${ARGN})
+  if (MSVC)
+    # BigObj required for tests.
+    set(cxx_flags "${cxx_flags} -bigobj")
+  endif()
   if (cxx_flags)
     set_target_properties(${name}
       PROPERTIES
@@ -206,7 +247,13 @@ function(cxx_executable name dir libs)
 endfunction()
 
 # 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...)
 #
@@ -214,7 +261,7 @@ find_package(PythonInterp)
 # from the given source files with the given compiler flags.
 function(cxx_test_with_flags name cxx_flags libs)
   cxx_executable_with_flags(${name} "${cxx_flags}" "${libs}" ${ARGN})
-  add_test(${name} ${name})
+    add_test(NAME ${name} COMMAND "$<TARGET_FILE:${name}>")
 endfunction()
 
 # cxx_test(name libs srcs...)
@@ -232,23 +279,66 @@ endfunction()
 # creates a Python test with the given name whose main module is in
 # test/name.py.  It does nothing if Python is not installed.
 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)
-    # ${CMAKE_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.
-    if (${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} GREATER 3.1)
-      add_test(
-        NAME ${name}
+    if ("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" VERSION_GREATER 3.1)
+      if (CMAKE_CONFIGURATION_TYPES)
+        # Multi-configuration build generators as for Visual Studio save
+        # output in a subdirectory of CMAKE_CURRENT_BINARY_DIR (Debug,
+        # Release etc.), so we have to provide it here.
+        add_test(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
-            --build_dir=${CMAKE_CURRENT_BINARY_DIR}/$<CONFIGURATION>)
-    else (${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} GREATER 3.1)
-      add_test(
-        ${name}
-        ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test/${name}.py
-          --build_dir=${CMAKE_CURRENT_BINARY_DIR}/\${CTEST_CONFIGURATION_TYPE})
-    endif (${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} GREATER 3.1)
+          --build_dir=${CMAKE_CURRENT_BINARY_DIR}/\${CTEST_CONFIGURATION_TYPE} ${ARGN})
+    endif()
+  endif(PYTHONINTERP_FOUND)
+endfunction()
+
+# install_project(targets...)
+#
+# 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()
 endfunction()

+ 21 - 0
contrib/gtest/cmake/libgtest.la.in

@@ -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@'

+ 4 - 0
contrib/gtest/docs/README.md

@@ -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.

+ 84 - 32
contrib/gtest/include/gtest/gtest-death-test.h

@@ -26,17 +26,17 @@
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
 //
-// Author: [email protected] (Zhanyong Wan)
-//
-// The Google C++ Testing Framework (Google Test)
+// The Google C++ Testing and Mocking Framework (Google Test)
 //
 // 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
 // directly.
+// GOOGLETEST_CM0001 DO NOT DELETE
 
-#ifndef GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_
-#define GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_
+#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_
+#define GOOGLETEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_
 
 #include "gtest/internal/gtest-death-test-internal.h"
 
@@ -97,12 +97,17 @@ GTEST_API_ bool InDeathTestChild();
 //
 //   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:
 //
+//   GOOGLETEST_CM0005 DO NOT DELETE
 //   On POSIX-compliant systems (*nix), we use the <regex.h> library,
 //   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
 //   implementation should be enough most of the time when writing
 //   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
 //   directory in PATH.
 //
-// TODO([email protected]): make thread-safe death tests search the PATH.
 
-// Asserts that a given statement causes the program to exit, with an
-// integer exit status that satisfies predicate, and emitting error output
-// that matches regex.
-# define ASSERT_EXIT(statement, predicate, regex) \
-    GTEST_DEATH_TEST_(statement, predicate, regex, GTEST_FATAL_FAILURE_)
+// Asserts that a given `statement` causes the program to exit, with an
+// integer exit status that satisfies `predicate`, and emitting error output
+// that matches `matcher`.
+# define ASSERT_EXIT(statement, predicate, matcher) \
+    GTEST_DEATH_TEST_(statement, predicate, matcher, GTEST_FATAL_FAILURE_)
 
-// Like ASSERT_EXIT, but continues on to successive tests in the
-// test case, if any:
-# define EXPECT_EXIT(statement, predicate, regex) \
-    GTEST_DEATH_TEST_(statement, predicate, regex, GTEST_NONFATAL_FAILURE_)
+// Like `ASSERT_EXIT`, but continues on to successive tests in the
+// test suite, if any:
+# define EXPECT_EXIT(statement, predicate, matcher) \
+    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
-// signal, and emitting error output that matches regex.
-# define ASSERT_DEATH(statement, regex) \
-    ASSERT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, regex)
+// signal, and emitting error output that matches `matcher`.
+# define ASSERT_DEATH(statement, matcher) \
+    ASSERT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, matcher)
 
-// Like ASSERT_DEATH, but continues on to successive tests in the
-// test case, if any:
-# define EXPECT_DEATH(statement, regex) \
-    EXPECT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, regex)
+// Like `ASSERT_DEATH`, but continues on to successive tests in the
+// test suite, if any:
+# define EXPECT_DEATH(statement, matcher) \
+    EXPECT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, matcher)
 
 // Two predicate classes that can be used in {ASSERT,EXPECT}_EXIT*:
 
@@ -190,17 +194,17 @@ GTEST_API_ bool InDeathTestChild();
 class GTEST_API_ ExitedWithCode {
  public:
   explicit ExitedWithCode(int exit_code);
+  ExitedWithCode(const ExitedWithCode&) = default;
+  void operator=(const ExitedWithCode& other) = delete;
   bool operator()(int exit_status) const;
  private:
-  // No implementation - assignment is unsupported.
-  void operator=(const ExitedWithCode& other);
-
   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
 // given signal.
+// GOOGLETEST_CM0006 DO NOT DELETE
 class GTEST_API_ KilledBySignal {
  public:
   explicit KilledBySignal(int signum);
@@ -226,7 +230,7 @@ class GTEST_API_ KilledBySignal {
 //   return 12;
 // }
 //
-// TEST(TestCase, TestDieOr12WorksInDgbAndOpt) {
+// TEST(TestSuite, TestDieOr12WorksInDgbAndOpt) {
 //   int sideeffect = 0;
 //   // Only asserts in dbg.
 //   EXPECT_DEBUG_DEATH(DieInDebugOr12(&sideeffect), "death");
@@ -272,6 +276,54 @@ class GTEST_API_ KilledBySignal {
 # endif  // NDEBUG for EXPECT_DEBUG_DEATH
 #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
 // ASSERT_DEATH_IF_SUPPORTED(statement, regex) expand to real death tests if
 // death tests are supported; otherwise they just issue a warning.  This is
@@ -284,11 +336,11 @@ class GTEST_API_ KilledBySignal {
     ASSERT_DEATH(statement, regex)
 #else
 # 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) \
-    GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, return)
+    GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, return)
 #endif
 
 }  // namespace testing
 
-#endif  // GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_
+#endif  // GOOGLETEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_

+ 930 - 0
contrib/gtest/include/gtest/gtest-matchers.h

@@ -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_

+ 16 - 47
contrib/gtest/include/gtest/gtest-message.h

@@ -26,10 +26,9 @@
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
 //
-// Author: [email protected] (Zhanyong Wan)
-//
-// The Google C++ Testing Framework (Google Test)
+// The Google C++ Testing and Mocking Framework (Google Test)
 //
 // This header file defines the Message class.
 //
@@ -43,13 +42,20 @@
 // to CHANGE WITHOUT NOTICE.  Therefore DO NOT DEPEND ON IT in a user
 // program!
 
-#ifndef GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_
-#define GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_
+// GOOGLETEST_CM0001 DO NOT DELETE
+
+#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_MESSAGE_H_
+#define GOOGLETEST_INCLUDE_GTEST_GTEST_MESSAGE_H_
 
 #include <limits>
+#include <memory>
+#include <sstream>
 
 #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.
 // See Message& operator<<(...) below for why.
 void operator<<(const testing::internal::Secret&, int);
@@ -102,14 +108,6 @@ class GTEST_API_ Message {
     *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.
   template <typename T>
   inline Message& operator <<(const T& val) {
@@ -147,14 +145,13 @@ class GTEST_API_ Message {
   // as "(null)".
   template <typename T>
   inline Message& operator <<(T* const& pointer) {  // NOLINT
-    if (pointer == NULL) {
+    if (pointer == nullptr) {
       *ss_ << "(null)";
     } else {
       *ss_ << pointer;
     }
     return *this;
   }
-#endif  // GTEST_OS_SYMBIAN
 
   // Since the basic IO manipulators are overloaded for both narrow
   // and wide streams, we have to provide this specialized definition
@@ -183,12 +180,6 @@ class GTEST_API_ Message {
   Message& operator <<(const ::std::wstring& wstr);
 #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.
   // Each '\0' character in the buffer is replaced with "\\0".
   //
@@ -196,32 +187,8 @@ class GTEST_API_ Message {
   std::string GetString() const;
 
  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.
-  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
   // from implementing the assignment operator.
@@ -247,4 +214,6 @@ std::string StreamableToString(const T& streamable) {
 }  // namespace internal
 }  // namespace testing
 
-#endif  // GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_
+GTEST_DISABLE_MSC_WARNINGS_POP_()  //  4251
+
+#endif  // GOOGLETEST_INCLUDE_GTEST_GTEST_MESSAGE_H_

Datei-Diff unterdrückt, da er zu groß ist
+ 43 - 907
contrib/gtest/include/gtest/gtest-param-test.h


Datei-Diff unterdrückt, da er zu groß ist
+ 462 - 426
contrib/gtest/include/gtest/gtest-printers.h


+ 16 - 10
contrib/gtest/include/gtest/gtest-spi.h

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

+ 27 - 22
contrib/gtest/include/gtest/gtest-test-part.h

@@ -27,17 +27,19 @@
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 //
-// Author: [email protected] (Markus Heule)
-//
+// GOOGLETEST_CM0001 DO NOT DELETE
 
-#ifndef GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_
-#define GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_
+#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_TEST_PART_H_
+#define GOOGLETEST_INCLUDE_GTEST_GTEST_TEST_PART_H_
 
 #include <iosfwd>
 #include <vector>
 #include "gtest/internal/gtest-internal.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 {
 
 // A copyable object representing the result of a test part (i.e. an
@@ -51,22 +53,20 @@ class GTEST_API_ TestPartResult {
   enum Type {
     kSuccess,          // Succeeded.
     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.
   // Always use this constructor (with parameters) to create a
   // TestPartResult object.
-  TestPartResult(Type a_type,
-                 const char* a_file_name,
-                 int a_line_number,
+  TestPartResult(Type a_type, const char* a_file_name, int a_line_number,
                  const char* a_message)
       : 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),
         summary_(ExtractSummary(a_message)),
-        message_(a_message) {
-  }
+        message_(a_message) {}
 
   // Gets the outcome of the test part.
   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
   // NULL if it's unknown.
   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,
@@ -87,18 +87,21 @@ class GTEST_API_ TestPartResult {
   // Gets the message associated with the test part.
   const char* message() const { return message_.c_str(); }
 
-  // Returns true iff the test part passed.
-  bool passed() const { return type_ == kSuccess; }
+  // Returns true if and only if the test part was skipped.
+  bool skipped() const { return type_ == kSkip; }
 
-  // Returns true iff the test part failed.
-  bool failed() const { return type_ != kSuccess; }
+  // Returns true if and only if the test part passed.
+  bool passed() const { return type_ == kSuccess; }
 
-  // Returns true iff the test part non-fatally failed.
+  // Returns true if and only if the test part non-fatally failed.
   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; }
 
+  // Returns true if and only if the test part failed.
+  bool failed() const { return fatally_failed() || nonfatally_failed(); }
+
  private:
   Type type_;
 
@@ -143,7 +146,7 @@ class GTEST_API_ TestPartResultArray {
 };
 
 // This interface knows how to report a test part result.
-class TestPartResultReporterInterface {
+class GTEST_API_ TestPartResultReporterInterface {
  public:
   virtual ~TestPartResultReporterInterface() {}
 
@@ -162,8 +165,8 @@ class GTEST_API_ HasNewFatalFailureHelper
     : public TestPartResultReporterInterface {
  public:
   HasNewFatalFailureHelper();
-  virtual ~HasNewFatalFailureHelper();
-  virtual void ReportTestPartResult(const TestPartResult& result);
+  ~HasNewFatalFailureHelper() override;
+  void ReportTestPartResult(const TestPartResult& result) override;
   bool has_new_fatal_failure() const { return has_new_fatal_failure_; }
  private:
   bool has_new_fatal_failure_;
@@ -176,4 +179,6 @@ class GTEST_API_ HasNewFatalFailureHelper
 
 }  // namespace testing
 
-#endif  // GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_
+GTEST_DISABLE_MSC_WARNINGS_POP_()  //  4251
+
+#endif  // GOOGLETEST_INCLUDE_GTEST_GTEST_TEST_PART_H_

+ 174 - 108
contrib/gtest/include/gtest/gtest-typed-test.h

@@ -26,11 +26,11 @@
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-//
-// Author: [email protected] (Zhanyong Wan)
 
-#ifndef GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_
-#define GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_
+// GOOGLETEST_CM0001 DO NOT DELETE
+
+#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.
 
@@ -51,22 +51,22 @@ class FooTest : public testing::Test {
   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
 // the macro to parse correctly.
 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
 // 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
-// tests for this test case as you want.
+// tests for this test suite as you want.
 TYPED_TEST(FooTest, DoesBlah) {
-  // Inside a test, refer to TypeParam to get the type parameter.
-  // Since we are inside a derived class template, C++ requires use to
-  // visit the members of FooTest via 'this'.
+  // Inside a test, refer to the special name TypeParam to get the type
+  // parameter.  Since we are inside a derived class template, C++ requires
+  // us to visit the members of FooTest via 'this'.
   TypeParam n = this->value_;
 
   // To visit static members of the fixture, add the TestFixture::
@@ -82,6 +82,24 @@ TYPED_TEST(FooTest, DoesBlah) {
 
 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
 
 // 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
 // prefer):
-TYPED_TEST_CASE_P(FooTest);
+TYPED_TEST_SUITE_P(FooTest);
 
 // 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) {
   // Inside a test, refer to TypeParam to get the type parameter.
   TypeParam n = 0;
@@ -124,10 +142,10 @@ TYPED_TEST_P(FooTest, HasPropertyA) { ... }
 
 // Now the tricky part: you need to register all test patterns before
 // 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.
-REGISTER_TYPED_TEST_CASE_P(FooTest,
-                           DoesBlah, HasPropertyA);
+REGISTER_TYPED_TEST_SUITE_P(FooTest,
+                            DoesBlah, HasPropertyA);
 
 // 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
@@ -135,129 +153,177 @@ REGISTER_TYPED_TEST_CASE_P(FooTest,
 //
 // To distinguish different instances of the pattern, the first
 // 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.
 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
 // 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
 
+#include "gtest/internal/gtest-internal.h"
 #include "gtest/internal/gtest-port.h"
 #include "gtest/internal/gtest-type-util.h"
 
 // Implements typed tests.
 
-#if GTEST_HAS_TYPED_TEST
-
 // INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
 //
 // Expands to the name of the typedef for the type parameters of the
-// given test case.
-# define GTEST_TYPE_PARAMS_(TestCaseName) gtest_type_params_##TestCaseName##_
-
-// The 'Types' template argument below must have spaces around it
-// since some compilers may choke on '>>' when passing a template
-// instance (e.g. Types<int>)
-# define TYPED_TEST_CASE(CaseName, Types) \
-  typedef ::testing::internal::TypeList< Types >::type \
-      GTEST_TYPE_PARAMS_(CaseName)
-
-# define TYPED_TEST(CaseName, TestName) \
-  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; \
-    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
+// given test suite.
+#define GTEST_TYPE_PARAMS_(TestSuiteName) gtest_type_params_##TestSuiteName##_
+
+// Expands to the name of the typedef for the NameGenerator, responsible for
+// creating the suffixes of the name.
+#define GTEST_NAME_GENERATOR_(TestSuiteName) \
+  gtest_type_params_##TestSuiteName##_NameGenerator
+
+#define TYPED_TEST_SUITE(CaseName, Types, ...)                          \
+  typedef ::testing::internal::GenerateTypeList<Types>::type            \
+      GTEST_TYPE_PARAMS_(CaseName);                                     \
+  typedef ::testing::internal::NameGeneratorSelector<__VA_ARGS__>::type \
+      GTEST_NAME_GENERATOR_(CaseName)
+
+#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.
 
-#if GTEST_HAS_TYPED_TEST_P
-
 // INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
 //
 // 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.
-# define GTEST_CASE_NAMESPACE_(TestCaseName) \
-  gtest_case_##TestCaseName##_
+#define GTEST_SUITE_NAMESPACE_(TestSuiteName) gtest_suite_##TestSuiteName##_
 
 // INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
 //
 // Expands to the name of the variable used to remember the names of
-// the defined tests in the given test case.
-# define GTEST_TYPED_TEST_CASE_P_STATE_(TestCaseName) \
-  gtest_typed_test_case_p_state_##TestCaseName##_
+// the defined tests in the given test suite.
+#define GTEST_TYPED_TEST_SUITE_P_STATE_(TestSuiteName) \
+  gtest_typed_test_suite_p_state_##TestSuiteName##_
 
 // INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE DIRECTLY.
 //
 // Expands to the name of the variable used to remember the names of
-// the registered tests in the given test case.
-# define GTEST_REGISTERED_TEST_NAMES_(TestCaseName) \
-  gtest_registered_test_names_##TestCaseName##_
+// the registered tests in the given test suite.
+#define GTEST_REGISTERED_TEST_NAMES_(TestSuiteName) \
+  gtest_registered_test_names_##TestSuiteName##_
 
 // The variables defined in the type-parameterized test macros are
 // static as typically these macros are used in a .h file that can be
 // #included in multiple translation units linked together.
-# define TYPED_TEST_CASE_P(CaseName) \
-  static ::testing::internal::TypedTestCasePState \
-      GTEST_TYPED_TEST_CASE_P_STATE_(CaseName)
-
-# define TYPED_TEST_P(CaseName, TestName) \
-  namespace GTEST_CASE_NAMESPACE_(CaseName) { \
-  template <typename gtest_TypeParam_> \
-  class TestName : public CaseName<gtest_TypeParam_> { \
-   private: \
-    typedef CaseName<gtest_TypeParam_> TestFixture; \
-    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, ...) \
-  namespace GTEST_CASE_NAMESPACE_(CaseName) { \
-  typedef ::testing::internal::Templates<__VA_ARGS__>::type gtest_AllTests_; \
-  } \
-  static const char* const GTEST_REGISTERED_TEST_NAMES_(CaseName) = \
-      GTEST_TYPED_TEST_CASE_P_STATE_(CaseName).VerifyRegisteredTestNames(\
-          __FILE__, __LINE__, #__VA_ARGS__)
-
-// The 'Types' template argument below must have spaces around it
-// since some compilers may choke on '>>' when passing a template
-// instance (e.g. Types<int>)
-# define INSTANTIATE_TYPED_TEST_CASE_P(Prefix, CaseName, Types) \
-  bool gtest_##Prefix##_##CaseName GTEST_ATTRIBUTE_UNUSED_ = \
-      ::testing::internal::TypeParameterizedTestCase<CaseName, \
-          GTEST_CASE_NAMESPACE_(CaseName)::gtest_AllTests_, \
-          ::testing::internal::TypeList< Types >::type>::Register(\
-              #Prefix, \
-              ::testing::internal::CodeLocation(__FILE__, __LINE__), \
-              &GTEST_TYPED_TEST_CASE_P_STATE_(CaseName), \
-              #CaseName, GTEST_REGISTERED_TEST_NAMES_(CaseName))
-
-#endif  // GTEST_HAS_TYPED_TEST_P
-
-#endif  // GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_
+#define TYPED_TEST_SUITE_P(SuiteName)              \
+  static ::testing::internal::TypedTestSuitePState \
+      GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName)
+
+// Legacy API is deprecated but still available
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+#define TYPED_TEST_CASE_P                                                 \
+  static_assert(::testing::internal::TypedTestCase_P_IsDeprecated(), ""); \
+  TYPED_TEST_SUITE_P
+#endif  // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+
+#define TYPED_TEST_P(SuiteName, TestName)                             \
+  namespace GTEST_SUITE_NAMESPACE_(SuiteName) {                       \
+    template <typename gtest_TypeParam_>                              \
+    class TestName : public SuiteName<gtest_TypeParam_> {             \
+     private:                                                         \
+      typedef SuiteName<gtest_TypeParam_> TestFixture;                \
+      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()
+
+// Note: this won't work correctly if the trailing arguments are macros.
+#define REGISTER_TYPED_TEST_SUITE_P(SuiteName, ...)                         \
+  namespace GTEST_SUITE_NAMESPACE_(SuiteName) {                             \
+    typedef ::testing::internal::Templates<__VA_ARGS__> gtest_AllTests_;    \
+  }                                                                         \
+  static const char* const GTEST_REGISTERED_TEST_NAMES_(                    \
+      SuiteName) GTEST_ATTRIBUTE_UNUSED_ =                                  \
+      GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName).VerifyRegisteredTestNames( \
+          GTEST_STRINGIFY_(SuiteName), __FILE__, __LINE__, #__VA_ARGS__)
+
+// 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_
+
+#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_

Datei-Diff unterdrückt, da er zu groß ist
+ 317 - 190
contrib/gtest/include/gtest/gtest.h


+ 44 - 43
contrib/gtest/include/gtest/gtest_pred_impl.h

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

+ 13 - 10
contrib/gtest/include/gtest/gtest_prod.h

@@ -26,13 +26,13 @@
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
 //
-// Author: [email protected] (Zhanyong Wan)
-//
-// Google C++ Testing Framework definitions useful in production code.
+// Google C++ Testing and Mocking Framework definitions useful in production code.
+// GOOGLETEST_CM0003 DO NOT DELETE
 
-#ifndef GTEST_INCLUDE_GTEST_GTEST_PROD_H_
-#define GTEST_INCLUDE_GTEST_GTEST_PROD_H_
+#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,
 // use the FRIEND_TEST macro to declare your tests as friends of the
@@ -40,19 +40,22 @@
 //
 // class MyClass {
 //  private:
-//   void MyMethod();
-//   FRIEND_TEST(MyClassTest, MyMethod);
+//   void PrivateMethod();
+//   FRIEND_TEST(MyClassTest, PrivateMethodWorks);
 // };
 //
 // class MyClassTest : public testing::Test {
 //   // ...
 // };
 //
-// TEST_F(MyClassTest, MyMethod) {
-//   // Can call MyClass::MyMethod() here.
+// TEST_F(MyClassTest, PrivateMethodWorks) {
+//   // 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)\
 friend class test_case_name##_##test_name##_Test
 
-#endif  // GTEST_INCLUDE_GTEST_GTEST_PROD_H_
+#endif  // GOOGLETEST_INCLUDE_GTEST_GTEST_PROD_H_

+ 56 - 0
contrib/gtest/include/gtest/internal/custom/README.md

@@ -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.

+ 4 - 36
contrib/gtest/include/gtest/internal/custom/gtest-port.h

@@ -27,43 +27,11 @@
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 //
-// Injection point for custom user configurations.
-// 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)
+// Injection point for custom user configurations. See README for details
 //
 // ** Custom implementation starts here **
 
-#ifndef GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_
-#define GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_
+#ifndef GOOGLETEST_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_

+ 5 - 5
contrib/gtest/include/gtest/internal/custom/gtest-printers.h

@@ -31,12 +31,12 @@
 // installation of gTest.
 // It will be included from gtest-printers.h and the overrides in this file
 // 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 **
 
-#ifndef GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_
-#define GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_
+#ifndef GOOGLETEST_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_

+ 4 - 8
contrib/gtest/include/gtest/internal/custom/gtest.h

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

+ 83 - 98
contrib/gtest/include/gtest/internal/gtest-death-test-internal.h

@@ -27,19 +27,20 @@
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 //
-// Authors: [email protected] (Zhanyong Wan), [email protected] (Sean Mcafee)
-//
-// The Google C++ Testing Framework (Google Test)
+// The Google C++ Testing and Mocking Framework (Google Test)
 //
 // This header file defines internal utilities needed for implementing
 // death tests.  They are subject to change without notice.
+// GOOGLETEST_CM0001 DO NOT DELETE
 
-#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_
-#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_
+#ifndef GOOGLETEST_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 <stdio.h>
+#include <memory>
 
 namespace testing {
 namespace internal {
@@ -53,6 +54,9 @@ const char kInternalRunDeathTestFlag[] = "internal_run_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
 // GTEST_DEATH_TEST_ macro.  It is abstract; its static Create method
 // 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
   // 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.
-  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);
   DeathTest();
   virtual ~DeathTest() { }
@@ -136,25 +140,50 @@ class GTEST_API_ DeathTest {
   GTEST_DISALLOW_COPY_AND_ASSIGN_(DeathTest);
 };
 
+GTEST_DISABLE_MSC_WARNINGS_POP_()  //  4251
+
 // Factory interface for death tests.  May be mocked out for testing.
 class DeathTestFactory {
  public:
   virtual ~DeathTestFactory() { }
-  virtual bool Create(const char* statement, const RE* regex,
-                      const char* file, int line, DeathTest** test) = 0;
+  virtual bool Create(const char* statement,
+                      Matcher<const std::string&> matcher, const char* file,
+                      int line, DeathTest** test) = 0;
 };
 
 // A concrete DeathTestFactory implementation for normal use.
 class DefaultDeathTestFactory : public DeathTestFactory {
  public:
-  virtual bool Create(const char* statement, const RE* regex,
-                      const char* file, int line, DeathTest** test);
+  bool Create(const char* statement, Matcher<const std::string&> matcher,
+              const char* file, int line, DeathTest** test) override;
 };
 
 // Returns true if exit_status describes a process that was terminated
 // by a signal, or exited normally with a nonzero exit code.
 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
 // failures. Note that trapping SEH exceptions is not implemented here.
 # if GTEST_HAS_EXCEPTIONS
@@ -182,50 +211,53 @@ GTEST_API_ bool ExitedUnsuccessfully(int exit_status);
 
 // This macro is for implementing ASSERT_DEATH*, EXPECT_DEATH*,
 // ASSERT_EXIT*, and EXPECT_EXIT*.
-# define GTEST_DEATH_TEST_(statement, predicate, regex, fail) \
-  GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
-  if (::testing::internal::AlwaysTrue()) { \
-    const ::testing::internal::RE& gtest_regex = (regex); \
-    ::testing::internal::DeathTest* gtest_dt; \
-    if (!::testing::internal::DeathTest::Create(#statement, &gtest_regex, \
-        __FILE__, __LINE__, &gtest_dt)) { \
-      goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \
-    } \
-    if (gtest_dt != NULL) { \
-      ::testing::internal::scoped_ptr< ::testing::internal::DeathTest> \
-          gtest_dt_ptr(gtest_dt); \
-      switch (gtest_dt->AssumeRole()) { \
-        case ::testing::internal::DeathTest::OVERSEE_TEST: \
-          if (!gtest_dt->Passed(predicate(gtest_dt->Wait()))) { \
-            goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \
-          } \
-          break; \
-        case ::testing::internal::DeathTest::EXECUTE_TEST: { \
-          ::testing::internal::DeathTest::ReturnSentinel \
-              gtest_sentinel(gtest_dt); \
-          GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, gtest_dt); \
-          gtest_dt->Abort(::testing::internal::DeathTest::TEST_DID_NOT_DIE); \
-          break; \
-        } \
-        default: \
-          break; \
-      } \
-    } \
-  } else \
-    GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__): \
-      fail(::testing::internal::DeathTest::LastMessage())
+#define GTEST_DEATH_TEST_(statement, predicate, regex_or_matcher, fail)        \
+  GTEST_AMBIGUOUS_ELSE_BLOCKER_                                                \
+  if (::testing::internal::AlwaysTrue()) {                                     \
+    ::testing::internal::DeathTest* gtest_dt;                                  \
+    if (!::testing::internal::DeathTest::Create(                               \
+            #statement,                                                        \
+            ::testing::internal::MakeDeathTestMatcher(regex_or_matcher),       \
+            __FILE__, __LINE__, &gtest_dt)) {                                  \
+      goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__);                        \
+    }                                                                          \
+    if (gtest_dt != nullptr) {                                                 \
+      std::unique_ptr< ::testing::internal::DeathTest> gtest_dt_ptr(gtest_dt); \
+      switch (gtest_dt->AssumeRole()) {                                        \
+        case ::testing::internal::DeathTest::OVERSEE_TEST:                     \
+          if (!gtest_dt->Passed(predicate(gtest_dt->Wait()))) {                \
+            goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__);                  \
+          }                                                                    \
+          break;                                                               \
+        case ::testing::internal::DeathTest::EXECUTE_TEST: {                   \
+          ::testing::internal::DeathTest::ReturnSentinel gtest_sentinel(       \
+              gtest_dt);                                                       \
+          GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, gtest_dt);            \
+          gtest_dt->Abort(::testing::internal::DeathTest::TEST_DID_NOT_DIE);   \
+          break;                                                               \
+        }                                                                      \
+        default:                                                               \
+          break;                                                               \
+      }                                                                        \
+    }                                                                          \
+  } else                                                                       \
+    GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__)                                \
+        : fail(::testing::internal::DeathTest::LastMessage())
 // The symbol "fail" here expands to something into which a message
 // can be streamed.
 
 // 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
-// ignored, and the macro must accept a streamed message even though the message
-// is never printed.
-# define GTEST_EXECUTE_STATEMENT_(statement, regex) \
-  GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
-  if (::testing::internal::AlwaysTrue()) { \
-     GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
-  } else \
+// NDEBUG mode. In this case we need the statements to be executed and the macro
+// must accept a streamed message even though the message is never printed.
+// The regex object is not evaluated, but it is used to prevent "unused"
+// warnings and to avoid an expression that doesn't compile in debug mode.
+#define GTEST_EXECUTE_STATEMENT_(statement, regex_or_matcher)    \
+  GTEST_AMBIGUOUS_ELSE_BLOCKER_                                  \
+  if (::testing::internal::AlwaysTrue()) {                       \
+    GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement);   \
+  } else if (!::testing::internal::AlwaysTrue()) {               \
+    ::testing::internal::MakeDeathTestMatcher(regex_or_matcher); \
+  } else                                                         \
     ::testing::Message()
 
 // A class representing the parsed contents of the
@@ -264,56 +296,9 @@ class InternalRunDeathTestFlag {
 // the flag is specified; otherwise returns NULL.
 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
 
 }  // namespace internal
 }  // namespace testing
 
-#endif  // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_
+#endif  // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_

+ 13 - 8
contrib/gtest/include/gtest/internal/gtest-filepath.h

@@ -27,21 +27,24 @@
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 //
-// Author: [email protected] (Keith Ray)
-//
 // Google Test filepath utilities
 //
 // This header file declares classes and functions used internally by
 // 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!
 
-#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_
-#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_
+// GOOGLETEST_CM0001 DO NOT DELETE
+
+#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_
+#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_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 internal {
 
@@ -107,7 +110,7 @@ class GTEST_API_ FilePath {
                                          const FilePath& base_name,
                                          const char* extension);
 
-  // Returns true iff the path is "".
+  // Returns true if and only if the path is "".
   bool IsEmpty() const { return pathname_.empty(); }
 
   // If input name has a trailing separator character, removes it and returns
@@ -192,7 +195,7 @@ class GTEST_API_ FilePath {
 
   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
   // separators. Returns NULL if no path separator was found.
   const char* FindLastPathSeparator() const;
@@ -203,4 +206,6 @@ class GTEST_API_ FilePath {
 }  // namespace internal
 }  // namespace testing
 
-#endif  // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_
+GTEST_DISABLE_MSC_WARNINGS_POP_()  //  4251
+
+#endif  // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_

Datei-Diff unterdrückt, da er zu groß ist
+ 515 - 279
contrib/gtest/include/gtest/internal/gtest-internal.h


+ 398 - 182
contrib/gtest/include/gtest/internal/gtest-param-util.h

@@ -26,33 +26,32 @@
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-//
-// Author: [email protected] (Vlad Losev)
+
 
 // Type and function utilities for implementing parameterized tests.
 
-#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_
-#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_
+// GOOGLETEST_CM0001 DO NOT DELETE
+
+#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_
+#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_
 
 #include <ctype.h>
 
+#include <cassert>
 #include <iterator>
+#include <memory>
 #include <set>
+#include <tuple>
+#include <type_traits>
 #include <utility>
 #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-linked_ptr.h"
 #include "gtest/internal/gtest-port.h"
 #include "gtest/gtest-printers.h"
-
-#if GTEST_HAS_PARAM_TEST
+#include "gtest/gtest-test-part.h"
 
 namespace testing {
-
 // Input to a parameterized test name generator, describing a test parameter.
 // Consists of the parameter value and the integer parameter index.
 template <class ParamType>
@@ -76,13 +75,14 @@ struct PrintToStringParamName {
 namespace internal {
 
 // INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
-//
+// Utility Functions
+
 // 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
 // but in different namespaces.
-GTEST_API_ void ReportInvalidTestCaseType(const char* test_case_name,
-                                          CodeLocation code_location);
+GTEST_API_ void ReportInvalidTestSuiteType(const char* test_suite_name,
+                                           CodeLocation code_location);
 
 template <typename> class ParamGeneratorInterface;
 template <typename> class ParamGenerator;
@@ -157,7 +157,7 @@ class ParamIterator {
  private:
   friend class ParamGenerator<T>;
   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
@@ -196,7 +196,7 @@ class ParamGenerator {
   iterator end() const { return iterator(impl_->End()); }
 
  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
@@ -209,12 +209,12 @@ class RangeGenerator : public ParamGeneratorInterface<T> {
   RangeGenerator(T begin, T end, IncrementT step)
       : begin_(begin), end_(end),
         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_);
   }
-  virtual ParamIteratorInterface<T>* End() const {
+  ParamIteratorInterface<T>* End() const override {
     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,
              IncrementT 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_;
     }
-    virtual void Advance() {
+    void Advance() override {
       value_ = static_cast<T>(value_ + step_);
       index_++;
     }
-    virtual ParamIteratorInterface<T>* Clone() const {
+    ParamIteratorInterface<T>* Clone() const override {
       return new Iterator(*this);
     }
-    virtual const T* Current() const { return &value_; }
-    virtual bool Equals(const ParamIteratorInterface<T>& other) const {
+    const T* Current() const override { return &value_; }
+    bool Equals(const ParamIteratorInterface<T>& 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())
@@ -294,12 +294,12 @@ class ValuesInIteratorRangeGenerator : public ParamGeneratorInterface<T> {
   template <typename ForwardIterator>
   ValuesInIteratorRangeGenerator(ForwardIterator begin, ForwardIterator end)
       : container_(begin, end) {}
-  virtual ~ValuesInIteratorRangeGenerator() {}
+  ~ValuesInIteratorRangeGenerator() override {}
 
-  virtual ParamIteratorInterface<T>* Begin() const {
+  ParamIteratorInterface<T>* Begin() const override {
     return new Iterator(this, container_.begin());
   }
-  virtual ParamIteratorInterface<T>* End() const {
+  ParamIteratorInterface<T>* End() const override {
     return new Iterator(this, container_.end());
   }
 
@@ -311,16 +311,16 @@ class ValuesInIteratorRangeGenerator : public ParamGeneratorInterface<T> {
     Iterator(const ParamGeneratorInterface<T>* base,
              typename ContainerType::const_iterator iterator)
         : base_(base), iterator_(iterator) {}
-    virtual ~Iterator() {}
+    ~Iterator() override {}
 
-    virtual const ParamGeneratorInterface<T>* BaseGenerator() const {
+    const ParamGeneratorInterface<T>* BaseGenerator() const override {
       return base_;
     }
-    virtual void Advance() {
+    void Advance() override {
       ++iterator_;
       value_.reset();
     }
-    virtual ParamIteratorInterface<T>* Clone() const {
+    ParamIteratorInterface<T>* Clone() const override {
       return new Iterator(*this);
     }
     // 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
     // detect that fact. The client code, on the other hand, is
     // responsible for not calling Current() on an out-of-range iterator.
-    virtual const T* Current() const {
-      if (value_.get() == NULL)
-        value_.reset(new T(*iterator_));
+    const T* Current() const override {
+      if (value_.get() == nullptr) value_.reset(new T(*iterator_));
       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
       // iterator is of the same type and we can downcast.
       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
     // pointer in the wrapping iterator's operator->().
     // 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.
-    mutable scoped_ptr<const T> value_;
+    mutable std::unique_ptr<const T> value_;
   };  // class ValuesInIteratorRangeGenerator::Iterator
 
   // No implementation - assignment is unsupported.
@@ -380,25 +379,12 @@ std::string DefaultParamName(const TestParamInfo<ParamType>& info) {
   return name_stream.GetString();
 }
 
-// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
-//
-// Parameterized test name overload helpers, which help the
-// 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() {
+  static_assert(sizeof(T) == 0, "Empty arguments are not allowed.");
 }
+template <typename T = int>
+void TestNotEmpty(const T&) {}
 
 // INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
 //
@@ -410,7 +396,7 @@ class ParameterizedTestFactory : public TestFactoryBase {
   typedef typename TestClass::ParamType ParamType;
   explicit ParameterizedTestFactory(ParamType parameter) :
       parameter_(parameter) {}
-  virtual Test* CreateTest() {
+  Test* CreateTest() override {
     TestClass::SetParam(&parameter_);
     return new TestClass();
   }
@@ -438,19 +424,19 @@ class TestMetaFactoryBase {
 // TestMetaFactory creates test factories for passing into
 // MakeAndRegisterTestInfo function. Since MakeAndRegisterTestInfo receives
 // 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
 // creator class.
-template <class TestCase>
+template <class TestSuite>
 class TestMetaFactory
-    : public TestMetaFactoryBase<typename TestCase::ParamType> {
+    : public TestMetaFactoryBase<typename TestSuite::ParamType> {
  public:
-  typedef typename TestCase::ParamType ParamType;
+  using ParamType = typename TestSuite::ParamType;
 
   TestMetaFactory() {}
 
-  virtual TestFactoryBase* CreateTestFactory(ParamType parameter) {
-    return new ParameterizedTestFactory<TestCase>(parameter);
+  TestFactoryBase* CreateTestFactory(ParamType parameter) override {
+    return new ParameterizedTestFactory<TestSuite>(parameter);
   }
 
  private:
@@ -459,113 +445,128 @@ class TestMetaFactory
 
 // INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
 //
-// ParameterizedTestCaseInfoBase is a generic interface
-// to ParameterizedTestCaseInfo classes. ParameterizedTestCaseInfoBase
+// ParameterizedTestSuiteInfoBase is a generic interface
+// to ParameterizedTestSuiteInfo classes. ParameterizedTestSuiteInfoBase
 // 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
-// in RegisterTests method. The ParameterizeTestCaseRegistry class holds
-// a collection of pointers to the ParameterizedTestCaseInfo objects
+// in RegisterTests method. The ParameterizeTestSuiteRegistry class holds
+// a collection of pointers to the ParameterizedTestSuiteInfo objects
 // and calls RegisterTests() on each of them when asked.
-class ParameterizedTestCaseInfoBase {
+class ParameterizedTestSuiteInfoBase {
  public:
-  virtual ~ParameterizedTestCaseInfoBase() {}
+  virtual ~ParameterizedTestSuiteInfoBase() {}
 
-  // Base part of test case name for display purposes.
-  virtual const string& GetTestCaseName() const = 0;
-  // Test case id to verify identity.
-  virtual TypeId GetTestCaseTypeId() const = 0;
+  // Base part of test suite name for display purposes.
+  virtual const std::string& GetTestSuiteName() const = 0;
+  // Test suite id to verify identity.
+  virtual TypeId GetTestSuiteTypeId() const = 0;
   // UnitTest class invokes this method to register tests in this
-  // test case right before running them in RUN_ALL_TESTS macro.
-  // This method should not be called more then once on any single
-  // instance of a ParameterizedTestCaseInfoBase derived class.
+  // test suite right before running them in RUN_ALL_TESTS macro.
+  // This method should not be called more than once on any single
+  // instance of a ParameterizedTestSuiteInfoBase derived class.
   virtual void RegisterTests() = 0;
 
  protected:
-  ParameterizedTestCaseInfoBase() {}
+  ParameterizedTestSuiteInfoBase() {}
 
  private:
-  GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseInfoBase);
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestSuiteInfoBase);
 };
 
 // INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
 //
-// ParameterizedTestCaseInfo accumulates tests obtained from TEST_P
-// macro invocations for a particular test case and generators
-// obtained from INSTANTIATE_TEST_CASE_P macro invocations for that
-// test case. It registers tests with all values generated by all
+// Report a the name of a test_suit as safe to ignore
+// as the side effect of construction of this type.
+struct GTEST_API_ MarkAsIgnored {
+  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.
-template <class TestCase>
-class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase {
+template <class TestSuite>
+class ParameterizedTestSuiteInfo : public ParameterizedTestSuiteInfoBase {
  public:
   // ParamType and GeneratorCreationFunc are private types but are required
   // for declarations of public methods AddTestPattern() and
-  // AddTestCaseInstantiation().
-  typedef typename TestCase::ParamType ParamType;
+  // AddTestSuiteInstantiation().
+  using ParamType = typename TestSuite::ParamType;
   // A function that returns an instance of appropriate generator type.
   typedef ParamGenerator<ParamType>(GeneratorCreationFunc)();
-  typedef typename ParamNameGenFunc<ParamType>::Type ParamNameGeneratorFunc;
+  using ParamNameGeneratorFunc = std::string(const TestParamInfo<ParamType>&);
 
-  explicit ParameterizedTestCaseInfo(
-      const char* name, CodeLocation code_location)
-      : test_case_name_(name), code_location_(code_location) {}
+  explicit ParameterizedTestSuiteInfo(const char* name,
+                                      CodeLocation code_location)
+      : test_suite_name_(name), code_location_(code_location) {}
 
-  // Test case base name for display purposes.
-  virtual const string& GetTestCaseName() const { return test_case_name_; }
-  // Test case id to verify identity.
-  virtual TypeId GetTestCaseTypeId() const { return GetTypeId<TestCase>(); }
+  // Test suite base name for display purposes.
+  const std::string& GetTestSuiteName() const override {
+    return test_suite_name_;
+  }
+  // Test suite id to verify identity.
+  TypeId GetTestSuiteTypeId() const override { return GetTypeId<TestSuite>(); }
   // TEST_P macro uses AddTestPattern() to record information
   // 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
   // parameter index. For the test SequenceA/FooTest.DoBar/1 FooTest is
-  // test case base name and DoBar is test base name.
-  void AddTestPattern(const char* test_case_name,
-                      const char* test_base_name,
-                      TestMetaFactoryBase<ParamType>* meta_factory) {
-    tests_.push_back(linked_ptr<TestInfo>(new TestInfo(test_case_name,
-                                                       test_base_name,
-                                                       meta_factory)));
+  // test suite base name and DoBar is test base name.
+  void AddTestPattern(const char* test_suite_name, const char* test_base_name,
+                      TestMetaFactoryBase<ParamType>* meta_factory,
+                      CodeLocation code_location) {
+    tests_.push_back(std::shared_ptr<TestInfo>(new TestInfo(
+        test_suite_name, test_base_name, meta_factory, code_location)));
   }
-  // INSTANTIATE_TEST_CASE_P macro uses AddGenerator() to record information
+  // INSTANTIATE_TEST_SUITE_P macro uses AddGenerator() to record information
   // about a generator.
-  int AddTestCaseInstantiation(const string& instantiation_name,
-                               GeneratorCreationFunc* func,
-                               ParamNameGeneratorFunc* name_func,
-                               const char* file,
-                               int line) {
+  int AddTestSuiteInstantiation(const std::string& instantiation_name,
+                                GeneratorCreationFunc* func,
+                                ParamNameGeneratorFunc* name_func,
+                                const char* file, int line) {
     instantiations_.push_back(
         InstantiationInfo(instantiation_name, func, name_func, file, line));
     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
-  // test cases right before running tests in RUN_ALL_TESTS macro.
-  // This method should not be called more then once on any single
-  // instance of a ParameterizedTestCaseInfoBase derived class.
-  // UnitTest has a guard to prevent from calling this method more then once.
-  virtual void RegisterTests() {
+  // UnitTest class invokes this method to register tests in this test suite
+  // right before running tests in RUN_ALL_TESTS macro.
+  // This method should not be called more than once on any single
+  // instance of a ParameterizedTestSuiteInfoBase derived class.
+  // UnitTest has a guard to prevent from calling this method more than once.
+  void RegisterTests() override {
+    bool generated_instantiations = false;
+
     for (typename TestInfoContainer::iterator test_it = tests_.begin();
          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 =
                instantiations_.begin(); gen_it != instantiations_.end();
                ++gen_it) {
-        const string& instantiation_name = gen_it->name;
+        const std::string& instantiation_name = gen_it->name;
         ParamGenerator<ParamType> generator((*gen_it->generator)());
         ParamNameGeneratorFunc* name_func = gen_it->name_func;
         const char* file = gen_it->file;
         int line = gen_it->line;
 
-        string test_case_name;
+        std::string test_suite_name;
         if ( !instantiation_name.empty() )
-          test_case_name = instantiation_name + "/";
-        test_case_name += test_info->test_case_base_name;
+          test_suite_name = instantiation_name + "/";
+        test_suite_name += test_info->test_suite_base_name;
 
         size_t i = 0;
         std::set<std::string> test_param_names;
         for (typename ParamGenerator<ParamType>::iterator param_it =
                  generator.begin();
              param_it != generator.end(); ++param_it, ++i) {
+          generated_instantiations = true;
+
           Message test_name_stream;
 
           std::string param_name = name_func(
@@ -582,39 +583,48 @@ class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase {
 
           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(
-              test_case_name.c_str(),
-              test_name_stream.GetString().c_str(),
-              NULL,  // No type parameter.
-              PrintToString(*param_it).c_str(),
-              code_location_,
-              GetTestCaseTypeId(),
-              TestCase::SetUpTestCase,
-              TestCase::TearDownTestCase,
+              test_suite_name.c_str(), test_name_stream.GetString().c_str(),
+              nullptr,  // No type parameter.
+              PrintToString(*param_it).c_str(), test_info->code_location,
+              GetTestSuiteTypeId(),
+              SuiteApiResolver<TestSuite>::GetSetUpCaseOrSuite(file, line),
+              SuiteApiResolver<TestSuite>::GetTearDownCaseOrSuite(file, line),
               test_info->test_meta_factory->CreateTestFactory(*param_it));
         }  // for param_it
       }  // for gen_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:
   // LocalTestInfo structure keeps information about a single test registered
   // with TEST_P macro.
   struct TestInfo {
-    TestInfo(const char* a_test_case_base_name,
-             const char* a_test_base_name,
-             TestMetaFactoryBase<ParamType>* a_test_meta_factory) :
-        test_case_base_name(a_test_case_base_name),
-        test_base_name(a_test_base_name),
-        test_meta_factory(a_test_meta_factory) {}
-
-    const string test_case_base_name;
-    const string test_base_name;
-    const scoped_ptr<TestMetaFactoryBase<ParamType> > test_meta_factory;
+    TestInfo(const char* a_test_suite_base_name, const char* a_test_base_name,
+             TestMetaFactoryBase<ParamType>* a_test_meta_factory,
+             CodeLocation a_code_location)
+        : test_suite_base_name(a_test_suite_base_name),
+          test_base_name(a_test_base_name),
+          test_meta_factory(a_test_meta_factory),
+          code_location(a_code_location) {}
+
+    const std::string test_suite_base_name;
+    const std::string test_base_name;
+    const std::unique_ptr<TestMetaFactoryBase<ParamType> > test_meta_factory;
+    const CodeLocation code_location;
   };
-  typedef ::std::vector<linked_ptr<TestInfo> > TestInfoContainer;
-  // Records data received from INSTANTIATE_TEST_CASE_P macros:
+  using TestInfoContainer = ::std::vector<std::shared_ptr<TestInfo> >;
+  // Records data received from INSTANTIATE_TEST_SUITE_P macros:
   //  <Instantiation name, Sequence generator creation function,
   //     Name generator function, Source file, Source line>
   struct InstantiationInfo {
@@ -651,81 +661,287 @@ class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase {
     return true;
   }
 
-  const string test_case_name_;
+  const std::string test_suite_name_;
   CodeLocation code_location_;
   TestInfoContainer tests_;
   InstantiationContainer instantiations_;
 
-  GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseInfo);
-};  // class ParameterizedTestCaseInfo
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestSuiteInfo);
+};  // 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.
 //
-// ParameterizedTestCaseRegistry contains a map of ParameterizedTestCaseInfoBase
-// classes accessed by test case names. TEST_P and INSTANTIATE_TEST_CASE_P
-// macros use it to locate their corresponding ParameterizedTestCaseInfo
-// descriptors.
-class ParameterizedTestCaseRegistry {
+// ParameterizedTestSuiteRegistry contains a map of
+// ParameterizedTestSuiteInfoBase classes accessed by test suite names. TEST_P
+// and INSTANTIATE_TEST_SUITE_P macros use it to locate their corresponding
+// ParameterizedTestSuiteInfo descriptors.
+class ParameterizedTestSuiteRegistry {
  public:
-  ParameterizedTestCaseRegistry() {}
-  ~ParameterizedTestCaseRegistry() {
-    for (TestCaseInfoContainer::iterator it = test_case_infos_.begin();
-         it != test_case_infos_.end(); ++it) {
-      delete *it;
+  ParameterizedTestSuiteRegistry() {}
+  ~ParameterizedTestSuiteRegistry() {
+    for (auto& test_suite_info : test_suite_infos_) {
+      delete test_suite_info;
     }
   }
 
   // Looks up or creates and returns a structure containing information about
-  // tests and instantiations of a particular test case.
-  template <class TestCase>
-  ParameterizedTestCaseInfo<TestCase>* GetTestCasePatternHolder(
-      const char* test_case_name,
-      CodeLocation code_location) {
-    ParameterizedTestCaseInfo<TestCase>* typed_test_info = NULL;
-    for (TestCaseInfoContainer::iterator it = test_case_infos_.begin();
-         it != test_case_infos_.end(); ++it) {
-      if ((*it)->GetTestCaseName() == test_case_name) {
-        if ((*it)->GetTestCaseTypeId() != GetTypeId<TestCase>()) {
+  // tests and instantiations of a particular test suite.
+  template <class TestSuite>
+  ParameterizedTestSuiteInfo<TestSuite>* GetTestSuitePatternHolder(
+      const char* test_suite_name, CodeLocation code_location) {
+    ParameterizedTestSuiteInfo<TestSuite>* typed_test_info = nullptr;
+    for (auto& test_suite_info : test_suite_infos_) {
+      if (test_suite_info->GetTestSuiteName() == test_suite_name) {
+        if (test_suite_info->GetTestSuiteTypeId() != GetTypeId<TestSuite>()) {
           // Complain about incorrect usage of Google Test facilities
           // and terminate the program since we cannot guaranty correct
-          // test case setup and tear-down in this case.
-          ReportInvalidTestCaseType(test_case_name, code_location);
+          // test suite setup and tear-down in this case.
+          ReportInvalidTestSuiteType(test_suite_name, code_location);
           posix::Abort();
         } else {
           // 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
           // without further checks.
           typed_test_info = CheckedDowncastToActualType<
-              ParameterizedTestCaseInfo<TestCase> >(*it);
+              ParameterizedTestSuiteInfo<TestSuite> >(test_suite_info);
         }
         break;
       }
     }
-    if (typed_test_info == NULL) {
-      typed_test_info = new ParameterizedTestCaseInfo<TestCase>(
-          test_case_name, code_location);
-      test_case_infos_.push_back(typed_test_info);
+    if (typed_test_info == nullptr) {
+      typed_test_info = new ParameterizedTestSuiteInfo<TestSuite>(
+          test_suite_name, code_location);
+      test_suite_infos_.push_back(typed_test_info);
     }
     return typed_test_info;
   }
   void RegisterTests() {
-    for (TestCaseInfoContainer::iterator it = test_case_infos_.begin();
-         it != test_case_infos_.end(); ++it) {
-      (*it)->RegisterTests();
+    for (auto& test_suite_info : test_suite_infos_) {
+      test_suite_info->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:
-  typedef ::std::vector<ParameterizedTestCaseInfoBase*> TestCaseInfoContainer;
+  struct TypeParameterizedTestSuiteInfo {
+    explicit TypeParameterizedTestSuiteInfo(CodeLocation c)
+        : code_location(c), instantiated(false) {}
 
-  TestCaseInfoContainer test_case_infos_;
+    CodeLocation code_location;
+    bool instantiated;
+  };
 
-  GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseRegistry);
+  std::map<std::string, TypeParameterizedTestSuiteInfo> suites_;
 };
 
 }  // namespace internal
-}  // namespace testing
 
-#endif  //  GTEST_HAS_PARAM_TEST
+// 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:
+  template <typename T, size_t... I>
+  std::vector<T> MakeVector(IndexSequence<I...>) const {
+    return std::vector<T>{static_cast<T>(v_.template Get<I>())...};
+  }
+
+  FlatTuple<Ts...> v_;
+};
+
+#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 testing
 
-#endif  // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_
+#endif  // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_

+ 29 - 8
contrib/gtest/include/gtest/internal/gtest-port-arch.h

@@ -27,25 +27,24 @@
 // (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 Framework (Google Test)
+// The Google C++ Testing and Mocking Framework (Google Test)
 //
 // This header file defines the GTEST_OS_* macro.
 // 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_
-#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_
+#ifndef GOOGLETEST_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.
 #ifdef __CYGWIN__
 # define GTEST_OS_CYGWIN 1
-#elif defined __SYMBIAN32__
-# define GTEST_OS_SYMBIAN 1
+# elif defined(__MINGW__) || defined(__MINGW32__) || defined(__MINGW64__)
+#  define GTEST_OS_WINDOWS_MINGW 1
+#  define GTEST_OS_WINDOWS 1
 #elif defined _WIN32
 # define GTEST_OS_WINDOWS 1
 # ifdef _WIN32_WCE
 #  define GTEST_OS_WINDOWS_MOBILE 1
-# elif defined(__MINGW__) || defined(__MINGW32__)
-#  define GTEST_OS_WINDOWS_MINGW 1
 # elif defined(WINAPI_FAMILY)
 #  include <winapifamily.h>
 #  if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
@@ -54,6 +53,9 @@
 #   define GTEST_OS_WINDOWS_PHONE 1
 #  elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)
 #   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
     // WINAPI_FAMILY defined but no known partition matched.
     // Default to desktop.
@@ -62,13 +64,22 @@
 # else
 #  define GTEST_OS_WINDOWS_DESKTOP 1
 # endif  // _WIN32_WCE
+#elif defined __OS2__
+# define GTEST_OS_OS2 1
 #elif defined __APPLE__
 # define GTEST_OS_MAC 1
+# include <TargetConditionals.h>
 # if TARGET_OS_IPHONE
 #  define GTEST_OS_IOS 1
 # endif
+#elif defined __DragonFly__
+# define GTEST_OS_DRAGONFLY 1
 #elif defined __FreeBSD__
 # 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__
 # define GTEST_OS_LINUX 1
 # if defined __ANDROID__
@@ -84,10 +95,20 @@
 # define GTEST_OS_HPUX 1
 #elif defined __native_client__
 # define GTEST_OS_NACL 1
+#elif defined __NetBSD__
+# define GTEST_OS_NETBSD 1
 #elif defined __OpenBSD__
 # define GTEST_OS_OPENBSD 1
 #elif defined __QNX__
 # 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  // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_
+#endif  // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_

Datei-Diff unterdrückt, da er zu groß ist
+ 226 - 501
contrib/gtest/include/gtest/internal/gtest-port.h


+ 24 - 16
contrib/gtest/include/gtest/internal/gtest-string.h

@@ -27,19 +27,19 @@
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 //
-// Authors: [email protected] (Zhanyong Wan), [email protected] (Sean Mcafee)
-//
-// The Google C++ Testing Framework (Google Test)
+// The Google C++ Testing and Mocking Framework (Google Test)
 //
 // 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
 // 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.
 
-#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_
-#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_
+// GOOGLETEST_CM0001 DO NOT DELETE
+
+#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_
+#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_
 
 #ifdef __BORLANDC__
 // string.h is not guaranteed to provide strcpy on C++ Builder.
@@ -47,6 +47,7 @@
 #endif
 
 #include <string.h>
+#include <cstdint>
 #include <string>
 
 #include "gtest/internal/gtest-port.h"
@@ -94,7 +95,8 @@ class GTEST_API_ String {
   static const char* Utf16ToAnsi(LPCWSTR utf16_str);
 #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
   // NULL C string is considered different to any non-NULL C string,
@@ -107,16 +109,16 @@ class GTEST_API_ String {
   // returned.
   static std::string ShowWideCString(const wchar_t* wide_c_str);
 
-  // Compares two wide C strings.  Returns true iff they have the same
-  // content.
+  // Compares two wide C strings.  Returns true if and only if they have the
+  // same content.
   //
   // Unlike wcscmp(), this function can handle NULL argument(s).  A
   // NULL C string is considered different to any non-NULL C string,
   // including the empty string.
   static bool WideCStringEquals(const wchar_t* lhs, const wchar_t* rhs);
 
-  // Compares two C strings, ignoring case.  Returns true iff they
-  // have the same content.
+  // Compares two C strings, ignoring case.  Returns true if and only if
+  // they have the same content.
   //
   // Unlike strcasecmp(), this function can handle NULL argument(s).
   // 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,
                                            const char* rhs);
 
-  // Compares two wide C strings, ignoring case.  Returns true iff they
-  // have the same content.
+  // Compares two wide C strings, ignoring case.  Returns true if and only if
+  // they have the same content.
   //
   // Unlike wcscasecmp(), this function can handle NULL argument(s).
   // 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,
                                                const wchar_t* rhs);
 
-  // Returns true iff the given string ends with the given suffix, ignoring
-  // case. Any string is considered to end with an empty suffix.
+  // Returns true if and only if the given string ends with the given suffix,
+  // ignoring case. Any string is considered to end with an empty suffix.
   static bool EndsWithCaseInsensitive(
       const std::string& str, const std::string& suffix);
 
   // Formats an int value as "%02d".
   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".
   static std::string FormatHexInt(int value);
 
+  // Formats an int value as "%X".
+  static std::string FormatHexUInt32(uint32_t value);
+
   // Formats a byte as "%02X".
   static std::string FormatByte(unsigned char value);
 
@@ -164,4 +172,4 @@ GTEST_API_ std::string StringStreamToString(::std::stringstream* stream);
 }  // namespace internal
 }  // namespace testing
 
-#endif  // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_
+#endif  // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_

Datei-Diff unterdrückt, da er zu groß ist
+ 44 - 1562
contrib/gtest/include/gtest/internal/gtest-type-util.h


+ 20 - 17
contrib/gtest/samples/prime_tables.h

@@ -26,16 +26,15 @@
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-//
-// Author: [email protected] (Zhanyong Wan)
-// Author: [email protected] (Vlad Losev)
+
+
 
 // This provides interface PrimeTable that determines whether a number is a
 // prime and determines a next prime number. This interface is used
 // in Google Test samples demonstrating use of parameterized tests.
 
-#ifndef GTEST_SAMPLES_PRIME_TABLES_H_
-#define GTEST_SAMPLES_PRIME_TABLES_H_
+#ifndef GOOGLETEST_SAMPLES_PRIME_TABLES_H_
+#define GOOGLETEST_SAMPLES_PRIME_TABLES_H_
 
 #include <algorithm>
 
@@ -44,7 +43,7 @@ class PrimeTable {
  public:
   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;
 
   // 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.
 class OnTheFlyPrimeTable : public PrimeTable {
  public:
-  virtual bool IsPrime(int n) const {
+  bool IsPrime(int n) const override {
     if (n <= 1) return false;
 
     for (int i = 2; i*i <= n; i++) {
@@ -66,12 +65,12 @@ class OnTheFlyPrimeTable : public PrimeTable {
     return true;
   }
 
-  virtual int GetNextPrime(int p) const {
-    for (int n = p + 1; n > 0; n++) {
+  int GetNextPrime(int p) const override {
+    if (p < 0) return -1;
+
+    for (int n = p + 1;; 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]) {
     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];
   }
 
-  virtual int GetNextPrime(int p) const {
+  int GetNextPrime(int p) const override {
     for (int n = p + 1; n < is_prime_size_; 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);
     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;
 
       // 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;
       }
     }
@@ -120,4 +123,4 @@ class PreCalculatedPrimeTable : public PrimeTable {
   void operator=(const PreCalculatedPrimeTable& rhs);
 };
 
-#endif  // GTEST_SAMPLES_PRIME_TABLES_H_
+#endif  // GOOGLETEST_SAMPLES_PRIME_TABLES_H_

+ 2 - 4
contrib/gtest/samples/sample1.cc

@@ -28,8 +28,6 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 // A sample program demonstrating using Google C++ testing framework.
-//
-// Author: [email protected] (Zhanyong Wan)
 
 #include "sample1.h"
 
@@ -43,7 +41,7 @@ int Factorial(int n) {
   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) {
   // Trivial case 1: small numbers
   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
   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;
 
     // Now, we have i <= n/i < n.

+ 4 - 6
contrib/gtest/samples/sample1.h

@@ -28,16 +28,14 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 // A sample program demonstrating using Google C++ testing framework.
-//
-// Author: [email protected] (Zhanyong Wan)
 
-#ifndef GTEST_SAMPLES_SAMPLE1_H_
-#define GTEST_SAMPLES_SAMPLE1_H_
+#ifndef GOOGLETEST_SAMPLES_SAMPLE1_H_
+#define GOOGLETEST_SAMPLES_SAMPLE1_H_
 
 // Returns n! (the factorial of n).  For negative n, n! is defined to be 1.
 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);
 
-#endif  // GTEST_SAMPLES_SAMPLE1_H_
+#endif  // GOOGLETEST_SAMPLES_SAMPLE1_H_

+ 4 - 9
contrib/gtest/samples/sample10_unittest.cc

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

+ 2 - 4
contrib/gtest/samples/sample1_unittest.cc

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

+ 1 - 3
contrib/gtest/samples/sample2.cc

@@ -28,8 +28,6 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 // A sample program demonstrating using Google C++ testing framework.
-//
-// Author: [email protected] (Zhanyong Wan)
 
 #include "sample2.h"
 
@@ -37,7 +35,7 @@
 
 // Clones a 0-terminated C string, allocating memory using new.
 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);
   char* const clone = new char[ len + 1 ];

+ 7 - 12
contrib/gtest/samples/sample2.h

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

+ 5 - 7
contrib/gtest/samples/sample2_unittest.cc

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

+ 12 - 12
contrib/gtest/samples/sample3-inl.h

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

+ 13 - 15
contrib/gtest/samples/sample3_unittest.cc

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

+ 10 - 2
contrib/gtest/samples/sample4.cc

@@ -28,8 +28,6 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 // A sample program demonstrating using Google C++ testing framework.
-//
-// Author: [email protected] (Zhanyong Wan)
 
 #include <stdio.h>
 
@@ -40,6 +38,16 @@ int Counter::Increment() {
   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.
 void Counter::Print() const {
   printf("%d", counter_);

+ 6 - 6
contrib/gtest/samples/sample4.h

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

+ 11 - 3
contrib/gtest/samples/sample4_unittest.cc

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

+ 11 - 14
contrib/gtest/samples/sample5_unittest.cc

@@ -26,8 +26,7 @@
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-//
-// Author: [email protected] (Zhanyong Wan)
+
 
 // This sample teaches how to reuse a test fixture in multiple test
 // cases by deriving sub-fixtures from it.
@@ -46,10 +45,10 @@
 
 #include <limits.h>
 #include <time.h>
-#include "sample3-inl.h"
 #include "gtest/gtest.h"
 #include "sample1.h"
-
+#include "sample3-inl.h"
+namespace {
 // 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
 // failure.
@@ -64,15 +63,13 @@ class QuickTest : public testing::Test {
  protected:
   // Remember that SetUp() is run immediately before a test starts.
   // This is a good place to record the start time.
-  virtual void SetUp() {
-    start_time_ = time(NULL);
-  }
+  void SetUp() override { start_time_ = time(nullptr); }
 
   // TearDown() is invoked immediately after a test finishes.  Here we
   // check if the test was too slow.
-  virtual void TearDown() {
+  void TearDown() override {
     // 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
     // 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.
 class QueueTest : public QuickTest {
  protected:
-  virtual void SetUp() {
+  void SetUp() override {
     // First, we need to set up the super fixture (QuickTest).
     QuickTest::SetUp();
 
@@ -177,21 +174,21 @@ TEST_F(QueueTest, DefaultConstructor) {
 // Tests Dequeue().
 TEST_F(QueueTest, Dequeue) {
   int* n = q0_.Dequeue();
-  EXPECT_TRUE(n == NULL);
+  EXPECT_TRUE(n == nullptr);
 
   n = q1_.Dequeue();
-  EXPECT_TRUE(n != NULL);
+  EXPECT_TRUE(n != nullptr);
   EXPECT_EQ(1, *n);
   EXPECT_EQ(0u, q1_.Size());
   delete n;
 
   n = q2_.Dequeue();
-  EXPECT_TRUE(n != NULL);
+  EXPECT_TRUE(n != nullptr);
   EXPECT_EQ(2, *n);
   EXPECT_EQ(1u, q2_.Size());
   delete n;
 }
-
+}  // namespace
 // If necessary, you can derive further test fixtures from a derived
 // fixture itself.  For example, you can derive another fixture from
 // QueueTest.  Google Test imposes no limit on how deep the hierarchy

+ 12 - 19
contrib/gtest/samples/sample6_unittest.cc

@@ -26,8 +26,7 @@
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-//
-// Author: [email protected] (Zhanyong Wan)
+
 
 // This sample shows how to test common properties of multiple
 // implementations of the same interface (aka interface tests).
@@ -36,7 +35,7 @@
 #include "prime_tables.h"
 
 #include "gtest/gtest.h"
-
+namespace {
 // First, we define some factory functions for creating instances of
 // the implementations.  You may be able to skip this step if all your
 // implementations can be constructed the same way.
@@ -62,7 +61,7 @@ class PrimeTableTest : public testing::Test {
   // implemented by T.
   PrimeTableTest() : table_(CreatePrimeTable<T>()) {}
 
-  virtual ~PrimeTableTest() { delete table_; }
+  ~PrimeTableTest() override { delete table_; }
 
   // Note that we test an implementation via the base interface
   // instead of the actual implementation class.  This is important
@@ -74,8 +73,6 @@ class PrimeTableTest : public testing::Test {
   PrimeTable* const table_;
 };
 
-#if GTEST_HAS_TYPED_TEST
-
 using testing::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
 //
-//   TYPED_TEST_CASE(TestCaseName, TypeList);
+//   TYPED_TEST_SUITE(TestCaseName, TypeList);
 //
 // to declare it and specify the type parameters.  As with TEST_F,
 // TestCaseName must match the test fixture name.
@@ -93,7 +90,7 @@ using testing::Types;
 // The list of types we want to test.
 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,
 // 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
-// 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.
 
-#endif  // GTEST_HAS_TYPED_TEST
-
-#if GTEST_HAS_TYPED_TEST_P
-
 using testing::Types;
 
 // 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
 // fixture, and also the name of the test case (as usual).  The _P
 // 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,
 // 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
 // enumerate the tests you defined:
-REGISTER_TYPED_TEST_CASE_P(
+REGISTER_TYPED_TEST_SUITE_P(
     PrimeTableTest2,  // The first argument is the test case name.
     // The rest of the arguments are the test names.
     ReturnsFalseForNonPrimes, ReturnsTrueForPrimes, CanGetNextPrime);
@@ -217,8 +210,8 @@ REGISTER_TYPED_TEST_CASE_P(
 // defined at the time we write the TYPED_TEST_P()s.
 typedef Types<OnTheFlyPrimeTable, PreCalculatedPrimeTable>
     PrimeTableImplementations;
-INSTANTIATE_TYPED_TEST_CASE_P(OnTheFlyAndPreCalculated,    // Instance name
-                              PrimeTableTest2,             // Test case name
-                              PrimeTableImplementations);  // Type list
+INSTANTIATE_TYPED_TEST_SUITE_P(OnTheFlyAndPreCalculated,    // Instance name
+                               PrimeTableTest2,             // Test case name
+                               PrimeTableImplementations);  // Type list
 
-#endif  // GTEST_HAS_TYPED_TEST_P
+}  // namespace

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.